[Zope3-checkins] CVS: Zope3/src/zodb/btrees/tests - test_conflict.py:1.11.10.1 test_btrees.py:1.4.8.1

Jeremy Hylton jeremy@zope.com
Wed, 12 Mar 2003 16:38:12 -0500


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

Modified Files:
      Tag: opaque-pickles-branch
	test_conflict.py test_btrees.py 
Log Message:
Update from trunk.


=== Zope3/src/zodb/btrees/tests/test_conflict.py 1.11 => 1.11.10.1 ===
--- 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	Wed Mar 12 16:37:38 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,


=== Zope3/src/zodb/btrees/tests/test_btrees.py 1.4 => 1.4.8.1 ===
--- Zope3/src/zodb/btrees/tests/test_btrees.py:1.4	Fri Jan 31 14:50:30 2003
+++ Zope3/src/zodb/btrees/tests/test_btrees.py	Wed Mar 12 16:37:38 2003
@@ -17,91 +17,106 @@
 from zodb.btrees.OIBTree import OIBTree, OIBucket, OISet, OITreeSet
 
 from zodb.btrees.check import check
-
+from zodb.db import DB
+from zodb.storage.mapping import MappingStorage
 
 from transaction import get_transaction
 
-from glob import glob
 import os
 import random
 from unittest import TestCase, TestSuite, TextTestRunner, makeSuite
 
 class Base(TestCase):
     """ Tests common to all types: sets, buckets, and BTrees """
+
+    db = None
+
     def tearDown(self):
+        if self.db is not None:
+            self.db.close()
         self.t = None
         del self.t
 
     def _getRoot(self):
-        from zodb.storage.file import DB
-        n = 'fs_tmp__%s' % os.getpid()
-        db = DB(n, cache_size=1)
-        root = db.open().root()
-        return root
-
-    def _closeDB(self, root):
-        if root is not None:
-            root._p_jar._db.close()
-
-    def _delDB(self):
-        for file in glob('fs_tmp__*'):
-            os.remove(file)
+        if self.db is None:
+            self.db = DB(MappingStorage(), cache_size=1)
+        return self.db.open().root()
+
+    def _closeRoot(self, root):
+        root._p_jar.close()
 
     def testLoadAndStore(self):
         for i in 0, 10, 1000:
             t = self.t.__class__()
             self._populate(t, i)
             root = None
-            try:
-                root = self._getRoot()
-                root[i] = t
-                get_transaction().commit()
-            except:
-                self._closeDB(root)
-                self._delDB()
-                raise
-
-            self._closeDB(root)
-
-            try:
-                root = self._getRoot()
-                #XXX BTree stuff doesn't implement comparison
-                if hasattr(t, 'items'):
-                    self.assertEqual(list(root[i].items()) , list(t.items()))
-                else:
-                    self.assertEqual(list(root[i].keys()) , list(t.keys()))
-            finally:
-                self._closeDB(root)
-                self._delDB()
-
+            root = self._getRoot()
+            root[i] = t
+            get_transaction().commit()
+
+            root2 = self._getRoot()
+            if hasattr(t, 'items'):
+                self.assertEqual(list(root2[i].items()) , list(t.items()))
+            else:
+                self.assertEqual(list(root2[i].keys()) , list(t.keys()))
+
+            self._closeRoot(root)
+            self._closeRoot(root2)
+            
     def testGhostUnghost(self):
         for i in 0, 10, 1000:
             t = self.t.__class__()
             self._populate(t, i)
-            root = None
-            try:
-                root = self._getRoot()
-                root[i] = t
-                get_transaction().commit()
-            except:
-                self._closeDB(root)
-                self._delDB()
-                raise
+            root = self._getRoot()
+            root[i] = t
+            get_transaction().commit()
+
+            root2 = self._getRoot()
+            root2[i]._p_deactivate()
+            get_transaction().commit()
+            if hasattr(t, 'items'):
+                self.assertEqual(list(root2[i].items()) , list(t.items()))
+            else:
+                self.assertEqual(list(root2[i].keys()) , list(t.keys()))
+                
+            self._closeRoot(root)
+            self._closeRoot(root2)
+
+    def testSimpleExclusiveKeyRange(self):
+        t = self.t.__class__()
+        self.assertEqual(list(t.keys()), [])
+        self.assertEqual(list(t.keys(excludemin=True)), [])
+        self.assertEqual(list(t.keys(excludemax=True)), [])
+        self.assertEqual(list(t.keys(excludemin=True, excludemax=True)), [])
+
+        self._populate(t, 1)
+        self.assertEqual(list(t.keys()), [0])
+        self.assertEqual(list(t.keys(excludemin=True)), [])
+        self.assertEqual(list(t.keys(excludemax=True)), [])
+        self.assertEqual(list(t.keys(excludemin=True, excludemax=True)), [])
 
