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