Commit 58cfe930 authored by Jeremy Hylton's avatar Jeremy Hylton

Fix tests to work correctly with conflict resolution.

Add new tests for conflict resolution.

The old blanket try-except for conflict resolution was hiding some
bugs caused by the pickles used in the test suite.  The conflict
resolution code imposes some restrictions on the format of pickles.
Basically, the conflict resolution requires that the storage API only
accept a pickles that constructed according to the ZODB rules.

XXX This new restriction sounds unfortunate, but it would require a
substantial change to conflict resolution to remove it.

The key changes to the test suite are to store only persistent objects
and to format the pickles using the standard ZODB format.  All tests
now use ZODB.tests.MinPO.MinPO instances for data.  The pickling is
down with zodb_pickle() and zodb_unpickle() defined in
StorageTestBase.

Add conflict resolution tests to testFileStorage.  (They pass.)
parent 95f9483c
...@@ -7,8 +7,10 @@ ...@@ -7,8 +7,10 @@
from ZODB.Transaction import Transaction from ZODB.Transaction import Transaction
from ZODB import POSException from ZODB import POSException
from ZODB.tests.MinPO import MinPO
from ZODB.tests.StorageTestBase import zodb_unpickle
ZERO = '\0'*8 ZERO = '\0'*8
import pickle
...@@ -70,16 +72,16 @@ class BasicStorage: ...@@ -70,16 +72,16 @@ class BasicStorage:
def checkNonVersionStoreAndLoad(self): def checkNonVersionStoreAndLoad(self):
oid = self._storage.new_oid() oid = self._storage.new_oid()
self._dostore(oid=oid, data=7) self._dostore(oid=oid, data=MinPO(7))
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
value = pickle.loads(data) value = zodb_unpickle(data)
assert value == 7 assert value == MinPO(7)
# Now do a bunch of updates to an object # Now do a bunch of updates to an object
for i in range(13, 22): for i in range(13, 22):
revid = self._dostore(oid, revid=revid, data=i) revid = self._dostore(oid, revid=revid, data=MinPO(i))
# Now get the latest revision of the object # Now get the latest revision of the object
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 21 assert zodb_unpickle(data) == MinPO(21)
def checkNonVersionModifiedInVersion(self): def checkNonVersionModifiedInVersion(self):
oid = self._storage.new_oid() oid = self._storage.new_oid()
...@@ -91,18 +93,18 @@ class BasicStorage: ...@@ -91,18 +93,18 @@ class BasicStorage:
revid = ZERO revid = ZERO
revisions = {} revisions = {}
for i in range(31, 38): for i in range(31, 38):
revid = self._dostore(oid, revid=revid, data=i) revid = self._dostore(oid, revid=revid, data=MinPO(i))
revisions[revid] = i revisions[revid] = MinPO(i)
# Now make sure all the revisions have the correct value # Now make sure all the revisions have the correct value
for revid, value in revisions.items(): for revid, value in revisions.items():
data = self._storage.loadSerial(oid, revid) data = self._storage.loadSerial(oid, revid)
assert pickle.loads(data) == value assert zodb_unpickle(data) == value
def checkConflicts(self): def checkConflicts(self):
oid = self._storage.new_oid() oid = self._storage.new_oid()
revid1 = self._dostore(oid, data=11) revid1 = self._dostore(oid, data=MinPO(11))
revid2 = self._dostore(oid, revid=revid1, data=12) revid2 = self._dostore(oid, revid=revid1, data=MinPO(12))
self.assertRaises(POSException.ConflictError, self.assertRaises(POSException.ConflictError,
self._dostore, self._dostore,
oid, revid=revid1, data=13) oid, revid=revid1, data=MinPO(13))
"""Tests for application-level conflict resolution."""
from ZODB.Transaction import Transaction
from ZODB.POSException import ConflictError
from Persistence import Persistent
from ZODB.tests.StorageTestBase import zodb_unpickle, zodb_pickle
import sys
import types
from cPickle import Pickler, Unpickler
from cStringIO import StringIO
class PCounter(Persistent):
_value = 0
def __repr__(self):
return "<PCounter %d>" % self._value
def inc(self):
self._value = self._value + 1
def _p_resolveConflict(self, oldState, savedState, newState):
savedDiff = savedState['_value'] - oldState['_value']
newDiff = newState['_value'] - oldState['_value']
oldState['_value'] = oldState['_value'] + savedDiff + newDiff
return oldState
# XXX What if _p_resolveConflict _thinks_ it resolved the
# conflict, but did something wrong?
class PCounter2(PCounter):
def _p_resolveConflict(self, oldState, savedState, newState):
raise ConflictError
class PCounter3(PCounter):
def _p_resolveConflict(self, oldState, savedState, newState):
raise AttributeError, "no attribute"
class ConflictResolvingStorage:
def checkResolve(self):
obj = PCounter()
obj.inc()
oid = self._storage.new_oid()
revid1 = self._dostoreNP(oid, data=zodb_pickle(obj))
obj.inc()
obj.inc()
# The effect of committing two transactions with the same
# pickle is to commit two different transactions relative to
# revid1 that add two to _value.
revid2 = self._dostoreNP(oid, revid=revid1, data=zodb_pickle(obj))
revid3 = self._dostoreNP(oid, revid=revid1, data=zodb_pickle(obj))
data, serialno = self._storage.load(oid, '')
inst = zodb_unpickle(data)
self.assert_(inst._value == 5)
def checkUnresolvable(self):
obj = PCounter2()
obj.inc()
oid = self._storage.new_oid()
revid1 = self._dostoreNP(oid, data=zodb_pickle(obj))
obj.inc()
obj.inc()
# The effect of committing two transactions with the same
# pickle is to commit two different transactions relative to
# revid1 that add two to _value.
revid2 = self._dostoreNP(oid, revid=revid1, data=zodb_pickle(obj))
self.assertRaises(ConflictError,
self._dostoreNP,
oid, revid=revid1, data=zodb_pickle(obj))
def checkBuggyResolve(self):
obj = PCounter3()
obj.inc()
oid = self._storage.new_oid()
revid1 = self._dostoreNP(oid, data=zodb_pickle(obj))
obj.inc()
obj.inc()
# The effect of committing two transactions with the same
# pickle is to commit two different transactions relative to
# revid1 that add two to _value.
revid2 = self._dostoreNP(oid, revid=revid1, data=zodb_pickle(obj))
self.assertRaises(AttributeError,
self._dostoreNP,
oid, revid=revid1, data=zodb_pickle(obj))
# XXX test conflict error raised during undo
...@@ -4,11 +4,69 @@ ...@@ -4,11 +4,69 @@
# store transaction for a single object revision. # store transaction for a single object revision.
import pickle import pickle
import string
import sys
import types
import unittest import unittest
from cPickle import Pickler, Unpickler
from cStringIO import StringIO
from ZODB.Transaction import Transaction from ZODB.Transaction import Transaction
from ZODB.tests.MinPO import MinPO
ZERO = '\0'*8 ZERO = '\0'*8
def zodb_pickle(obj):
f = StringIO()
p = Pickler(f, 1)
klass = obj.__class__
assert not hasattr(obj, '__getinitargs__'), "not ready for constructors"
args = None
mod = getattr(klass, '__module__', None)
if mod is not None:
klass = mod, klass.__name__
state = obj.__getstate__()
p.dump((klass, args))
p.dump(state)
return f.getvalue(1)
def zodb_unpickle(data):
f = StringIO(data)
u = Unpickler(f)
klass_info = u.load()
if isinstance(klass_info, types.TupleType):
if isinstance(klass_info[0], types.TupleType):
modname, klassname = klass_info[0]
args = klass_info[1]
else:
modname, klassname = klass_info
args = None
if modname == "__main__":
ns = globals()
else:
mod = import_helper(modname)
ns = mod.__dict__
try:
klass = ns[klassname]
except KeyError:
print >> sys.stderr, "can't find %s in %s" % (klassname,
repr(ns))
inst = klass()
else:
raise ValueError, "expected class info: %s" % repr(klass_info)
state = u.load()
inst.__setstate__(state)
return inst
def import_helper(name):
mod = __import__(name)
for part in string.split(name, ".")[1:]:
mod = getattr(mod, part)
return mod
class StorageTestBase(unittest.TestCase): class StorageTestBase(unittest.TestCase):
...@@ -39,9 +97,12 @@ class StorageTestBase(unittest.TestCase): ...@@ -39,9 +97,12 @@ class StorageTestBase(unittest.TestCase):
if revid is None: if revid is None:
revid = ZERO revid = ZERO
if data is None: if data is None:
data = 7 data = MinPO(7)
if type(data) == types.IntType:
data = MinPO(data)
if not already_pickled: if not already_pickled:
data = pickle.dumps(data) ## data = pickle.dumps(data)
data = zodb_pickle(data)
if version is None: if version is None:
version = '' version = ''
# Begin the transaction # Begin the transaction
......
# Check transactionalUndo(). Any storage that supports transactionalUndo() # Check transactionalUndo(). Any storage that supports transactionalUndo()
# must pass these tests. # must pass these tests.
import pickle
import types import types
from ZODB import POSException from ZODB import POSException
ZERO = '\0'*8 from ZODB.tests.MinPO import MinPO
from ZODB.tests.StorageTestBase import zodb_pickle, zodb_unpickle
ZERO = '\0'*8
class TransactionalUndoStorage: class TransactionalUndoStorage:
...@@ -47,9 +48,9 @@ class TransactionalUndoStorage: ...@@ -47,9 +48,9 @@ class TransactionalUndoStorage:
def checkSimpleTransactionalUndo(self): def checkSimpleTransactionalUndo(self):
oid = self._storage.new_oid() oid = self._storage.new_oid()
revid = self._dostore(oid, data=23) revid = self._dostore(oid, data=MinPO(23))
revid = self._dostore(oid, revid=revid, data=24) revid = self._dostore(oid, revid=revid, data=MinPO(24))
revid = self._dostore(oid, revid=revid, data=25) revid = self._dostore(oid, revid=revid, data=MinPO(25))
info = self._storage.undoInfo() info = self._storage.undoInfo()
tid = info[0]['id'] tid = info[0]['id']
...@@ -62,7 +63,7 @@ class TransactionalUndoStorage: ...@@ -62,7 +63,7 @@ class TransactionalUndoStorage:
assert len(oids) == 1 assert len(oids) == 1
assert oids[0] == oid assert oids[0] == oid
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 24 assert zodb_unpickle(data) == MinPO(24)
# Do another one # Do another one
info = self._storage.undoInfo() info = self._storage.undoInfo()
tid = info[2]['id'] tid = info[2]['id']
...@@ -74,7 +75,7 @@ class TransactionalUndoStorage: ...@@ -74,7 +75,7 @@ class TransactionalUndoStorage:
assert len(oids) == 1 assert len(oids) == 1
assert oids[0] == oid assert oids[0] == oid
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 23 assert zodb_unpickle(data) == MinPO(23)
# Try to undo the first record # Try to undo the first record
info = self._storage.undoInfo() info = self._storage.undoInfo()
tid = info[4]['id'] tid = info[4]['id']
...@@ -99,12 +100,12 @@ class TransactionalUndoStorage: ...@@ -99,12 +100,12 @@ class TransactionalUndoStorage:
assert len(oids) == 1 assert len(oids) == 1
assert oids[0] == oid assert oids[0] == oid
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 23 assert zodb_unpickle(data) == MinPO(23)
def checkUndoCreationBranch1(self): def checkUndoCreationBranch1(self):
oid = self._storage.new_oid() oid = self._storage.new_oid()
revid = self._dostore(oid, data=11) revid = self._dostore(oid, data=MinPO(11))
revid = self._dostore(oid, revid=revid, data=12) revid = self._dostore(oid, revid=revid, data=MinPO(12))
# Undo the last transaction # Undo the last transaction
info = self._storage.undoInfo() info = self._storage.undoInfo()
tid = info[0]['id'] tid = info[0]['id']
...@@ -115,7 +116,7 @@ class TransactionalUndoStorage: ...@@ -115,7 +116,7 @@ class TransactionalUndoStorage:
assert len(oids) == 1 assert len(oids) == 1
assert oids[0] == oid assert oids[0] == oid
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 11 assert zodb_unpickle(data) == MinPO(11)
# Now from here, we can either redo the last undo, or undo the object # Now from here, we can either redo the last undo, or undo the object
# creation. Let's undo the object creation. # creation. Let's undo the object creation.
info = self._storage.undoInfo() info = self._storage.undoInfo()
...@@ -130,8 +131,8 @@ class TransactionalUndoStorage: ...@@ -130,8 +131,8 @@ class TransactionalUndoStorage:
def checkUndoCreationBranch2(self): def checkUndoCreationBranch2(self):
oid = self._storage.new_oid() oid = self._storage.new_oid()
revid = self._dostore(oid, data=11) revid = self._dostore(oid, data=MinPO(11))
revid = self._dostore(oid, revid=revid, data=12) revid = self._dostore(oid, revid=revid, data=MinPO(12))
# Undo the last transaction # Undo the last transaction
info = self._storage.undoInfo() info = self._storage.undoInfo()
tid = info[0]['id'] tid = info[0]['id']
...@@ -142,7 +143,7 @@ class TransactionalUndoStorage: ...@@ -142,7 +143,7 @@ class TransactionalUndoStorage:
assert len(oids) == 1 assert len(oids) == 1
assert oids[0] == oid assert oids[0] == oid
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 11 assert zodb_unpickle(data) == MinPO(11)
# Now from here, we can either redo the last undo, or undo the object # Now from here, we can either redo the last undo, or undo the object
# creation. Let's redo the last undo # creation. Let's redo the last undo
info = self._storage.undoInfo() info = self._storage.undoInfo()
...@@ -154,11 +155,12 @@ class TransactionalUndoStorage: ...@@ -154,11 +155,12 @@ class TransactionalUndoStorage:
assert len(oids) == 1 assert len(oids) == 1
assert oids[0] == oid assert oids[0] == oid
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 12 assert zodb_unpickle(data) == MinPO(12)
def checkTwoObjectUndo(self): def checkTwoObjectUndo(self):
# Convenience # Convenience
p31, p32, p51, p52 = map(pickle.dumps, (31, 32, 51, 52)) p31, p32, p51, p52 = map(zodb_pickle,
map(MinPO, (31, 32, 51, 52)))
oid1 = self._storage.new_oid() oid1 = self._storage.new_oid()
oid2 = self._storage.new_oid() oid2 = self._storage.new_oid()
revid1 = revid2 = ZERO revid1 = revid2 = ZERO
...@@ -186,9 +188,9 @@ class TransactionalUndoStorage: ...@@ -186,9 +188,9 @@ class TransactionalUndoStorage:
assert revid1 == revid2 assert revid1 == revid2
# Make sure the objects have the current value # Make sure the objects have the current value
data, revid1 = self._storage.load(oid1, '') data, revid1 = self._storage.load(oid1, '')
assert pickle.loads(data) == 32 assert zodb_unpickle(data) == MinPO(32)
data, revid2 = self._storage.load(oid2, '') data, revid2 = self._storage.load(oid2, '')
assert pickle.loads(data) == 52 assert zodb_unpickle(data) == MinPO(52)
# Now attempt to undo the transaction containing two objects # Now attempt to undo the transaction containing two objects
info = self._storage.undoInfo() info = self._storage.undoInfo()
tid = info[0]['id'] tid = info[0]['id']
...@@ -199,14 +201,15 @@ class TransactionalUndoStorage: ...@@ -199,14 +201,15 @@ class TransactionalUndoStorage:
assert len(oids) == 2 assert len(oids) == 2
assert oid1 in oids and oid2 in oids assert oid1 in oids and oid2 in oids
data, revid1 = self._storage.load(oid1, '') data, revid1 = self._storage.load(oid1, '')
assert pickle.loads(data) == 31 assert zodb_unpickle(data) == MinPO(31)
data, revid2 = self._storage.load(oid2, '') data, revid2 = self._storage.load(oid2, '')
assert pickle.loads(data) == 51 assert zodb_unpickle(data) == MinPO(51)
def checkTwoObjectUndoAtOnce(self): def checkTwoObjectUndoAtOnce(self):
# Convenience # Convenience
p30, p31, p32, p50, p51, p52 = map(pickle.dumps, p30, p31, p32, p50, p51, p52 = map(zodb_pickle,
(30, 31, 32, 50, 51, 52)) map(MinPO,
(30, 31, 32, 50, 51, 52)))
oid1 = self._storage.new_oid() oid1 = self._storage.new_oid()
oid2 = self._storage.new_oid() oid2 = self._storage.new_oid()
revid1 = revid2 = ZERO revid1 = revid2 = ZERO
...@@ -230,9 +233,9 @@ class TransactionalUndoStorage: ...@@ -230,9 +233,9 @@ class TransactionalUndoStorage:
assert revid1 == revid2 assert revid1 == revid2
# Make sure the objects have the current value # Make sure the objects have the current value
data, revid1 = self._storage.load(oid1, '') data, revid1 = self._storage.load(oid1, '')
assert pickle.loads(data) == 32 assert zodb_unpickle(data) == MinPO(32)
data, revid2 = self._storage.load(oid2, '') data, revid2 = self._storage.load(oid2, '')
assert pickle.loads(data) == 52 assert zodb_unpickle(data) == MinPO(52)
# Now attempt to undo the transaction containing two objects # Now attempt to undo the transaction containing two objects
info = self._storage.undoInfo() info = self._storage.undoInfo()
tid = info[0]['id'] tid = info[0]['id']
...@@ -249,9 +252,9 @@ class TransactionalUndoStorage: ...@@ -249,9 +252,9 @@ class TransactionalUndoStorage:
assert len(oids1) == 2 assert len(oids1) == 2
assert oid1 in oids and oid2 in oids assert oid1 in oids and oid2 in oids
data, revid1 = self._storage.load(oid1, '') data, revid1 = self._storage.load(oid1, '')
assert pickle.loads(data) == 30 assert zodb_unpickle(data) == MinPO(30)
data, revid2 = self._storage.load(oid2, '') data, revid2 = self._storage.load(oid2, '')
assert pickle.loads(data) == 50 assert zodb_unpickle(data) == MinPO(50)
# Now try to undo the one we just did to undo, whew # Now try to undo the one we just did to undo, whew
info = self._storage.undoInfo() info = self._storage.undoInfo()
tid = info[0]['id'] tid = info[0]['id']
...@@ -262,18 +265,19 @@ class TransactionalUndoStorage: ...@@ -262,18 +265,19 @@ class TransactionalUndoStorage:
assert len(oids) == 2 assert len(oids) == 2
assert oid1 in oids and oid2 in oids assert oid1 in oids and oid2 in oids
data, revid1 = self._storage.load(oid1, '') data, revid1 = self._storage.load(oid1, '')
assert pickle.loads(data) == 32 assert zodb_unpickle(data) == MinPO(32)
data, revid2 = self._storage.load(oid2, '') data, revid2 = self._storage.load(oid2, '')
assert pickle.loads(data) == 52 assert zodb_unpickle(data) == MinPO(52)
def checkTwoObjectUndoAgain(self): def checkTwoObjectUndoAgain(self):
p32, p33, p52, p53 = map(pickle.dumps, (32, 33, 52, 53)) p32, p33, p52, p53 = map(zodb_pickle,
map(MinPO, (32, 33, 52, 53)))
# Like the above, but the first revision of the objects are stored in # Like the above, but the first revision of the objects are stored in
# different transactions. # different transactions.
oid1 = self._storage.new_oid() oid1 = self._storage.new_oid()
oid2 = self._storage.new_oid() oid2 = self._storage.new_oid()
revid1 = self._dostore(oid1, data=31) revid1 = self._dostore(oid1, data=MinPO(31))
revid2 = self._dostore(oid2, data=51) revid2 = self._dostore(oid2, data=MinPO(51))
# Update those same two objects # Update those same two objects
self._storage.tpc_begin(self._transaction) self._storage.tpc_begin(self._transaction)
self._transaction_begin() self._transaction_begin()
...@@ -295,9 +299,9 @@ class TransactionalUndoStorage: ...@@ -295,9 +299,9 @@ class TransactionalUndoStorage:
assert len(oids) == 2 assert len(oids) == 2
assert oid1 in oids and oid2 in oids assert oid1 in oids and oid2 in oids
data, revid1 = self._storage.load(oid1, '') data, revid1 = self._storage.load(oid1, '')
assert pickle.loads(data) == 31 assert zodb_unpickle(data) == MinPO(31)
data, revid2 = self._storage.load(oid2, '') data, revid2 = self._storage.load(oid2, '')
assert pickle.loads(data) == 51 assert zodb_unpickle(data) == MinPO(51)
# Like the above, but this time, the second transaction contains only # Like the above, but this time, the second transaction contains only
# one object. # one object.
self._storage.tpc_begin(self._transaction) self._storage.tpc_begin(self._transaction)
...@@ -311,8 +315,8 @@ class TransactionalUndoStorage: ...@@ -311,8 +315,8 @@ class TransactionalUndoStorage:
revid2 = self._transaction_newserial(oid2) revid2 = self._transaction_newserial(oid2)
assert revid1 == revid2 assert revid1 == revid2
# Update in different transactions # Update in different transactions
revid1 = self._dostore(oid1, revid=revid1, data=34) revid1 = self._dostore(oid1, revid=revid1, data=MinPO(34))
revid2 = self._dostore(oid2, revid=revid2, data=54) revid2 = self._dostore(oid2, revid=revid2, data=MinPO(54))
# Now attempt to undo the transaction containing two objects # Now attempt to undo the transaction containing two objects
info = self._storage.undoInfo() info = self._storage.undoInfo()
tid = info[1]['id'] tid = info[1]['id']
...@@ -323,17 +327,17 @@ class TransactionalUndoStorage: ...@@ -323,17 +327,17 @@ class TransactionalUndoStorage:
assert len(oids) == 1 assert len(oids) == 1
assert oid1 in oids and not oid2 in oids assert oid1 in oids and not oid2 in oids
data, revid1 = self._storage.load(oid1, '') data, revid1 = self._storage.load(oid1, '')
assert pickle.loads(data) == 33 assert zodb_unpickle(data) == MinPO(33)
data, revid2 = self._storage.load(oid2, '') data, revid2 = self._storage.load(oid2, '')
assert pickle.loads(data) == 54 assert zodb_unpickle(data) == MinPO(54)
def checkNotUndoable(self): def checkNotUndoable(self):
# Set things up so we've got a transaction that can't be undone # Set things up so we've got a transaction that can't be undone
oid = self._storage.new_oid() oid = self._storage.new_oid()
revid_a = self._dostore(oid, data=51) revid_a = self._dostore(oid, data=MinPO(51))
revid_b = self._dostore(oid, revid=revid_a, data=52) revid_b = self._dostore(oid, revid=revid_a, data=MinPO(52))
revid_c = self._dostore(oid, revid=revid_b, data=53) revid_c = self._dostore(oid, revid=revid_b, data=MinPO(53))
# Start the undo # Start the undo
info = self._storage.undoInfo() info = self._storage.undoInfo()
tid = info[1]['id'] tid = info[1]['id']
...@@ -349,7 +353,8 @@ class TransactionalUndoStorage: ...@@ -349,7 +353,8 @@ class TransactionalUndoStorage:
revid1 = revid_c revid1 = revid_c
oid2 = self._storage.new_oid() oid2 = self._storage.new_oid()
revid2 = ZERO revid2 = ZERO
p81, p82, p91, p92 = map(pickle.dumps, (81, 82, 91, 92)) p81, p82, p91, p92 = map(zodb_pickle,
map(MinPO, (81, 82, 91, 92)))
self._storage.tpc_begin(self._transaction) self._storage.tpc_begin(self._transaction)
self._transaction_begin() self._transaction_begin()
...@@ -362,12 +367,12 @@ class TransactionalUndoStorage: ...@@ -362,12 +367,12 @@ class TransactionalUndoStorage:
assert revid1 == revid2 assert revid1 == revid2
# Make sure the objects have the expected values # Make sure the objects have the expected values
data, revid_11 = self._storage.load(oid1, '') data, revid_11 = self._storage.load(oid1, '')
assert pickle.loads(data) == 81 assert zodb_unpickle(data) == MinPO(81)
data, revid_22 = self._storage.load(oid2, '') data, revid_22 = self._storage.load(oid2, '')
assert pickle.loads(data) == 91 assert zodb_unpickle(data) == MinPO(91)
assert revid_11 == revid1 and revid_22 == revid2 assert revid_11 == revid1 and revid_22 == revid2
# Now modify oid2 # Now modify oid2
revid2 = self._dostore(oid2, revid=revid2, data=p92) revid2 = self._dostore(oid2, revid=revid2, data=MinPO(92))
assert revid1 <> revid2 and revid2 <> revid_22 assert revid1 <> revid2 and revid2 <> revid_22
info = self._storage.undoInfo() info = self._storage.undoInfo()
tid = info[1]['id'] tid = info[1]['id']
......
# Check interactions between transactionalUndo() and versions. Any storage # Check interactions between transactionalUndo() and versions. Any storage
# that supports both transactionalUndo() and versions must pass these tests. # that supports both transactionalUndo() and versions must pass these tests.
import pickle
from ZODB import POSException from ZODB import POSException
from ZODB.tests.MinPO import MinPO
from ZODB.tests.StorageTestBase import zodb_unpickle
class TransactionalUndoVersionStorage: class TransactionalUndoVersionStorage:
def checkUndoInVersion(self): def checkUndoInVersion(self):
oid = self._storage.new_oid() oid = self._storage.new_oid()
version = 'one' version = 'one'
revid_a = self._dostore(oid, data=91) revid_a = self._dostore(oid, data=MinPO(91))
revid_b = self._dostore(oid, revid=revid_a, data=92, version=version) revid_b = self._dostore(oid, revid=revid_a, data=MinPO(92), version=version)
revid_c = self._dostore(oid, revid=revid_b, data=93, version=version) revid_c = self._dostore(oid, revid=revid_b, data=MinPO(93), version=version)
info=self._storage.undoInfo() info=self._storage.undoInfo()
tid=info[0]['id'] tid=info[0]['id']
self._storage.tpc_begin(self._transaction) self._storage.tpc_begin(self._transaction)
...@@ -23,10 +23,10 @@ class TransactionalUndoVersionStorage: ...@@ -23,10 +23,10 @@ class TransactionalUndoVersionStorage:
assert oids[0] == oid assert oids[0] == oid
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert revid == revid_a assert revid == revid_a
assert pickle.loads(data) == 91 assert zodb_unpickle(data) == MinPO(91)
data, revid = self._storage.load(oid, version) data, revid = self._storage.load(oid, version)
assert revid > revid_b and revid > revid_c assert revid > revid_b and revid > revid_c
assert pickle.loads(data) == 92 assert zodb_unpickle(data) == MinPO(92)
# Now commit the version... # Now commit the version...
self._storage.tpc_begin(self._transaction) self._storage.tpc_begin(self._transaction)
oids = self._storage.commitVersion(version, '', self._transaction) oids = self._storage.commitVersion(version, '', self._transaction)
...@@ -40,9 +40,9 @@ class TransactionalUndoVersionStorage: ...@@ -40,9 +40,9 @@ class TransactionalUndoVersionStorage:
#JF# self._storage.load, #JF# self._storage.load,
#JF# oid, version) #JF# oid, version)
data, revid = self._storage.load(oid, version) data, revid = self._storage.load(oid, version)
assert pickle.loads(data) == 92 assert zodb_unpickle(data) == MinPO(92)
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 92 assert zodb_unpickle(data) == MinPO(92)
# ...and undo the commit # ...and undo the commit
info=self._storage.undoInfo() info=self._storage.undoInfo()
tid=info[0]['id'] tid=info[0]['id']
...@@ -53,9 +53,9 @@ class TransactionalUndoVersionStorage: ...@@ -53,9 +53,9 @@ class TransactionalUndoVersionStorage:
assert len(oids) == 1 assert len(oids) == 1
assert oids[0] == oid assert oids[0] == oid
data, revid = self._storage.load(oid, version) data, revid = self._storage.load(oid, version)
assert pickle.loads(data) == 92 assert zodb_unpickle(data) == MinPO(92)
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 91 assert zodb_unpickle(data) == MinPO(91)
# Now abort the version # Now abort the version
self._storage.tpc_begin(self._transaction) self._storage.tpc_begin(self._transaction)
oids = self._storage.abortVersion(version, self._transaction) oids = self._storage.abortVersion(version, self._transaction)
...@@ -70,9 +70,9 @@ class TransactionalUndoVersionStorage: ...@@ -70,9 +70,9 @@ class TransactionalUndoVersionStorage:
#JF# self._storage.load, #JF# self._storage.load,
#JF# oid, version) #JF# oid, version)
data, revid = self._storage.load(oid, version) data, revid = self._storage.load(oid, version)
assert pickle.loads(data) == 91 assert zodb_unpickle(data) == MinPO(91)
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 91 assert zodb_unpickle(data) == MinPO(91)
# Now undo the abort # Now undo the abort
info=self._storage.undoInfo() info=self._storage.undoInfo()
tid=info[0]['id'] tid=info[0]['id']
...@@ -84,6 +84,6 @@ class TransactionalUndoVersionStorage: ...@@ -84,6 +84,6 @@ class TransactionalUndoVersionStorage:
assert oids[0] == oid assert oids[0] == oid
# And the object should be back in versions 'one' and '' # And the object should be back in versions 'one' and ''
data, revid = self._storage.load(oid, version) data, revid = self._storage.load(oid, version)
assert pickle.loads(data) == 92 assert zodb_unpickle(data) == MinPO(92)
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 91 assert zodb_unpickle(data) == MinPO(91)
# Run the version related tests for a storage. Any storage that supports # Run the version related tests for a storage. Any storage that supports
# versions should be able to pass all these tests. # versions should be able to pass all these tests.
try:
import cPickle
pickle = cPickle
#import cPickle as pickle
except ImportError:
import pickle
from ZODB import POSException from ZODB import POSException
from ZODB.tests.MinPO import MinPO
from ZODB.tests.StorageTestBase import zodb_unpickle
class VersionStorage: class VersionStorage:
def checkVersionedStoreAndLoad(self): def checkVersionedStoreAndLoad(self):
# Store a couple of non-version revisions of the object # Store a couple of non-version revisions of the object
oid = self._storage.new_oid() oid = self._storage.new_oid()
revid = self._dostore(oid, data=11) revid = self._dostore(oid, data=MinPO(11))
revid = self._dostore(oid, revid=revid, data=12) revid = self._dostore(oid, revid=revid, data=MinPO(12))
# And now store some new revisions in a version # And now store some new revisions in a version
version = 'test-version' version = 'test-version'
revid = self._dostore(oid, revid=revid, data=13, version=version) revid = self._dostore(oid, revid=revid, data=MinPO(13),
revid = self._dostore(oid, revid=revid, data=14, version=version) version=version)
revid = self._dostore(oid, revid=revid, data=15, version=version) revid = self._dostore(oid, revid=revid, data=MinPO(14),
version=version)
revid = self._dostore(oid, revid=revid, data=MinPO(15),
version=version)
# Now read back the object in both the non-version and version and # Now read back the object in both the non-version and version and
# make sure the values jive. # make sure the values jive.
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 12 assert zodb_unpickle(data) == MinPO(12)
data, revid = self._storage.load(oid, version) data, revid = self._storage.load(oid, version)
assert pickle.loads(data) == 15 assert zodb_unpickle(data) == MinPO(15)
def checkVersionedLoadErrors(self): def checkVersionedLoadErrors(self):
oid = self._storage.new_oid() oid = self._storage.new_oid()
version = 'test-version' version = 'test-version'
revid = self._dostore(oid, data=11) revid = self._dostore(oid, data=MinPO(11))
revid = self._dostore(oid, revid=revid, data=12, version=version) revid = self._dostore(oid, revid=revid, data=MinPO(12),
version=version)
# Try to load a bogus oid # Try to load a bogus oid
self.assertRaises(KeyError, self.assertRaises(KeyError,
self._storage.load, self._storage.load,
...@@ -45,17 +43,17 @@ class VersionStorage: ...@@ -45,17 +43,17 @@ class VersionStorage:
#JF# self._storage.load, #JF# self._storage.load,
#JF# oid, 'bogus') #JF# oid, 'bogus')
data, revid = self._storage.load(oid, 'bogus') data, revid = self._storage.load(oid, 'bogus')
assert pickle.loads(data) == 11 assert zodb_unpickle(data) == MinPO(11)
def checkVersionLock(self): def checkVersionLock(self):
oid = self._storage.new_oid() oid = self._storage.new_oid()
revid = self._dostore(oid, data=11) revid = self._dostore(oid, data=MinPO(11))
version = 'test-version' version = 'test-version'
revid = self._dostore(oid, revid=revid, data=12, version=version) revid = self._dostore(oid, revid=revid, data=MinPO(12), version=version)
self.assertRaises(POSException.VersionLockError, self.assertRaises(POSException.VersionLockError,
self._dostore, self._dostore,
oid, revid=revid, data=14, oid, revid=revid, data=MinPO(14),
version='another-version') version='another-version')
def checkVersionEmpty(self): def checkVersionEmpty(self):
...@@ -67,10 +65,12 @@ class VersionStorage: ...@@ -67,10 +65,12 @@ class VersionStorage:
assert self._storage.versionEmpty(version) assert self._storage.versionEmpty(version)
# Now store some objects # Now store some objects
oid = self._storage.new_oid() oid = self._storage.new_oid()
revid = self._dostore(oid, data=11) revid = self._dostore(oid, data=MinPO(11))
revid = self._dostore(oid, revid=revid, data=12) revid = self._dostore(oid, revid=revid, data=MinPO(12))
revid = self._dostore(oid, revid=revid, data=13, version=version) revid = self._dostore(oid, revid=revid, data=MinPO(13),
revid = self._dostore(oid, revid=revid, data=14, version=version) version=version)
revid = self._dostore(oid, revid=revid, data=MinPO(14),
version=version)
# The blank version should not be empty # The blank version should not be empty
#JF# The empty string is not a valid version. I think that this should #JF# The empty string is not a valid version. I think that this should
#JF# be an error. Let's punt for now. #JF# be an error. Let's punt for now.
...@@ -86,13 +86,16 @@ class VersionStorage: ...@@ -86,13 +86,16 @@ class VersionStorage:
oid1 = self._storage.new_oid() oid1 = self._storage.new_oid()
oid2 = self._storage.new_oid() oid2 = self._storage.new_oid()
oid3 = self._storage.new_oid() oid3 = self._storage.new_oid()
revid1 = self._dostore(oid1, data=11) revid1 = self._dostore(oid1, data=MinPO(11))
revid2 = self._dostore(oid2, data=12) revid2 = self._dostore(oid2, data=MinPO(12))
revid3 = self._dostore(oid3, data=13) revid3 = self._dostore(oid3, data=MinPO(13))
# Now create some new versions # Now create some new versions
revid1 = self._dostore(oid1, revid=revid1, data=14, version='one') revid1 = self._dostore(oid1, revid=revid1, data=MinPO(14),
revid2 = self._dostore(oid2, revid=revid2, data=15, version='two') version='one')
revid3 = self._dostore(oid3, revid=revid3, data=16, version='three') revid2 = self._dostore(oid2, revid=revid2, data=MinPO(15),
version='two')
revid3 = self._dostore(oid3, revid=revid3, data=MinPO(16),
version='three')
# Ask for the versions # Ask for the versions
versions = self._storage.versions() versions = self._storage.versions()
assert 'one' in versions assert 'one' in versions
...@@ -106,13 +109,16 @@ class VersionStorage: ...@@ -106,13 +109,16 @@ class VersionStorage:
def _setup_version(self, version='test-version'): def _setup_version(self, version='test-version'):
# Store some revisions in the non-version # Store some revisions in the non-version
oid = self._storage.new_oid() oid = self._storage.new_oid()
revid = self._dostore(oid, data=49) revid = self._dostore(oid, data=MinPO(49))
revid = self._dostore(oid, revid=revid, data=50) revid = self._dostore(oid, revid=revid, data=MinPO(50))
nvrevid = revid = self._dostore(oid, revid=revid, data=51) nvrevid = revid = self._dostore(oid, revid=revid, data=MinPO(51))
# Now do some stores in a version # Now do some stores in a version
revid = self._dostore(oid, revid=revid, data=52, version=version) revid = self._dostore(oid, revid=revid, data=MinPO(52),
revid = self._dostore(oid, revid=revid, data=53, version=version) version=version)
revid = self._dostore(oid, revid=revid, data=54, version=version) revid = self._dostore(oid, revid=revid, data=MinPO(53),
version=version)
revid = self._dostore(oid, revid=revid, data=MinPO(54),
version=version)
return oid, version return oid, version
def checkAbortVersion(self): def checkAbortVersion(self):
...@@ -125,7 +131,7 @@ class VersionStorage: ...@@ -125,7 +131,7 @@ class VersionStorage:
assert len(oids) == 1 assert len(oids) == 1
assert oids[0] == oid assert oids[0] == oid
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 51 assert zodb_unpickle(data) == MinPO(51)
def checkAbortVersionErrors(self): def checkAbortVersionErrors(self):
oid, version = self._setup_version() oid, version = self._setup_version()
...@@ -150,7 +156,7 @@ class VersionStorage: ...@@ -150,7 +156,7 @@ class VersionStorage:
assert len(oids) == 1 assert len(oids) == 1
assert oids[0] == oid assert oids[0] == oid
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 51 assert zodb_unpickle(data) == MinPO(51)
def checkModifyAfterAbortVersion(self): def checkModifyAfterAbortVersion(self):
oid, version = self._setup_version() oid, version = self._setup_version()
...@@ -162,41 +168,41 @@ class VersionStorage: ...@@ -162,41 +168,41 @@ class VersionStorage:
# Load the object's current state (which gets us the revid) # Load the object's current state (which gets us the revid)
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
# And modify it a few times # And modify it a few times
revid = self._dostore(oid, revid=revid, data=52) revid = self._dostore(oid, revid=revid, data=MinPO(52))
revid = self._dostore(oid, revid=revid, data=53) revid = self._dostore(oid, revid=revid, data=MinPO(53))
revid = self._dostore(oid, revid=revid, data=54) revid = self._dostore(oid, revid=revid, data=MinPO(54))
data, newrevid = self._storage.load(oid, '') data, newrevid = self._storage.load(oid, '')
assert newrevid == revid assert newrevid == revid
assert pickle.loads(data) == 54 assert zodb_unpickle(data) == MinPO(54)
def checkCommitToNonVersion(self): def checkCommitToNonVersion(self):
oid, version = self._setup_version() oid, version = self._setup_version()
data, revid = self._storage.load(oid, version) data, revid = self._storage.load(oid, version)
assert pickle.loads(data) == 54 assert zodb_unpickle(data) == MinPO(54)
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 51 assert zodb_unpickle(data) == MinPO(51)
# Try committing this version to the empty version # Try committing this version to the empty version
self._storage.tpc_begin(self._transaction) self._storage.tpc_begin(self._transaction)
oids = self._storage.commitVersion(version, '', self._transaction) oids = self._storage.commitVersion(version, '', self._transaction)
self._storage.tpc_vote(self._transaction) self._storage.tpc_vote(self._transaction)
self._storage.tpc_finish(self._transaction) self._storage.tpc_finish(self._transaction)
data, revid = self._storage.load(oid, '') data, revid = self._storage.load(oid, '')
assert pickle.loads(data) == 54 assert zodb_unpickle(data) == MinPO(54)
def checkCommitToOtherVersion(self): def checkCommitToOtherVersion(self):
oid1, version1 = self._setup_version('one') oid1, version1 = self._setup_version('one')
data, revid1 = self._storage.load(oid1, version1) data, revid1 = self._storage.load(oid1, version1)
assert pickle.loads(data) == 54 assert zodb_unpickle(data) == MinPO(54)
oid2, version2 = self._setup_version('two') oid2, version2 = self._setup_version('two')
data, revid2 = self._storage.load(oid2, version2) data, revid2 = self._storage.load(oid2, version2)
assert pickle.loads(data) == 54 assert zodb_unpickle(data) == MinPO(54)
# Let's make sure we can't get object1 in version2 # Let's make sure we can't get object1 in version2
#JF# This won't fail because we fall back to non-version data. #JF# This won't fail because we fall back to non-version data.
#JF# In fact, it must succed and give us 51 #JF# In fact, it must succed and give us 51
#JF# self.assertRaises(POSException.VersionError, #JF# self.assertRaises(POSException.VersionError,
#JF# self._storage.load, oid1, version2) #JF# self._storage.load, oid1, version2)
data, revid2 = self._storage.load(oid1, version2) data, revid2 = self._storage.load(oid1, version2)
assert pickle.loads(data) == 51 assert zodb_unpickle(data) == MinPO(51)
# Okay, now let's commit object1 to version2 # Okay, now let's commit object1 to version2
self._storage.tpc_begin(self._transaction) self._storage.tpc_begin(self._transaction)
...@@ -207,22 +213,22 @@ class VersionStorage: ...@@ -207,22 +213,22 @@ class VersionStorage:
assert len(oids) == 1 assert len(oids) == 1
assert oids[0] == oid1 assert oids[0] == oid1
data, revid = self._storage.load(oid1, version2) data, revid = self._storage.load(oid1, version2)
assert pickle.loads(data) == 54 assert zodb_unpickle(data) == MinPO(54)
data, revid = self._storage.load(oid2, version2) data, revid = self._storage.load(oid2, version2)
assert pickle.loads(data) == 54 assert zodb_unpickle(data) == MinPO(54)
#JF# Ditto, sort of #JF# Ditto, sort of
#JF# self.assertRaises(POSException.VersionError, #JF# self.assertRaises(POSException.VersionError,
#JF# self._storage.load, oid1, version1) #JF# self._storage.load, oid1, version1)
data, revid2 = self._storage.load(oid1, version1) data, revid2 = self._storage.load(oid1, version1)
assert pickle.loads(data) == 51 assert zodb_unpickle(data) == MinPO(51)
def checkAbortOneVersionCommitTheOther(self): def checkAbortOneVersionCommitTheOther(self):
oid1, version1 = self._setup_version('one') oid1, version1 = self._setup_version('one')
data, revid1 = self._storage.load(oid1, version1) data, revid1 = self._storage.load(oid1, version1)
assert pickle.loads(data) == 54 assert zodb_unpickle(data) == MinPO(54)
oid2, version2 = self._setup_version('two') oid2, version2 = self._setup_version('two')
data, revid2 = self._storage.load(oid2, version2) data, revid2 = self._storage.load(oid2, version2)
assert pickle.loads(data) == 54 assert zodb_unpickle(data) == MinPO(54)
# Let's make sure we can't get object1 in version2 # Let's make sure we can't get object1 in version2
#JF# It's not an error to load data in a different version when data #JF# It's not an error to load data in a different version when data
...@@ -231,7 +237,7 @@ class VersionStorage: ...@@ -231,7 +237,7 @@ class VersionStorage:
#JF# self.assertRaises(POSException.VersionError, #JF# self.assertRaises(POSException.VersionError,
#JF# self._storage.load, oid1, version2) #JF# self._storage.load, oid1, version2)
data, revid2 = self._storage.load(oid1, version2) data, revid2 = self._storage.load(oid1, version2)
assert pickle.loads(data) == 51 assert zodb_unpickle(data) == MinPO(51)
# First, let's abort version1 # First, let's abort version1
self._storage.tpc_begin(self._transaction) self._storage.tpc_begin(self._transaction)
...@@ -241,22 +247,22 @@ class VersionStorage: ...@@ -241,22 +247,22 @@ class VersionStorage:
assert len(oids) == 1 assert len(oids) == 1
assert oids[0] == oid1 assert oids[0] == oid1
data, revid = self._storage.load(oid1, '') data, revid = self._storage.load(oid1, '')
assert pickle.loads(data) == 51 assert zodb_unpickle(data) == MinPO(51)
#JF# Ditto #JF# Ditto
#JF# self.assertRaises(POSException.VersionError, #JF# self.assertRaises(POSException.VersionError,
#JF# self._storage.load, oid1, version1) #JF# self._storage.load, oid1, version1)
data, revid = self._storage.load(oid1, '') data, revid = self._storage.load(oid1, '')
assert pickle.loads(data) == 51 assert zodb_unpickle(data) == MinPO(51)
#JF# self.assertRaises(POSException.VersionError, #JF# self.assertRaises(POSException.VersionError,
#JF# self._storage.load, oid1, version2) #JF# self._storage.load, oid1, version2)
data, revid = self._storage.load(oid1, '') data, revid = self._storage.load(oid1, '')
assert pickle.loads(data) == 51 assert zodb_unpickle(data) == MinPO(51)
data, revid = self._storage.load(oid2, '') data, revid = self._storage.load(oid2, '')
assert pickle.loads(data) == 51 assert zodb_unpickle(data) == MinPO(51)
data, revid = self._storage.load(oid2, version2) data, revid = self._storage.load(oid2, version2)
assert pickle.loads(data) == 54 assert zodb_unpickle(data) == MinPO(54)
# Okay, now let's commit version2 back to the trunk # Okay, now let's commit version2 back to the trunk
self._storage.tpc_begin(self._transaction) self._storage.tpc_begin(self._transaction)
oids = self._storage.commitVersion(version2, '', self._transaction) oids = self._storage.commitVersion(version2, '', self._transaction)
...@@ -269,18 +275,18 @@ class VersionStorage: ...@@ -269,18 +275,18 @@ class VersionStorage:
#JF# self.assertRaises(POSException.VersionError, #JF# self.assertRaises(POSException.VersionError,
#JF# self._storage.load, oid1, version2) #JF# self._storage.load, oid1, version2)
data, revid = self._storage.load(oid1, '') data, revid = self._storage.load(oid1, '')
assert pickle.loads(data) == 51 assert zodb_unpickle(data) == MinPO(51)
#JF# self.assertRaises(POSException.VersionError, #JF# self.assertRaises(POSException.VersionError,
#JF# self._storage.load, oid2, version2) #JF# self._storage.load, oid2, version2)
# But the trunk should be up to date now # But the trunk should be up to date now
data, revid = self._storage.load(oid2, version2) data, revid = self._storage.load(oid2, version2)
assert pickle.loads(data) == 54 assert zodb_unpickle(data) == MinPO(54)
data, revid = self._storage.load(oid2, '') data, revid = self._storage.load(oid2, '')
assert pickle.loads(data) == 54 assert zodb_unpickle(data) == MinPO(54)
#JF# To do a test like you want, you have to add the data in a version #JF# To do a test like you want, you have to add the data in a version
oid = self._storage.new_oid() oid = self._storage.new_oid()
revid = self._dostore(oid, revid=revid, data=54, version='one') revid = self._dostore(oid, revid=revid, data=MinPO(54), version='one')
self.assertRaises(KeyError, self.assertRaises(KeyError,
self._storage.load, oid, '') self._storage.load, oid, '')
self.assertRaises(KeyError, self.assertRaises(KeyError,
......
...@@ -7,6 +7,7 @@ import StorageTestBase, BasicStorage, TransactionalUndoStorage ...@@ -7,6 +7,7 @@ import StorageTestBase, BasicStorage, TransactionalUndoStorage
import VersionStorage, TransactionalUndoVersionStorage import VersionStorage, TransactionalUndoVersionStorage
import PackableStorage import PackableStorage
import Synchronization import Synchronization
import ConflictResolution
class FileStorageTests( class FileStorageTests(
StorageTestBase.StorageTestBase, StorageTestBase.StorageTestBase,
...@@ -16,6 +17,7 @@ class FileStorageTests( ...@@ -16,6 +17,7 @@ class FileStorageTests(
TransactionalUndoVersionStorage.TransactionalUndoVersionStorage, TransactionalUndoVersionStorage.TransactionalUndoVersionStorage,
PackableStorage.PackableStorage, PackableStorage.PackableStorage,
Synchronization.SynchronizedStorage, Synchronization.SynchronizedStorage,
ConflictResolution.ConflictResolvingStorage,
): ):
def setUp(self): def setUp(self):
......
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