-            self._closeDB(root)
+        t.clear()
+        self._populate(t, 2)
+        self.assertEqual(list(t.keys()), [0, 1])
+        self.assertEqual(list(t.keys(excludemin=True)), [1])
+        self.assertEqual(list(t.keys(excludemax=True)), [0])
+        self.assertEqual(list(t.keys(excludemin=True, excludemax=True)), [])
 
-            root = None
-            try:
-                root = self._getRoot()
-                root[i]._p_deactivate()
-                get_transaction().commit()
-                if hasattr(t, 'items'):
-                    self.assertEqual(list(root[i].items()) , list(t.items()))
-                else:
-                    self.assertEqual(list(root[i].keys()) , list(t.keys()))
-            finally:
-                self._closeDB(root)
-                self._delDB()
+        t.clear()
+        self._populate(t, 3)
+        self.assertEqual(list(t.keys()), [0, 1, 2])
+        self.assertEqual(list(t.keys(excludemin=True)), [1, 2])
+        self.assertEqual(list(t.keys(excludemax=True)), [0, 1])
+        self.assertEqual(list(t.keys(excludemin=True, excludemax=True)), [1])
+
+        self.assertEqual(list(t.keys(-1, 3, excludemin=True, excludemax=True)),
+                         [0, 1, 2])
+        self.assertEqual(list(t.keys(0, 3, excludemin=True, excludemax=True)),
+                         [1, 2])
+        self.assertEqual(list(t.keys(-1, 2, excludemin=True, excludemax=True)),
+                         [0, 1])
+        self.assertEqual(list(t.keys(0, 2, excludemin=True, excludemax=True)),
+                         [1])
 
 class MappingBase(Base):
     """ Tests common to mappings (buckets, btrees) """
@@ -181,6 +196,10 @@
             lst.sort()
             self.assertEqual(lst,range(0+x,99-x+1))
 
+            lst = list(self.t.values(max=99-x, min=0+x))
+            lst.sort()
+            self.assertEqual(lst,range(0+x,99-x+1))
+
     def testKeysWorks(self):
         for x in range(100):
             self.t[x] = x
@@ -194,6 +213,9 @@
             lst = self.t.keys(0+x,99-x)
             self.assertEqual(list(lst), range(0+x, 99-x+1))
 
+            lst = self.t.keys(max=99-x, min=0+x)
+            self.assertEqual(list(lst), range(0+x, 99-x+1))
+
         self.assertEqual(len(v), 100)
 
     def testItemsWorks(self):
@@ -211,6 +233,13 @@
             self.assertEqual(x, (i, 2*i))
             i += 1
 
+        items = list(self.t.items(min=12, max=20))
+        self.assertEqual(items, zip(range(12, 21), range(24, 43, 2)))
+
+        items = list(self.t.iteritems(min=12, max=20))
+        self.assertEqual(items, zip(range(12, 21), range(24, 43, 2)))
+
+
     def testDeleteInvalidKeyRaisesKeyError(self):
         self.assertRaises(KeyError, self._deletefail)
 
@@ -282,6 +311,11 @@
         self.assertEqual(list(keys), [])
         self.assertEqual(list(t.iterkeys(200, 50)), [])
 
+        keys = t.keys(max=50, min=200)
+        self.assertEqual(len(keys), 0)
+        self.assertEqual(list(keys), [])
+        self.assertEqual(list(t.iterkeys(max=50, min=200)), [])
+
     def testSlicing(self):
         # Test that slicing of .keys()/.values()/.items() works exactly the
         # same way as slicing a Python list with the same contents.
@@ -425,15 +459,15 @@
                     for key2 in min_mid_max:
                         for hi in range(key2 - 1, key2 + 2):
                             goodkeys = [k for k in keys if lo <= k <= hi]
-                            got = t.iterkeys(lo, hi)
+                            got = t.iterkeys(min=lo, max=hi)
                             self.assertEqual(goodkeys, list(got))
 
                             goodvalues = [t[k] for k in goodkeys]
-                            got = t.itervalues(lo, hi)
+                            got = t.itervalues(lo, max=hi)
                             self.assertEqual(goodvalues, list(got))
 
                             gooditems = zip(goodkeys, goodvalues)
-                            got = t.iteritems(lo, hi)
+                            got = t.iteritems(max=hi, min=lo)
                             self.assertEqual(gooditems, list(got))
 
     def testBadUpdateTupleSize(self):
