[Zope-Checkins] CVS: StandaloneZODB/ZODB/tests - IteratorStorage.py:1.10 testFileStorage.py:1.15

Guido van Rossum guido@python.org
Thu, 24 Jan 2002 21:15:08 -0500


Update of /cvs-repository/StandaloneZODB/ZODB/tests
In directory cvs.zope.org:/tmp/cvs-serv21356/tests

Modified Files:
	IteratorStorage.py testFileStorage.py 
Log Message:
Commit changes from the short-lived "Recovery" branch to the trunk.
Highlights:

BaseStorage.py:

Fix copyTransactionsFrom() when commitVersion(), abortVersion() or
transactionalUndo() is used.

FileStorage.py:

Add restore() method, which can store data records corresponding to
undo or version manipulations; add close() method to FileIterator
class; raise POSKeyError instead of KeyError for bad keys, to ensure
safe formatting of transaction ids (binary strings) in tracebacks.

POSException.py:

Add POSKeyError.

fsdump.py:

Deal with records indicating the undo or abort of a version doing
object creation.

tests/IteratorStorage.py:

New unittests for the iterator() method and interface of the
storage API; new unit tests for extended file iterators; new class,
IteratorDeepCompare; test of the iterator .close() method.

tests/testFileStorage.py:

Add class FileStorageRecoveryTest, which adds two simple tests for
copyTransactionsFrom().  This indirectly tests the new restore()
method.



=== StandaloneZODB/ZODB/tests/IteratorStorage.py 1.9 => 1.10 ===
         self.iter_verify(txniter, [revid1, revid2, revid3], 11)
 
+    def checkClose(self):
+        self._oid = oid = self._storage.new_oid()
+        revid1 = self._dostore(oid, data=MinPO(11))
+        txniter = self._storage.iterator()
+        txniter.close()
+        self.assertRaises(IOError, txniter.__getitem__, 0)
+
     def checkVersionIterator(self):
         if not self._storage.supportsVersions():
             return
@@ -60,53 +67,41 @@
         self._storage.tpc_vote(t)
         self._storage.tpc_finish(t)
 
-        # XXX extend these checks.  right now, just iterating with CVS
-        # FS or Berkeley will fail here, but once fixed we should
-        # check that the right data is returned.
         txniter = self._storage.iterator()
         for trans in txniter:
             for data in trans:
                 pass
 
-    def checkTransactionalUndoIterator(self):
+    def checkUndoZombieNonVersion(self):
         if not hasattr(self._storage, 'supportsTransactionalUndo'):
             return
         if not self._storage.supportsTransactionalUndo():
             return
 
         oid = self._storage.new_oid()
-        revid = self._dostore(oid, data=MinPO(23))
-        revid = self._dostore(oid, revid=revid, data=MinPO(24))
-        revid = self._dostore(oid, revid=revid, data=MinPO(25))
-
-        self.undoTrans(0)
-        self.undoTrans(2)
-        self.undoTrans(4)
-
-        # XXX extend these checks.  right now, just iterating with CVS
-        # FS or Berkeley will fail here, but once fixed we should
-        # check that the right data is returned.
-        txniter = self._storage.iterator()
-        for trans in txniter:
-            for data in trans:
-                pass
-
-        # The last transaction performed an undo of the transaction
-        # that created object oid.  (As Barry points out, the object
-        # is now in the George Bailey state.)  Assert that the final
-        # data record contains None in the data attribute.
-        self.assertEqual(data.oid, oid)
-        self.assertEqual(data.data, None)
-
-    def undoTrans(self, i):
+        revid = self._dostore(oid, data=MinPO(94))
+        # Get the undo information
         info = self._storage.undoInfo()
-        tid = info[i]['id']
+        tid = info[0]['id']
+        # Undo the creation of the object, rendering it a zombie
         t = Transaction()
         self._storage.tpc_begin(t)
         oids = self._storage.transactionalUndo(tid, t)
         self._storage.tpc_vote(t)
         self._storage.tpc_finish(t)
-        
+        # Now attempt to iterator over the storage
+        iter = self._storage.iterator()
+        for txn in iter:
+            for rec in txn:
+                pass
+
+        # The last transaction performed an undo of the transaction that
+        # created object oid.  (As Barry points out, the object is now in the
+        # George Bailey state.)  Assert that the final data record contains
+        # None in the data attribute.
+        self.assertEqual(rec.oid, oid)
+        self.assertEqual(rec.data, None)
+
 
 class ExtendedIteratorStorage(IteratorCompare):
 
@@ -145,3 +140,27 @@
         txniter = self._storage.iterator(revid3, revid3)
         self.iter_verify(txniter, [revid3], 13)
         
+class IteratorDeepCompare:
+    def compare(self, storage1, storage2):
+        eq = self.assertEqual
+        iter1 = storage1.iterator()
+        iter2 = storage2.iterator()
+        for txn1, txn2 in zip(iter1, iter2):
+            eq(txn1.tid,         txn2.tid)
+            eq(txn1.status,      txn2.status)
+            eq(txn1.user,        txn2.user)
+            eq(txn1.description, txn2.description)
+            eq(txn1._extension,  txn2._extension)
+            for rec1, rec2 in zip(txn1, txn2):
+                eq(rec1.oid,     rec2.oid)
+                eq(rec1.serial,  rec2.serial)
+                eq(rec1.version, rec2.version)
+                eq(rec1.data,    rec2.data)
+            # Make sure there are no more records left in rec1 and rec2,
+            # meaning they were the same length.
+            self.assertRaises(IndexError, txn1.next)
+            self.assertRaises(IndexError, txn2.next)
+        # Make sure ther are no more records left in txn1 and txn2, meaning
+        # they were the same length
+        self.assertRaises(IndexError, iter1.next)
+        self.assertRaises(IndexError, iter2.next)


=== StandaloneZODB/ZODB/tests/testFileStorage.py 1.14 => 1.15 ===
+
 import ZODB.FileStorage
 import sys, os, unittest
+import errno
+from ZODB.Transaction import Transaction
 
 from ZODB.tests import StorageTestBase, BasicStorage, \
      TransactionalUndoStorage, VersionStorage, \
@@ -45,10 +49,83 @@
             if os.path.exists(path):
                 os.remove(path)
 
+class FileStorageRecoveryTest(
+    StorageTestBase.StorageTestBase,
+    IteratorStorage.IteratorDeepCompare,
+    ):
+
+    def setUp(self):
+        StorageTestBase.StorageTestBase.setUp(self)
+        self._storage = ZODB.FileStorage.FileStorage('Source.fs')
+        self._dst = ZODB.FileStorage.FileStorage('Dest.fs')
+
+    def tearDown(self):
+        StorageTestBase.StorageTestBase.tearDown(self)
+        self._dst.close()
+        for ext in '', '.old', '.tmp', '.lock', '.index':
+            for fs in 'Source', 'Dest':
+                path = fs + '.fs' + ext
+                try:
+                    os.remove(path)
+                except OSError, e:
+                    if e.errno <> errno.ENOENT: raise
+
+    def checkSimpleRecovery(self):
+        oid = self._storage.new_oid()
+        revid = self._dostore(oid, data=11)
+        revid = self._dostore(oid, revid=revid, data=12)
+        revid = self._dostore(oid, revid=revid, data=13)
+        self._dst.copyTransactionsFrom(self._storage)
+        self.compare(self._storage, self._dst)
+
+    def checkRecoveryAcrossVersions(self):
+        oid = self._storage.new_oid()
+        revid = self._dostore(oid, data=21)
+        revid = self._dostore(oid, revid=revid, data=22)
+        revid = self._dostore(oid, revid=revid, data=23, version='one')
+        revid = self._dostore(oid, revid=revid, data=34, version='one')
+        # Now commit the version
+        t = Transaction()
+        self._storage.tpc_begin(t)
+        self._storage.commitVersion('one', '', t)
+        self._storage.tpc_vote(t)
+        self._storage.tpc_finish(t)
+        self._dst.copyTransactionsFrom(self._storage)
+        self.compare(self._storage, self._dst)
+
+    def checkRecoverAbortVersion(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])
+        self._dst.copyTransactionsFrom(self._storage)
+        self.compare(self._storage, self._dst)
+        # Also make sure the the last transaction has a data record
+        # with None for its data attribute, because we've undone the
+        # object.
+        for s in self._storage, self._dst:
+            iter = s.iterator()
+            for trans in iter:
+                pass # iterate until we get the last one
+            data = trans[0]
+            self.assertRaises(IndexError, lambda i:trans[i], 1)
+            self.assertEqual(data.oid, oid)
+            self.assertEqual(data.data, None)
+                
+
 def test_suite():
     suite = unittest.makeSuite(FileStorageTests, 'check')
     suite2 = unittest.makeSuite(Corruption.FileStorageCorruptTests, 'check')
+    suite3 = unittest.makeSuite(FileStorageRecoveryTest, 'check')
     suite.addTest(suite2)
+    suite.addTest(suite3)
     return suite
 
 def main():