[Zodb-checkins] CVS: ZODB3/ZODB/tests - testTransaction.py:1.9

Jeremy Hylton jeremy@zope.com
Wed, 14 Aug 2002 11:37:08 -0400


Update of /cvs-repository/ZODB3/ZODB/tests
In directory cvs.zope.org:/tmp/cvs-serv6497/tests

Modified Files:
	testTransaction.py 
Log Message:
Fix testHoserStoppage(),
and remove testExceptionInTpcFinish() and BUGtestExceptionInSubTpcBegin().

The tpc_finish test removed duplicated the logic of "hoser stoppage,"
while the BUGtest was never executed.

The testHoserStoppage() test used to depend on the order of objects in
a dict's values() list.  This order was unpredictable and caused
intermittent failures.  Make two changes to work around this:  1) Mark
the non-error object as modified first, since this seems to get
values() in a good order.  2) Retry the test with 2 randomly selected
objects until it passes, but fail after 10 tries.



=== ZODB3/ZODB/tests/testTransaction.py 1.8 => 1.9 ===
--- ZODB3/ZODB/tests/testTransaction.py:1.8	Mon Aug 12 16:00:49 2002
+++ ZODB3/ZODB/tests/testTransaction.py	Wed Aug 14 11:37:08 2002
@@ -44,6 +44,7 @@
     
 """
 
+import random
 from types import TupleType
 import unittest
 
@@ -358,24 +359,6 @@
         assert self.sub1._p_jar.ctpc_abort == 1
         assert Transaction.hosed == 0
 
-    def testExceptionInTpcFinish(self):
-
-        for sub in self.sub1, self.sub2:
-            sub._p_jar = SubTransactionJar(errors='tpc_finish')
-            sub.modify(nojar=1)
-        
-        self.nosub1.modify()
-
-        try: 
-            get_transaction().commit()
-        except TestTxnException: pass
-        except Transaction.POSException.TransactionError: pass
-        
-        assert Transaction.hosed == 1
-
-        # reset the transaction hosed flag        
-        Transaction.hosed = 0
-        
     def testExceptionInTpcBegin(self):
         """
         ok this test reveals a bug in the TM.py
@@ -494,67 +477,55 @@
         else:
             self.assertEqual(self.sub3._p_jar.cabort_sub, 1)
 
-    ### XXX
-    def BUGtestExceptionInSubTpcBegin(self):
-
-        """
-        bug, we short circuit on notifying objects in
-        previous subtransactions of the transaction outcome
-
-        untested but this probably also applies to error
-        in tpc_finish, as the error has to do with
-        not checking the implicit sub transaction commit
-        done when closing the outer transaction.
-        
-        trace:
-        
-        nosub1 calling method tpc_begin
-        sub2 calling method tpc_begin
-        sub2 calling method commit
-        nosub1 calling method tpc_finish
-        sub2 calling method tpc_finish
-        sub3 calling method tpc_begin
-        sub3 calling method commit
-        sub1 calling method tpc_begin
-        sub1 calling method abort
-        sub1 calling method tpc_abort
-        sub3 calling method tpc_abort
-        """
-        
-        self.nosub1.modify(tracing='nosub1')
-
-        self.sub2._p_jar = SubTransactionJar(tracing='sub2')
-        
-        self.sub2.modify(nojar=1)
-        
-        get_transaction().commit(1)
-
-        self.sub3.modify(tracing='sub3')
-        
-        self.sub1._p_jar = SubTransactionJar(tracing='sub1',
-                                                errors='tpc_begin')
-        self.sub1.modify(nojar=1)        
-
-        try:
-            get_transaction().commit()
-        except TestTxnException: pass
-
-    ## done with exception permutations
-
     # last test, check the hosing mechanism
 
     def testHoserStoppage(self):
-        # must have errors in at least two jars to guarantee a failure
-        for sub in self.sub1, self.sub2:
-            sub._p_jar = SubTransactionJar(errors='tpc_finish')
-            sub.modify(nojar=1)
-        self.nosub1.modify()
-
-        try:
-            get_transaction().commit()
-        except TestTxnException: pass
-
-        assert Transaction.hosed == 1
+        # It's hard to test the "hosed" state of the database, where
+        # hosed means that a failure occurred in the second phase of
+        # the two phase commit.  It's hard because the database can
+        # recover from such an error if it occurs during the very first
+        # tpc_finish() call of the second phase.
+
+        objects = self.sub1, self.sub2, self.sub3, self.nosub1
+
+        for i in range(10):
+            L = list(objects)
+            random.shuffle(L)
+            obj = L.pop()
+            obj.modify()
+            obj2 = L.pop()
+            obj2._p_jar = SubTransactionJar(errors='tpc_finish')
+            obj2.modify(nojar=1)
+
+            try:
+                get_transaction().commit()
+            except TestTxnException:
+                pass
+
+            # figure out all the jars that may be involved
+            jars = {}
+            for obj in objects:
+                jar = obj._p_jar
+                if jar is not None:
+                    jars[jar] = 1
+            jars = jars.keys()
+            succeed = 0
+            fail = 0
+            for jar in jars:
+                if jar.ctpc_finish:
+                    succeed += 1
+                elif jar.ctpc_abort:
+                    fail += 1
+            
+            if Transaction.hosed:
+                self.assert_(fail > 0 and succeed > 0)
+                break
+            else:
+                self.assert_(fail and not succeed),
+                get_transaction().abort()
+                self.setUp()
+        else:
+            self.fail("Couldn't provoke hosed state.")
         
         self.sub2.modify()