Commit 097e74a2 authored by Julien Muchembled's avatar Julien Muchembled

Check invalidation in case of conflict resolution

parent f46359eb
...@@ -13,9 +13,10 @@ ...@@ -13,9 +13,10 @@
############################################################################## ##############################################################################
"""Tests for application-level conflict resolution.""" """Tests for application-level conflict resolution."""
from ZODB import DB
from ZODB.POSException import ConflictError, UndoError from ZODB.POSException import ConflictError, UndoError
from persistent import Persistent from persistent import Persistent
from transaction import Transaction from transaction import Transaction, TransactionManager
from ZODB.tests.StorageTestBase import zodb_unpickle, zodb_pickle from ZODB.tests.StorageTestBase import zodb_unpickle, zodb_pickle
...@@ -26,8 +27,8 @@ class PCounter(Persistent): ...@@ -26,8 +27,8 @@ class PCounter(Persistent):
def __repr__(self): def __repr__(self):
return "<PCounter %d>" % self._value return "<PCounter %d>" % self._value
def inc(self): def inc(self, n=1):
self._value = self._value + 1 self._value = self._value + n
def _p_resolveConflict(self, oldState, savedState, newState): def _p_resolveConflict(self, oldState, savedState, newState):
savedDiff = savedState['_value'] - oldState['_value'] savedDiff = savedState['_value'] - oldState['_value']
...@@ -55,46 +56,38 @@ class PCounter4(PCounter): ...@@ -55,46 +56,38 @@ class PCounter4(PCounter):
class ConflictResolvingStorage: class ConflictResolvingStorage:
def checkResolve(self): def checkResolve(self, resolvable=True):
obj = PCounter() db = DB(self._storage)
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.assertEqual(inst._value, 5)
def checkUnresolvable(self):
obj = PCounter2()
obj.inc()
oid = self._storage.new_oid() t1 = TransactionManager()
c1 = db.open(t1)
o1 = c1.root()['p'] = (PCounter if resolvable else PCounter2)()
o1.inc()
t1.commit()
revid1 = self._dostoreNP(oid, data=zodb_pickle(obj)) t2 = TransactionManager()
c2 = db.open(t2)
o2 = c2.root()['p']
o2.inc(2)
t2.commit()
obj.inc() o1.inc(3)
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))
try: try:
self._dostoreNP(oid, revid=revid1, data=zodb_pickle(obj)) t1.commit()
except ConflictError as err: except ConflictError as err:
self.assertTrue("PCounter2" in str(err)) self.assertIn(".PCounter2,", str(err))
self.assertEqual(o1._value, 3)
else: else:
self.fail("Expected ConflictError") self.assertTrue(resolvable, "Expected ConflictError")
self.assertEqual(o1._value, 6)
t2.begin()
self.assertEqual(o2._value, o1._value)
db.close()
def checkUnresolvable(self):
self.checkResolve(False)
def checkZClassesArentResolved(self): def checkZClassesArentResolved(self):
from ZODB.ConflictResolution import find_global, BadClassName from ZODB.ConflictResolution import find_global, BadClassName
......
...@@ -39,6 +39,31 @@ import ZODB.tests.util ...@@ -39,6 +39,31 @@ import ZODB.tests.util
import ZODB.utils import ZODB.utils
from zope.testing import renormalizing from zope.testing import renormalizing
# With the following monkey-patch, we can test the different ways
# to update _p_changed/_p_serial status of committed oids.
class DemoStorage(ZODB.DemoStorage.DemoStorage):
delayed_store = False
def tpc_begin(self, *args):
super(DemoStorage, self).tpc_begin(*args)
self.__stored = []
def store(self, oid, *args):
s = super(DemoStorage, self).store(oid, *args)
if not self.delayed_store:
return s
self.__stored.append((oid, s))
tpc_vote = property(lambda self: self._tpc_vote, lambda *_: None)
def _tpc_vote(self, transaction):
s = self.changes.tpc_vote(transaction)
assert s is None, s
return self.__stored if self.delayed_store else s
ZODB.DemoStorage.DemoStorage = DemoStorage
class DemoStorageTests( class DemoStorageTests(
StorageTestBase.StorageTestBase, StorageTestBase.StorageTestBase,
...@@ -104,6 +129,10 @@ class DemoStorageTests( ...@@ -104,6 +129,10 @@ class DemoStorageTests(
self._checkHistory(base_and_changes()) self._checkHistory(base_and_changes())
self._storage = self._storage.pop() self._storage = self._storage.pop()
def checkResolveLate(self):
self._storage.delayed_store = True
self.checkResolve()
class DemoStorageHexTests(DemoStorageTests): class DemoStorageHexTests(DemoStorageTests):
......
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