Commit d4d74383 authored by Jason Madden's avatar Jason Madden

TransactionUndoStorage tests close the conn and db

This fixes leaking connections (relying on the GC to close them).

Tested with RelStorage.

Fixes #138
parent 93baff53
...@@ -3,7 +3,7 @@ sudo: false ...@@ -3,7 +3,7 @@ sudo: false
matrix: matrix:
include: include:
- os: linux - os: linux
python: pypy python: pypy-5.4.1
- os: linux - os: linux
python: pypy3 python: pypy3
env: BUILOUT_OPTIONS=sphinx:eggs= env: BUILOUT_OPTIONS=sphinx:eggs=
......
...@@ -407,154 +407,167 @@ class TransactionalUndoStorage: ...@@ -407,154 +407,167 @@ class TransactionalUndoStorage:
eq = self.assertEqual eq = self.assertEqual
db = DB(self._storage) db = DB(self._storage)
conn = db.open() conn = db.open()
root = conn.root() try:
root = conn.root()
o1 = C()
o2 = C()
root['obj'] = o1
o1.obj = o2
txn = transaction.get()
txn.note(u'o1 -> o2')
txn.commit()
now = packtime = time.time()
while packtime <= now:
packtime = time.time()
o3 = C()
o2.obj = o3
txn = transaction.get()
txn.note(u'o1 -> o2 -> o3')
txn.commit()
o1.obj = o3
txn = transaction.get()
txn.note(u'o1 -> o3')
txn.commit()
log = self._storage.undoLog()
eq(len(log), 4)
for entry in zip(log, (b'o1 -> o3', b'o1 -> o2 -> o3',
b'o1 -> o2', b'initial database creation')):
eq(entry[0]['description'], entry[1])
self._storage.pack(packtime, referencesf) o1 = C()
o2 = C()
log = self._storage.undoLog() root['obj'] = o1
for entry in zip(log, (b'o1 -> o3', b'o1 -> o2 -> o3')): o1.obj = o2
eq(entry[0]['description'], entry[1]) txn = transaction.get()
txn.note(u'o1 -> o2')
txn.commit()
now = packtime = time.time()
while packtime <= now:
packtime = time.time()
tid = log[0]['id'] o3 = C()
db.undo(tid) o2.obj = o3
txn = transaction.get() txn = transaction.get()
txn.note(u'undo') txn.note(u'o1 -> o2 -> o3')
txn.commit() txn.commit()
# undo does a txn-undo, but doesn't invalidate
conn.sync()
log = self._storage.undoLog() o1.obj = o3
for entry in zip(log, (b'undo', b'o1 -> o3', b'o1 -> o2 -> o3')): txn = transaction.get()
eq(entry[0]['description'], entry[1]) txn.note(u'o1 -> o3')
txn.commit()
eq(o1.obj, o2) log = self._storage.undoLog()
eq(o1.obj.obj, o3) eq(len(log), 4)
self._iterate() for entry in zip(log, (b'o1 -> o3', b'o1 -> o2 -> o3',
b'o1 -> o2', b'initial database creation')):
eq(entry[0]['description'], entry[1])
def checkPackAfterUndoDeletion(self): self._storage.pack(packtime, referencesf)
db = DB(self._storage)
cn = db.open()
root = cn.root()
pack_times = [] log = self._storage.undoLog()
def set_pack_time(): for entry in zip(log, (b'o1 -> o3', b'o1 -> o2 -> o3')):
pack_times.append(time.time()) eq(entry[0]['description'], entry[1])
snooze()
root["key0"] = MinPO(0) tid = log[0]['id']
root["key1"] = MinPO(1) db.undo(tid)
root["key2"] = MinPO(2) txn = transaction.get()
txn = transaction.get() txn.note(u'undo')
txn.note(u"create 3 keys") txn.commit()
txn.commit() # undo does a txn-undo, but doesn't invalidate
conn.sync()
set_pack_time() log = self._storage.undoLog()
for entry in zip(log, (b'undo', b'o1 -> o3', b'o1 -> o2 -> o3')):
eq(entry[0]['description'], entry[1])
del root["key1"] eq(o1.obj, o2)
txn = transaction.get() eq(o1.obj.obj, o3)
txn.note(u"delete 1 key") self._iterate()
txn.commit() finally:
conn.close()
db.close()
set_pack_time() def checkPackAfterUndoDeletion(self):
db = DB(self._storage)
cn = db.open()
try:
root = cn.root()
root._p_deactivate() pack_times = []
cn.sync() def set_pack_time():
self.assertTrue(listeq(root.keys(), ["key0", "key2"])) pack_times.append(time.time())
snooze()
L = db.undoInfo() root["key0"] = MinPO(0)
db.undo(L[0]["id"]) root["key1"] = MinPO(1)
txn = transaction.get() root["key2"] = MinPO(2)
txn.note(u"undo deletion") txn = transaction.get()
txn.commit() txn.note(u"create 3 keys")
txn.commit()
set_pack_time() set_pack_time()
root._p_deactivate() del root["key1"]
cn.sync() txn = transaction.get()
self.assertTrue(listeq(root.keys(), ["key0", "key1", "key2"])) txn.note(u"delete 1 key")
txn.commit()
for t in pack_times: set_pack_time()
self._storage.pack(t, referencesf)
root._p_deactivate() root._p_deactivate()
cn.sync() cn.sync()
self.assertTrue(listeq(root.keys(), ["key0", "key1", "key2"])) self.assertTrue(listeq(root.keys(), ["key0", "key2"]))
for i in range(3):
obj = root["key%d" % i]
self.assertEqual(obj.value, i)
root.items()
self._inter_pack_pause()
def checkPackAfterUndoManyTimes(self):
db = DB(self._storage)
cn = db.open()
rt = cn.root()
rt["test"] = MinPO(1)
transaction.commit()
rt["test2"] = MinPO(2)
transaction.commit()
rt["test"] = MinPO(3)
txn = transaction.get()
txn.note(u"root of undo")
txn.commit()
packtimes = []
for i in range(10):
L = db.undoInfo() L = db.undoInfo()
db.undo(L[0]["id"]) db.undo(L[0]["id"])
txn = transaction.get() txn = transaction.get()
txn.note(u"undo %d" % i) txn.note(u"undo deletion")
txn.commit() txn.commit()
rt._p_deactivate()
set_pack_time()
root._p_deactivate()
cn.sync() cn.sync()
self.assertTrue(listeq(root.keys(), ["key0", "key1", "key2"]))
self.assertEqual(rt["test"].value, i % 2 and 3 or 1) for t in pack_times:
self.assertEqual(rt["test2"].value, 2) self._storage.pack(t, referencesf)
packtimes.append(time.time()) root._p_deactivate()
snooze() cn.sync()
self.assertTrue(listeq(root.keys(), ["key0", "key1", "key2"]))
for i in range(3):
obj = root["key%d" % i]
self.assertEqual(obj.value, i)
root.items()
self._inter_pack_pause()
finally:
cn.close()
db.close()
for t in packtimes:
self._storage.pack(t, referencesf)
cn.sync()
# TODO: Is _cache supposed to have a clear() method, or not? def checkPackAfterUndoManyTimes(self):
# cn._cache.clear() db = DB(self._storage)
cn = db.open()
try:
rt = cn.root()
# The last undo set the value to 3 and pack should rt["test"] = MinPO(1)
# never change that. transaction.commit()
self.assertEqual(rt["test"].value, 3) rt["test2"] = MinPO(2)
self.assertEqual(rt["test2"].value, 2) transaction.commit()
self._inter_pack_pause() rt["test"] = MinPO(3)
txn = transaction.get()
txn.note(u"root of undo")
txn.commit()
packtimes = []
for i in range(10):
L = db.undoInfo()
db.undo(L[0]["id"])
txn = transaction.get()
txn.note(u"undo %d" % i)
txn.commit()
rt._p_deactivate()
cn.sync()
self.assertEqual(rt["test"].value, i % 2 and 3 or 1)
self.assertEqual(rt["test2"].value, 2)
packtimes.append(time.time())
snooze()
for t in packtimes:
self._storage.pack(t, referencesf)
cn.sync()
# TODO: Is _cache supposed to have a clear() method, or not?
# cn._cache.clear()
# The last undo set the value to 3 and pack should
# never change that.
self.assertEqual(rt["test"].value, 3)
self.assertEqual(rt["test2"].value, 2)
self._inter_pack_pause()
finally:
cn.close()
db.close()
def _inter_pack_pause(self): def _inter_pack_pause(self):
# DirectoryStorage needs a pause between packs, # DirectoryStorage needs a pause between packs,
...@@ -654,17 +667,21 @@ class TransactionalUndoStorage: ...@@ -654,17 +667,21 @@ class TransactionalUndoStorage:
t.setUser(u'u3',path=u'p3') t.setUser(u'u3',path=u'p3')
db = DB(self._storage) db = DB(self._storage)
conn = db.open() conn = db.open()
root = conn.root() try:
o1 = C() root = conn.root()
root['obj'] = o1 o1 = C()
txn = transaction.get() root['obj'] = o1
txn.commit() txn = transaction.get()
l = self._storage.undoLog() txn.commit()
self.assertEqual(len(l),2) l = self._storage.undoLog()
d = l[0] self.assertEqual(len(l),2)
self.assertEqual(d['description'], b't1') d = l[0]
self.assertEqual(d['k2'], 'this is transaction metadata') self.assertEqual(d['description'], b't1')
self.assertEqual(d['user_name'], b'p3 u3') self.assertEqual(d['k2'], 'this is transaction metadata')
self.assertEqual(d['user_name'], b'p3 u3')
finally:
conn.close()
db.close()
# A common test body for index tests on undoInfo and undoLog. Before # A common test body for index tests on undoInfo and undoLog. Before
# ZODB 3.4, they always returned a wrong number of results (one too # ZODB 3.4, they always returned a wrong number of results (one too
...@@ -729,26 +746,28 @@ class TransactionalUndoStorage: ...@@ -729,26 +746,28 @@ class TransactionalUndoStorage:
def checkUndoMultipleConflictResolution(self, reverse=False): def checkUndoMultipleConflictResolution(self, reverse=False):
from .ConflictResolution import PCounter from .ConflictResolution import PCounter
db = DB(self._storage) db = DB(self._storage)
cn = db.open() cn = db.open()
cn.root.x = PCounter() try:
transaction.commit() cn.root.x = PCounter()
transaction.commit()
for i in range(4): for i in range(4):
with db.transaction() as conn: with db.transaction() as conn:
conn.transaction_manager.get().note( conn.transaction_manager.get().note(
(str if PY3 else unicode)(i)) (str if PY3 else unicode)(i))
conn.root.x.inc() conn.root.x.inc()
ids = [l['id'] for l in db.undoLog(1, 3)] ids = [l['id'] for l in db.undoLog(1, 3)]
if reverse: if reverse:
ids.reverse() ids.reverse()
db.undoMultiple(ids) db.undoMultiple(ids)
transaction.commit() transaction.commit()
self.assertEqual(cn.root.x._value, 2) self.assertEqual(cn.root.x._value, 2)
cn.close() finally:
cn.close()
db.close()
def checkUndoMultipleConflictResolutionReversed(self): def checkUndoMultipleConflictResolutionReversed(self):
self.checkUndoMultipleConflictResolution(True) self.checkUndoMultipleConflictResolution(True)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment