[Zope-Checkins] CVS: ZODB3/ZODB/tests - VersionStorage.py:1.17.4.2 TransactionalUndoStorage.py:1.22.2.3 StorageTestBase.py:1.17.8.4 PackableStorage.py:1.13.8.1
Jeremy Hylton
jeremy@zope.com
Tue, 13 May 2003 14:53:46 -0400
Update of /cvs-repository/ZODB3/ZODB/tests
In directory cvs.zope.org:/tmp/cvs-serv27185/ZODB/tests
Modified Files:
Tag: ZODB3-3_1-branch
VersionStorage.py TransactionalUndoStorage.py
StorageTestBase.py PackableStorage.py
Log Message:
Backport new pack tests from head.
=== ZODB3/ZODB/tests/VersionStorage.py 1.17.4.1 => 1.17.4.2 ===
--- ZODB3/ZODB/tests/VersionStorage.py:1.17.4.1 Fri Nov 15 12:18:55 2002
+++ ZODB3/ZODB/tests/VersionStorage.py Tue May 13 14:53:45 2003
@@ -1,3 +1,16 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
"""Run the version related tests for a storage.
Any storage that supports versions should be able to pass all these tests.
@@ -7,10 +20,14 @@
# They were introduced when Jim reviewed the original version of the
# code. Barry and Jeremy didn't understand versions then.
+import time
+
from ZODB import POSException
+from ZODB.referencesf import referencesf
from ZODB.Transaction import Transaction
from ZODB.tests.MinPO import MinPO
-from ZODB.tests.StorageTestBase import zodb_unpickle
+from ZODB.tests.StorageTestBase import zodb_unpickle, snooze
+from ZODB import DB
class VersionStorage:
@@ -37,7 +54,7 @@
# use repr() to avoid getting binary data in a traceback on error
self.assertEqual(`revid1`, `revid3`)
self.assertNotEqual(`revid2`, `revid3`)
-
+
def checkVersionedStoreAndLoad(self):
eq = self.assertEqual
# Store a couple of non-version revisions of the object
@@ -350,3 +367,164 @@
self._storage.load, oid, '')
self.assertRaises(KeyError,
self._storage.load, oid, 'two')
+
+ def checkCreateObjectInVersionWithAbort(self):
+ oid = self._storage.new_oid()
+ revid = self._dostore(oid, data=21, version="one")
+ revid = self._dostore(oid, revid=revid, data=23, version='one')
+ revid = self._dostore(oid, revid=revid, data=34, version='one')
+ # Now abort the version and the creation
+ t = Transaction()
+ self._storage.tpc_begin(t)
+ oids = self._storage.abortVersion('one', t)
+ self._storage.tpc_vote(t)
+ self._storage.tpc_finish(t)
+ self.assertEqual(oids, [oid])
+
+ def checkPackVersions(self):
+ db = DB(self._storage)
+ cn = db.open(version="testversion")
+ root = cn.root()
+
+ obj = root["obj"] = MinPO("obj")
+ root["obj2"] = MinPO("obj2")
+ txn = get_transaction()
+ txn.note("create 2 objs in version")
+ txn.commit()
+
+ obj.value = "77"
+ txn = get_transaction()
+ txn.note("modify obj in version")
+ txn.commit()
+
+ # undo the modification to generate a mix of backpointers
+ # and versions for pack to chase
+ info = db.undoInfo()
+ db.undo(info[0]["id"])
+ txn = get_transaction()
+ txn.note("undo modification")
+ txn.commit()
+
+ snooze()
+ self._storage.pack(time.time(), referencesf)
+
+ db.commitVersion("testversion")
+ txn = get_transaction()
+ txn.note("commit version")
+ txn.commit()
+
+ cn = db.open()
+ root = cn.root()
+ root["obj"] = "no version"
+
+ txn = get_transaction()
+ txn.note("modify obj")
+ txn.commit()
+
+ self._storage.pack(time.time(), referencesf)
+
+ def checkPackVersionsInPast(self):
+ db = DB(self._storage)
+ cn = db.open(version="testversion")
+ root = cn.root()
+
+ obj = root["obj"] = MinPO("obj")
+ root["obj2"] = MinPO("obj2")
+ txn = get_transaction()
+ txn.note("create 2 objs in version")
+ txn.commit()
+
+ obj.value = "77"
+ txn = get_transaction()
+ txn.note("modify obj in version")
+ txn.commit()
+
+ t0 = time.time()
+ snooze()
+
+ # undo the modification to generate a mix of backpointers
+ # and versions for pack to chase
+ info = db.undoInfo()
+ db.undo(info[0]["id"])
+ txn = get_transaction()
+ txn.note("undo modification")
+ txn.commit()
+
+ self._storage.pack(t0, referencesf)
+
+ db.commitVersion("testversion")
+ txn = get_transaction()
+ txn.note("commit version")
+ txn.commit()
+
+ cn = db.open()
+ root = cn.root()
+ root["obj"] = "no version"
+
+ txn = get_transaction()
+ txn.note("modify obj")
+ txn.commit()
+
+ self._storage.pack(time.time(), referencesf)
+
+ def checkPackVersionReachable(self):
+ db = DB(self._storage)
+ cn = db.open()
+ root = cn.root()
+
+ names = "a", "b", "c"
+
+ for name in names:
+ root[name] = MinPO(name)
+ get_transaction().commit()
+
+ for name in names:
+ cn2 = db.open(version=name)
+ rt2 = cn2.root()
+ obj = rt2[name]
+ obj.value = MinPO("version")
+ get_transaction().commit()
+ cn2.close()
+
+ root["d"] = MinPO("d")
+ get_transaction().commit()
+
+ self._storage.pack(time.time(), referencesf)
+ cn.sync()
+ cn._cache.clear()
+
+ # make sure all the non-version data is there
+ for name, obj in root.items():
+ self.assertEqual(name, obj.value)
+
+ # make sure all the version-data is there,
+ # and create a new revision in the version
+ for name in names:
+ cn2 = db.open(version=name)
+ rt2 = cn2.root()
+ obj = rt2[name].value
+ self.assertEqual(obj.value, "version")
+ obj.value = "still version"
+ get_transaction().commit()
+ cn2.close()
+
+ db.abortVersion("b")
+ txn = get_transaction()
+ txn.note("abort version b")
+ txn.commit()
+
+ t = time.time()
+ snooze()
+
+ L = db.undoInfo()
+ db.undo(L[0]["id"])
+ txn = get_transaction()
+ txn.note("undo abort")
+ txn.commit()
+
+ self._storage.pack(t, referencesf)
+
+ cn2 = db.open(version="b")
+ rt2 = cn2.root()
+ self.assertEqual(rt2["b"].value.value, "still version")
+
=== ZODB3/ZODB/tests/TransactionalUndoStorage.py 1.22.2.2 => 1.22.2.3 ===
--- ZODB3/ZODB/tests/TransactionalUndoStorage.py:1.22.2.2 Thu Oct 24 12:28:08 2002
+++ ZODB3/ZODB/tests/TransactionalUndoStorage.py Tue May 13 14:53:45 2003
@@ -1,3 +1,16 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
"""Check transactionalUndo().
Any storage that supports transactionalUndo() must pass these tests.
@@ -14,13 +27,21 @@
from Persistence import Persistent
from ZODB.tests.MinPO import MinPO
-from ZODB.tests.StorageTestBase import zodb_pickle, zodb_unpickle
+from ZODB.tests.StorageTestBase import zodb_pickle, zodb_unpickle, snooze
ZERO = '\0'*8
class C(Persistent):
pass
+def listeq(L1, L2):
+ """Return True if L1.sort() == L2.sort()"""
+ c1 = L1[:]
+ c2 = L2[:]
+ c1.sort()
+ c2.sort()
+ return c1 == c2
+
class TransactionalUndoStorage:
def _transaction_begin(self):
@@ -453,14 +474,8 @@
# Add a few object revisions
oid = self._storage.new_oid()
revid1 = self._dostore(oid, data=MinPO(51))
- # For the pack(), we need a timestamp greater than revid1's timestamp.
- # The semantics of pack()'s `t' argument is that all non-current
- # revisions with an earlier timestamp will be packed away. If they
- # were equal (because the Windows clock resolution is too coarse),
- # then we won't pack away the first revision.
- now = packtime = time.time()
- while packtime <= now:
- packtime = time.time()
+ packtime = time.time()
+ snooze() # time.time() now distinct from packtime
revid2 = self._dostore(oid, revid=revid1, data=MinPO(52))
revid3 = self._dostore(oid, revid=revid2, data=MinPO(53))
# Now get the undo log
@@ -542,6 +557,98 @@
eq(o1.obj, o2)
eq(o1.obj.obj, o3)
self._iterate()
+
+ def checkPackAfterUndoDeletion(self):
+ db = DB(self._storage)
+ cn = db.open()
+ root = cn.root()
+
+ pack_times = []
+ def set_pack_time():
+ snooze()
+ pack_times.append(time.time())
+
+ root["key0"] = MinPO(0)
+ root["key1"] = MinPO(1)
+ root["key2"] = MinPO(2)
+ txn = get_transaction()
+ txn.note("create 3 keys")
+ txn.commit()
+
+ set_pack_time()
+
+ del root["key1"]
+ txn = get_transaction()
+ txn.note("delete 1 key")
+ txn.commit()
+
+ set_pack_time()
+
+ root._p_deactivate()
+ cn.sync()
+ self.assert_(listeq(root.keys(), ["key0", "key2"]))
+
+ L = db.undoInfo()
+ db.undo(L[0]["id"])
+ txn = get_transaction()
+ txn.note("undo deletion")
+ txn.commit()
+
+ set_pack_time()
+
+ root._p_deactivate()
+ cn.sync()
+ self.assert_(listeq(root.keys(), ["key0", "key1", "key2"]))
+
+ for t in pack_times:
+ self._storage.pack(t, referencesf)
+
+ root._p_deactivate()
+ cn.sync()
+ self.assert_(listeq(root.keys(), ["key0", "key1", "key2"]))
+ for i in range(3):
+ obj = root["key%d" % i]
+ self.assertEqual(obj.value, i)
+ root.items()
+
+ def checkPackAfterUndoManyTimes(self):
+ db = DB(self._storage)
+ cn = db.open()
+ rt = cn.root()
+
+ rt["test"] = MinPO(1)
+ get_transaction().commit()
+ rt["test2"] = MinPO(2)
+ get_transaction().commit()
+ rt["test"] = MinPO(3)
+ txn = get_transaction()
+ txn.note("root of undo")
+ txn.commit()
+
+ packtimes = []
+ for i in range(10):
+ L = db.undoInfo()
+ db.undo(L[0]["id"])
+ txn = get_transaction()
+ txn.note("undo %d" % i)
+ txn.commit()
+ rt._p_deactivate()
+ cn.sync()
+
+ self.assertEqual(rt["test"].value, i % 2 and 3 or 1)
+ self.assertEqual(rt["test2"].value, 2)
+
+ packtimes.append(time.time())
+ snooze()
+
+ for t in packtimes:
+ self._storage.pack(t, referencesf)
+ cn.sync()
+ cn._cache.clear()
+ # The last undo set the value to 3 and pack should
+ # never change that.
+ self.assertEqual(rt["test"].value, 3)
+ self.assertEqual(rt["test2"].value, 2)
def checkTransactionalUndoIterator(self):
# check that data_txn set in iterator makes sense
=== ZODB3/ZODB/tests/StorageTestBase.py 1.17.8.3 => 1.17.8.4 ===
--- ZODB3/ZODB/tests/StorageTestBase.py:1.17.8.3 Wed Jan 22 11:58:15 2003
+++ ZODB3/ZODB/tests/StorageTestBase.py Tue May 13 14:53:45 2003
@@ -11,6 +11,7 @@
import pickle
import string
import sys
+import time
import types
import unittest
from cPickle import Pickler, Unpickler
@@ -70,6 +71,15 @@
inst.__setstate__(state)
return inst
+def snooze():
+ # In Windows, it's possible that two successive time.time() calls return
+ # the same value. Tim guarantees that time never runs backwards. You
+ # usually want to call this before you pack a storage, or must make other
+ # guarantees about increasing timestamps.
+ now = time.time()
+ while now == time.time():
+ time.sleep(0.1)
+
def handle_all_serials(oid, *args):
"""Return dict of oid to serialno from store() and tpc_vote().
=== ZODB3/ZODB/tests/PackableStorage.py 1.13 => 1.13.8.1 ===
--- ZODB3/ZODB/tests/PackableStorage.py:1.13 Wed Aug 14 18:07:09 2002
+++ ZODB3/ZODB/tests/PackableStorage.py Tue May 13 14:53:45 2003
@@ -1,3 +1,16 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
"""Run some tests relevant for storages that support pack()."""
try:
@@ -13,6 +26,8 @@
from StringIO import StringIO
import time
+from ZODB import DB
+from Persistence import Persistent
from ZODB.referencesf import referencesf
@@ -42,6 +57,9 @@
return self._oid
+class C(Persistent):
+ pass
+
# Here's where all the magic occurs. Sadly, the pickle module is a bit
# underdocumented, but here's what happens: by setting the persistent_id
# attribute to getpersid() on the pickler, that function gets called for every
@@ -159,8 +177,10 @@
eq(pobj.value, 3)
# Now pack all transactions; need to sleep a second to make
# sure that the pack time is greater than the last commit time.
- time.sleep(1)
- self._storage.pack(time.time(), referencesf)
+ now = packtime = time.time()
+ while packtime <= now:
+ packtime = time.time()
+ self._storage.pack(packtime, referencesf)
# All revisions of the object should be gone, since there is no
# reference from the root object to this object.
raises(KeyError, self._storage.loadSerial, oid, revid1)
@@ -210,8 +230,10 @@
eq(pobj.value, 3)
# Now pack just revisions 1 and 2. The object's current revision
# should stay alive because it's pointed to by the root.
- time.sleep(1)
- self._storage.pack(time.time(), referencesf)
+ now = packtime = time.time()
+ while packtime <= now:
+ packtime = time.time()
+ self._storage.pack(packtime, referencesf)
# Make sure the revisions are gone, but that object zero and revision
# 3 are still there and correct
data, revid = self._storage.load(ZERO, '')
@@ -287,8 +309,10 @@
# Now pack just revisions 1 and 2 of object1. Object1's current
# revision should stay alive because it's pointed to by the root, as
# should Object2's current revision.
- time.sleep(1)
- self._storage.pack(time.time(), referencesf)
+ now = packtime = time.time()
+ while packtime <= now:
+ packtime = time.time()
+ self._storage.pack(packtime, referencesf)
# Make sure the revisions are gone, but that object zero, object2, and
# revision 3 of object1 are still there and correct.
data, revid = self._storage.load(ZERO, '')
@@ -312,3 +336,43 @@
pobj = pickle.loads(data)
eq(pobj.getoid(), oid2)
eq(pobj.value, 11)
+
+ def checkPackUnlinkedFromRoot(self):
+ eq = self.assertEqual
+ db = DB(self._storage)
+ conn = db.open()
+ root = conn.root()
+
+ txn = get_transaction()
+ txn.note('root')
+ txn.commit()
+
+ now = packtime = time.time()
+ while packtime <= now:
+ packtime = time.time()
+
+ obj = C()
+ obj.value = 7
+
+ root['obj'] = obj
+ txn = get_transaction()
+ txn.note('root -> o1')
+ txn.commit()
+
+ del root['obj']
+ txn = get_transaction()
+ txn.note('root -x-> o1')
+ txn.commit()
+
+ self._storage.pack(packtime, referencesf)
+
+ log = self._storage.undoLog()
+ tid = log[0]['id']
+ db.undo(tid)
+ txn = get_transaction()
+ txn.note('undo root -x-> o1')
+ txn.commit()
+
+ conn.sync()
+
+ eq(root['obj'].value, 7)