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

Tim Peters tim.one@comcast.net
Sun, 12 Jan 2003 14:54:49 -0500


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

Modified Files:
	test_conflict.py 
Log Message:
New testEmptyBucketConflict *should* raise ConflictError, but currently
creates an insane BTree instead.  Conflict resolution doesn't know enough
to empty a bucket correctly all on its own.

New test testEmptyBucketNoConflict shouldn't raise ConflictError, and
doesn't -- an empty bucket coming *into* ConflictResolution isn't
necessarily a problem.  This test is here mostly to ensure that the
eventual fix for testEmptyBucketConflict doesn't complain about too much.


=== Zope3/src/zodb/btrees/tests/test_conflict.py 1.8 => 1.9 ===
--- Zope3/src/zodb/btrees/tests/test_conflict.py:1.8	Sat Jan 11 01:59:59 2003
+++ Zope3/src/zodb/btrees/tests/test_conflict.py	Sun Jan 12 14:54:46 2003
@@ -523,6 +523,153 @@
         self.assertRaises(ConflictError, get_transaction().commit)
         get_transaction().abort()   # horrible things happen w/o this
 
+    def testEmptyBucketConflict(self):
+        # Tests that an emptied bucket *created by* conflict resolution is
+        # viewed as a conflict:  conflict resolution doesn't have enough
+        # info to unlink the empty bucket from the BTree correctly.
+        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)
+
+        # Invoke conflict resolution by committing a transaction.
+        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)
+
+        # In one transaction, delete half of bucket 1.
+        b = self.t
+        for k in 60, 64, 68, 72, 76, 80, 84, 88:
+            del b[k]
+        # bucket 0 has 15 values: 0, 4 .. 56
+        # bucket 1 has 7 values: 92, 96, 100, 104, 108, 112, 116
+        # bucket 2 has 20 values: 120, 124 .. 196
+        state = b.__getstate__()
+        # Looks like:  ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
+        # The next block is still verifying preconditions.
+        self.assertEqual(len(state) , 2)
+        self.assertEqual(len(state[0]), 5)
+        self.assertEqual(state[0][1], 60)
+        self.assertEqual(state[0][3], 120)
+
+        get_transaction().commit()
+
+        # In the other transaction, delete the other half of bucket 1.
+        b = copy
+        for k in 92, 96, 100, 104, 108, 112, 116:
+            del b[k]
+        # bucket 0 has 15 values: 0, 4 .. 56
+        # bucket 1 has 8 values: 60, 64, 68, 72, 76, 80, 84, 88
+        # bucket 2 has 20 values: 120, 124 .. 196
+        state = b.__getstate__()
+        # Looks like:  ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
+        # The next block is still verifying preconditions.
+        self.assertEqual(len(state), 2)
+        self.assertEqual(len(state[0]), 5)
+        self.assertEqual(state[0][1], 60)
+        self.assertEqual(state[0][3], 120)
+
+        # Conflict resolution empties bucket1 entirely.
+
+        # XXX This is broken:  it doesn't raise ConflictError now.
+        ### XXX The ConflictError imported at the top of this module isn't
+        ### XXX the ConflictError that gets raised here.
+        ##from zodb.interfaces import ConflictError
+        ##self.assertRaises(ConflictError, get_transaction().commit)
+        ##get_transaction().abort()   # horrible things happen w/o this
+
+        # XXX Instead it creates an insane BTree (with an empty bucket
+        # XXX still linked in.  Remove the remaining lines and uncomment
+        # XXX the lines above when this is fixed.
+        # XXX    AssertionError: Bucket length < 1
+        get_transaction().commit()
+        self.assertRaises(AssertionError, b._check)
+
+
+    def testEmptyBucketNoConflict(self):
+        # Tests that a plain empty bucket (on input) is not viewed as a
+        # conflict.
+        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)
+
+        # Invoke conflict resolution by committing a transaction.
+        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)
+
+        # In one transaction, just add a key.
+        b = self.t
+        b[1] = 1
+        # bucket 0 has 16 values: [0, 1] + [4, 8 .. 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)
+        # The next block is still verifying preconditions.
+        self.assertEqual(len(state), 2)
+        self.assertEqual(len(state[0]), 5)
+        self.assertEqual(state[0][1], 60)
+        self.assertEqual(state[0][3], 120)
+
+        get_transaction().commit()
+
+        # In the other transaction, delete bucket 2.
+        b = copy
+        for k in range(120, 200, 4):
+            del b[k]
+        # bucket 0 has 15 values: 0, 4 .. 56
+        # bucket 1 has 15 values: 60, 64 .. 116
+        state = b.__getstate__()
+        # Looks like:  ((bucket0, 60, bucket1), firstbucket)
+        # The next block is still verifying preconditions.
+        self.assertEqual(len(state), 2)
+        self.assertEqual(len(state[0]), 3)
+        self.assertEqual(state[0][1], 60)
+
+        # This shouldn't create a ConflictError.
+        get_transaction().commit()
+        # And the resulting BTree shouldn't have internal damage.
+        b._check()
 
 def test_suite():
     suite = TestSuite()