[Zodb-checkins] CVS: Zope3/src/zodb/btrees/tests - test_conflict.py:1.12

Tim Peters tim.one@comcast.net
Tue, 4 Mar 2003 16:06:42 -0500


Update of /cvs-repository/Zope3/src/zodb/btrees/tests
In directory cvs.zope.org:/tmp/cvs-serv8392/src/zodb/btrees/tests

Modified Files:
	test_conflict.py 
Log Message:
A joint production from Jeremy and Tim.

BTree__p_resolveConflict():  Fixed a bug unique to ZODB4.  We can't
resolve a BTree node conflict unless it's the special case of a BTree
with a single bucket.  Turns out the test suite didn't provoke any
kind of BTree conflict other than the special case, but one of Tim's
"stress test" programs did, and when the input wasn't the single-
bucket special case the code raised TypeError instead of ConflictError.
It raises ConflictError now (as it should), but raises TypeError for
"impossible" inputs (corrupt or hostile state).

The new test testCantResolveBTreeConflict() ensures that the non-special
case is provoked and handled correctly.


=== Zope3/src/zodb/btrees/tests/test_conflict.py 1.11 => 1.12 ===
--- Zope3/src/zodb/btrees/tests/test_conflict.py:1.11	Fri Jan 17 11:44:36 2003
+++ Zope3/src/zodb/btrees/tests/test_conflict.py	Tue Mar  4 16:06:39 2003
@@ -659,6 +659,61 @@
         # And the resulting BTree shouldn't have internal damage.
         b._check()
 
+    def testCantResolveBTreeConflict(self):
+        # Test that a conflict involving two different changes to
+        # an internal BTree node is unresolvable.  An internal node
+        # only changes when there are enough additions or deletions
+        # to a child bucket that the bucket is split or removed.
+        # It's (almost necessarily) a white-box test, and sensitive to
+        # implementation details.
+        b = self.t
+        for i in range(0, 200, 4):
+            b[i] = i
+        # bucket 0 has 15 values: 0, 4 .. 56
+        # bucket 1 has 15 values: 60, 64 .. 116
+        # bucket 2 has 20 values: 120, 124 .. 196
+        state = b.__getstate__()
+        # Looks like:  ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
+        # If these fail, the *preconditions* for running the test aren't
+        # satisfied -- the test itself hasn't been run yet.
+        self.assertEqual(len(state), 2)
+        self.assertEqual(len(state[0]), 5)
+        self.assertEqual(state[0][1], 60)
+        self.assertEqual(state[0][3], 120)
+
+        # Set up database connections to provoke conflict.
+        self.openDB()
+        r1 = self.db.open().root()
+        r1["t"] = self.t
+        get_transaction().commit()
+
+        r2 = self.db.open().root()
+        copy = r2["t"]
+        # Make sure all of copy is loaded.
+        list(copy.values())
+
+        self.assertEqual(self.t._p_serial, copy._p_serial)
+
+        # Now one transaction should add enough keys to cause a split,
+        # and another should remove all the keys in one bucket.
+
+        for k in range(200, 300, 4):
+            self.t[k] = k
+        get_transaction().commit()
+
+        for k in range(0, 60, 4):
+            del copy[k]
+
+        try:
+            get_transaction().commit()
+        except ConflictError, detail:
+            self.assert_(str(detail).endswith(
+                'Conflicting changes in an internal BTree node'))
+            get_transaction().abort()
+        else:
+            self.fail("expected ConflictError")
+
+
 def test_suite():
     suite = TestSuite()
     for k in (TestIOBTrees,   TestOOBTrees,   TestOIBTrees,   TestIIBTrees,