[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()