[Zodb-checkins] CVS: Zope3/src/zodb/tests - test_zodb.py:1.3.8.1 test_pool.py:1.1.8.1 test_invalidation.py:1.2.8.1 test_connection.py:1.2.12.1
Jeremy Hylton
jeremy@zope.com
Wed, 12 Mar 2003 17:12:20 -0500
Update of /cvs-repository/Zope3/src/zodb/tests
In directory cvs.zope.org:/tmp/cvs-serv7164
Modified Files:
Tag: opaque-pickles-branch
test_zodb.py test_pool.py test_invalidation.py
test_connection.py
Log Message:
Update from trunk.
=== Zope3/src/zodb/tests/test_zodb.py 1.3 => 1.3.8.1 ===
--- Zope3/src/zodb/tests/test_zodb.py:1.3 Tue Jan 28 11:42:23 2003
+++ Zope3/src/zodb/tests/test_zodb.py Wed Mar 12 17:12:14 2003
@@ -15,7 +15,8 @@
import unittest
import tempfile
-from zodb.storage.file import DB
+from zodb.db import DB
+from zodb.storage.file import FileStorage
from zodb.utils import u64
from zodb.tests.undo import TransactionalUndoDB
from persistence.dict import PersistentDict
@@ -105,7 +106,7 @@
unittest.TestCase):
def setUp(self):
- self._db = DB(_fsname, create=1)
+ self._db = DB(FileStorage(_fsname, create=True))
self._conn = self._db.open()
self._root = self._conn.root()
=== Zope3/src/zodb/tests/test_pool.py 1.1 => 1.1.8.1 ===
--- Zope3/src/zodb/tests/test_pool.py:1.1 Tue Jan 21 13:19:56 2003
+++ Zope3/src/zodb/tests/test_pool.py Wed Mar 12 17:12:14 2003
@@ -17,7 +17,8 @@
import time
import unittest
-from zodb.storage.mapping import DB
+from zodb.db import DB
+from zodb.storage.mapping import MappingStorage
class Counter:
@@ -60,7 +61,7 @@
def setUp(self):
self.close = threading.Event()
- self.db = DB(pool_size=7)
+ self.db = DB(MappingStorage(), pool_size=7)
self.threads = []
def tearDown(self):
=== Zope3/src/zodb/tests/test_invalidation.py 1.2 => 1.2.8.1 ===
--- Zope3/src/zodb/tests/test_invalidation.py:1.2 Tue Jan 21 13:19:56 2003
+++ Zope3/src/zodb/tests/test_invalidation.py Wed Mar 12 17:12:14 2003
@@ -69,5 +69,24 @@
root = cn.root()
self.assertEqual(root[1].value, 2)
+ def testReadConflict(self):
+ # If an object is modified after a transaction begins and the
+ # transaction reads the object, it should get a read conflict.
+ pass
+
+ def testReadConflictIgnored(self):
+ # If an application gets a read conflict and ignores it, the
+ # data manager for the object should refuse to commit the
+ # transaction.
+ pass
+
+ def testAtomicInvalidations(self):
+ # Invalidations must be delivered atomically. If several
+ # objects are modified by a transaction, other transactions
+ # should apply all the invalidations at once. Otherwise, a
+ # mix of out-of-date and current object revisions could be
+ # read.
+ pass
+
def test_suite():
return unittest.makeSuite(InvalidationTests)
=== Zope3/src/zodb/tests/test_connection.py 1.2 => 1.2.12.1 ===
--- Zope3/src/zodb/tests/test_connection.py:1.2 Wed Dec 25 09:12:21 2002
+++ Zope3/src/zodb/tests/test_connection.py Wed Mar 12 17:12:14 2003
@@ -14,22 +14,40 @@
import unittest
from persistence import Persistent
+from persistence.dict import PersistentDict
from transaction.tests.abstestIDataManager import IDataManagerTests
+from transaction import get_transaction
-from zodb.storage.mapping import DB
+from zodb.db import DB
+from zodb.storage.mapping import MappingStorage
from zodb.ztransaction import Transaction
+from zodb.interfaces import ReadConflictError, ConflictError
class P(Persistent):
pass
+class Independent(Persistent):
+
+ def _p_independent(self):
+ return True
+
+class DecoyIndependent(Persistent):
+
+ def _p_independent(self):
+ return False
+
class ConnectionTests(IDataManagerTests):
def setUp(self):
- self.db = DB()
+ self.db = DB(MappingStorage())
self.datamgr = self.db.open()
self.obj = P()
self.txn_factory = Transaction
+ def tearDown(self):
+ # Make sure the test doesn't leave a transaction active.
+ get_transaction().abort()
+
def get_transaction(self):
t = super(ConnectionTests, self).get_transaction()
t.setUser('IDataManagerTests')
@@ -38,6 +56,101 @@
def test_cacheGC(self):
self.datamgr.cacheGC()
+
+ def testReadConflict(self, shouldFail=True):
+ # Two transactions run concurrently. Each reads some object,
+ # then one commits and the other tries to read an object
+ # modified by the first. This read should fail with a conflict
+ # error because the object state read is not necessarily
+ # consistent with the objects read earlier in the transaction.
+
+ r1 = self.datamgr.root()
+ r1["p"] = self.obj
+ self.obj.child1 = P()
+ get_transaction().commit()
+
+ # start a new transaction with a new connection
+ cn2 = self.db.open()
+ r2 = cn2.root()
+
+ # start a new transaction with the other connection
+ txn = get_transaction()
+ txn.suspend()
+
+ self.obj.child2 = P()
+ get_transaction().commit()
+
+ # resume the transaction using cn2
+ txn.resume()
+ obj = r2["p"]
+ # An attempt to access obj should fail, because r2 was read
+ # earlier in the transaction and obj was modified by the othe
+ # transaction.
+ if shouldFail:
+ self.assertRaises(ReadConflictError, lambda: obj.child1)
+ else:
+ # make sure that accessing the object succeeds
+ obj.child1
+ txn.abort()
+
+ def testReadConflictIgnored(self):
+ # Test that an application that catches a read conflict and
+ # continues can not commit the transaction later.
+ root = self.datamgr.root()
+ root["real_data"] = real_data = PersistentDict()
+ root["index"] = index = PersistentDict()
+
+ real_data["a"] = PersistentDict({"indexed_value": False})
+ real_data["b"] = PersistentDict({"indexed_value": True})
+ index[True] = PersistentDict({"b": 1})
+ index[False] = PersistentDict({"a": 1})
+ get_transaction().commit()
+
+ # load some objects from one connection
+ cn2 = self.db.open()
+ r2 = cn2.root()
+ real_data2 = r2["real_data"]
+ index2 = r2["index"]
+
+ # start a new transaction with the other connection
+ txn = get_transaction()
+ txn.suspend()
+
+ real_data["b"]["indexed_value"] = False
+ del index[True]["b"]
+ index[False]["b"] = 1
+ get_transaction().commit()
+
+ # switch back to the other transaction
+ txn.resume()
+
+ del real_data2["a"]
+ try:
+ del index2[False]["a"]
+ except ReadConflictError:
+ # This is the crux of the text. Ignore the error.
+ pass
+ else:
+ self.fail("No conflict occurred")
+
+ # real_data2 still ready to commit
+ self.assert_(real_data2._p_changed)
+
+ # index2 values not ready to commit
+ self.assert_(not index2._p_changed)
+ self.assert_(not index2[False]._p_changed)
+ self.assert_(not index2[True]._p_changed)
+
+ self.assertRaises(ConflictError, txn.commit)
+ get_transaction().abort()
+
+ def testIndependent(self):
+ self.obj = Independent()
+ self.testReadConflict(shouldFail=False)
+
+ def testNotIndependent(self):
+ self.obj = DecoyIndependent()
+ self.testReadConflict()
def tearDown(self):
self.datamgr.close()