@@ -457,6 +491,62 @@
         self.t.update([(1, 2)])
         self.assertEqual(list(self.t.items()), [(1, 2)])
 
+    def testSimpleExclusivRanges(self):
+        def identity(x):
+            return x
+        def dup(x):
+            return [(y, y) for y in x]
+
+        for methodname, f in (("keys", identity),
+                              ("values", identity),
+                              ("items", dup),
+                              ("iterkeys", identity),
+                              ("itervalues", identity),
+                              ("iteritems", dup)):
+
+            t = self.t.__class__()
+            meth = getattr(t, methodname, None)
+            if meth is None:
+                continue
+
+            self.assertEqual(list(meth()), [])
+            self.assertEqual(list(meth(excludemin=True)), [])
+            self.assertEqual(list(meth(excludemax=True)), [])
+            self.assertEqual(list(meth(excludemin=True, excludemax=True)), [])
+
+            self._populate(t, 1)
+            self.assertEqual(list(meth()), f([0]))
+            self.assertEqual(list(meth(excludemin=True)), [])
+            self.assertEqual(list(meth(excludemax=True)), [])
+            self.assertEqual(list(meth(excludemin=True, excludemax=True)), [])
+
+            t.clear()
+            self._populate(t, 2)
+            self.assertEqual(list(meth()), f([0, 1]))
+            self.assertEqual(list(meth(excludemin=True)), f([1]))
+            self.assertEqual(list(meth(excludemax=True)), f([0]))
+            self.assertEqual(list(meth(excludemin=True, excludemax=True)), [])
+
+            t.clear()
+            self._populate(t, 3)
+            self.assertEqual(list(meth()), f([0, 1, 2]))
+            self.assertEqual(list(meth(excludemin=True)), f([1, 2]))
+            self.assertEqual(list(meth(excludemax=True)), f([0, 1]))
+            self.assertEqual(list(meth(excludemin=True, excludemax=True)),
+                            f([1]))
+            self.assertEqual(list(meth(-1, 3, excludemin=True,
+                                       excludemax=True)),
+                             f([0, 1, 2]))
+            self.assertEqual(list(meth(0, 3, excludemin=True,
+                                       excludemax=True)),
+                             f([1, 2]))
+            self.assertEqual(list(meth(-1, 2, excludemin=True,
+                                       excludemax=True)),
+                             f([0, 1]))
+            self.assertEqual(list(meth(0, 2, excludemin=True,
+                                       excludemax=True)),
+                             f([1]))
+
 class NormalSetTests(Base):
     """ Test common to all set types """
 
@@ -509,9 +599,11 @@
     def testKeys(self):
         t = self.t
         r = xrange(1000)
-        for x in r: t.insert(x)
+        for x in r:
+            t.insert(x)
         diff = lsubtract(t.keys(), r)
-        self.assertEqual(diff , [], diff)
+        self.assertEqual(diff, [])
+
 
     def testClear(self):
         t = self.t
@@ -573,6 +665,10 @@
         self.assertEqual(len(keys), 0)
         self.assertEqual(list(keys), [])
 
+        keys = t.keys(max=50, min=200)
+        self.assertEqual(len(keys), 0)
+        self.assertEqual(list(keys), [])
+
     def testSlicing(self):
         # Test that slicing of .keys() works exactly the same way as slicing
         # a Python list with the same contents.
@@ -881,7 +977,7 @@
         # to "go backwards" in the BTree then; if it doesn't, it will
         # erroneously claim that the range is empty.
         del t[firstkey]
-        therange = t.keys(-1, firstkey)
+        therange = t.keys(min=-1, max=firstkey)
         self.assertEqual(len(therange), firstkey)
         self.assertEqual(list(therange), range(firstkey))
 
@@ -1133,9 +1229,16 @@
         # Try all range searches.
         for lo in range(lokey - 1, hikey + 2):
             for hi in range(lo - 1, hikey + 2):
-                want = [k for k in keys if lo <= k <= hi]
-                got = list(tree.keys(lo, hi))
-                self.assertEqual(want, got)
+                for skipmin in False, True:
+                    for skipmax in False, True:
+                        wantlo, wanthi = lo, hi
+                        if skipmin:
+                            wantlo += 1
+                        if skipmax:
+                            wanthi -= 1
+                        want = [k for k in keys if wantlo <= k <= wanthi]
+                        got = list(tree.keys(lo, hi, skipmin, skipmax))
+                        self.assertEqual(want, got)
 
     def testRanges(self):
         t, keys = self._build_degenerate_tree()