[Zope3-checkins] CVS: Zope3/src/transaction/tests -
test_SampleDataManager.py:1.2 test_util.py:1.2
__init__.py:1.3 abstestIDataManager.py:1.4 test_txn.py:NONE
Fred L. Drake, Jr.
fred at zope.com
Fri Feb 20 11:57:59 EST 2004
Update of /cvs-repository/Zope3/src/transaction/tests
In directory cvs.zope.org:/tmp/cvs-serv22507/src/transaction/tests
Modified Files:
__init__.py abstestIDataManager.py
Added Files:
test_SampleDataManager.py test_util.py
Removed Files:
test_txn.py
Log Message:
update to replace ZODB 4 with ZODB 3
=== Zope3/src/transaction/tests/test_SampleDataManager.py 1.1 => 1.2 ===
--- /dev/null Fri Feb 20 11:57:58 2004
+++ Zope3/src/transaction/tests/test_SampleDataManager.py Fri Feb 20 11:56:57 2004
@@ -0,0 +1,412 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+"""Sample objects for use in tests
+
+$Id$
+"""
+
+class DataManager(object):
+ """Sample data manager
+
+ This class provides a trivial data-manager implementation and doc
+ strings to illustrate the the protocol and to provide a tool for
+ writing tests.
+
+ Our sample data manager has state that is updated through an inc
+ method and through transaction operations.
+
+ When we create a sample data manager:
+
+ >>> dm = DataManager()
+
+ It has two bits of state, state:
+
+ >>> dm.state
+ 0
+
+ and delta:
+
+ >>> dm.delta
+ 0
+
+ Both of which are initialized to 0. state is meant to model
+ committed state, while delta represents tentative changes within a
+ transaction. We change the state by calling inc:
+
+ >>> dm.inc()
+
+ which updates delta:
+
+ >>> dm.delta
+ 1
+
+ but state isn't changed until we commit the transaction:
+
+ >>> dm.state
+ 0
+
+ To commit the changes, we use 2-phase commit. We execute the first
+ stage by calling prepare. We need to pass a transation. Our
+ sample data managers don't really use the transactions for much,
+ so we'll be lazy and use strings for transactions:
+
+ >>> t1 = '1'
+ >>> dm.prepare(t1)
+
+ The sample data manager updates the state when we call prepare:
+
+ >>> dm.state
+ 1
+ >>> dm.delta
+ 1
+
+ This is mainly so we can detect some affect of calling the methods.
+
+ Now if we call commit:
+
+ >>> dm.commit(t1)
+
+ Our changes are"permanent". The state reflects the changes and the
+ delta has been reset to 0.
+
+ >>> dm.state
+ 1
+ >>> dm.delta
+ 0
+ """
+
+ def __init__(self):
+ self.state = 0
+ self.sp = 0
+ self.transaction = None
+ self.delta = 0
+ self.prepared = False
+
+ def inc(self, n=1):
+ self.delta += n
+
+ def prepare(self, transaction):
+ """Prepare to commit data
+
+ >>> dm = DataManager()
+ >>> dm.inc()
+ >>> t1 = '1'
+ >>> dm.prepare(t1)
+ >>> dm.commit(t1)
+ >>> dm.state
+ 1
+ >>> dm.inc()
+ >>> t2 = '2'
+ >>> dm.prepare(t2)
+ >>> dm.abort(t2)
+ >>> dm.state
+ 1
+
+ It is en error to call prepare more than once without an intervening
+ commit or abort:
+
+ >>> dm.prepare(t1)
+
+ >>> dm.prepare(t1)
+ Traceback (most recent call last):
+ ...
+ TypeError: Already prepared
+
+ >>> dm.prepare(t2)
+ Traceback (most recent call last):
+ ...
+ TypeError: Already prepared
+
+ >>> dm.abort(t1)
+
+ If there was a preceeding savepoint, the transaction must match:
+
+ >>> rollback = dm.savepoint(t1)
+ >>> dm.prepare(t2)
+ Traceback (most recent call last):
+ ,,,
+ TypeError: ('Transaction missmatch', '2', '1')
+
+ >>> dm.prepare(t1)
+
+ """
+ if self.prepared:
+ raise TypeError('Already prepared')
+ self._checkTransaction(transaction)
+ self.prepared = True
+ self.transaction = transaction
+ self.state += self.delta
+
+ def _checkTransaction(self, transaction):
+ if (transaction is not self.transaction
+ and self.transaction is not None):
+ raise TypeError("Transaction missmatch",
+ transaction, self.transaction)
+
+ def abort(self, transaction):
+ """Abort a transaction
+
+ The abort method can be called before two-phase commit to
+ throw away work done in the transaction:
+
+ >>> dm = DataManager()
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 1)
+ >>> t1 = '1'
+ >>> dm.abort(t1)
+ >>> dm.state, dm.delta
+ (0, 0)
+
+ The abort method also throws away work done in savepoints:
+
+ >>> dm.inc()
+ >>> r = dm.savepoint(t1)
+ >>> dm.inc()
+ >>> r = dm.savepoint(t1)
+ >>> dm.state, dm.delta
+ (0, 2)
+ >>> dm.abort(t1)
+ >>> dm.state, dm.delta
+ (0, 0)
+
+ If savepoints are used, abort must be passed the same
+ transaction:
+
+ >>> dm.inc()
+ >>> r = dm.savepoint(t1)
+ >>> t2 = '2'
+ >>> dm.abort(t2)
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Transaction missmatch', '2', '1')
+
+ >>> dm.abort(t1)
+
+ The abort method is also used to abort a two-phase commit:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 1)
+ >>> dm.prepare(t1)
+ >>> dm.state, dm.delta
+ (1, 1)
+ >>> dm.abort(t1)
+ >>> dm.state, dm.delta
+ (0, 0)
+
+ Of course, the transactions passed to prepare and abort must
+ match:
+
+ >>> dm.prepare(t1)
+ >>> dm.abort(t2)
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Transaction missmatch', '2', '1')
+
+ >>> dm.abort(t1)
+
+
+ """
+ self._checkTransaction(transaction)
+ if self.transaction is not None:
+ self.transaction = None
+
+ if self.prepared:
+ self.state -= self.delta
+ self.prepared = False
+
+ self.delta = 0
+
+ def commit(self, transaction):
+ """Complete two-phase commit
+
+ >>> dm = DataManager()
+ >>> dm.state
+ 0
+ >>> dm.inc()
+
+ We start two-phase commit by calling prepare:
+
+ >>> t1 = '1'
+ >>> dm.prepare(t1)
+
+ We complete it by calling commit:
+
+ >>> dm.commit(t1)
+ >>> dm.state
+ 1
+
+ It is an error ro call commit without calling prepare first:
+
+ >>> dm.inc()
+ >>> t2 = '2'
+ >>> dm.commit(t2)
+ Traceback (most recent call last):
+ ...
+ TypeError: Not prepared to commit
+
+ >>> dm.prepare(t2)
+ >>> dm.commit(t2)
+
+ If course, the transactions given to prepare and commit must
+ be the same:
+
+ >>> dm.inc()
+ >>> t3 = '3'
+ >>> dm.prepare(t3)
+ >>> dm.commit(t2)
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Transaction missmatch', '2', '3')
+
+ """
+ if not self.prepared:
+ raise TypeError('Not prepared to commit')
+ self._checkTransaction(transaction)
+ self.delta = 0
+ self.transaction = None
+ self.prepared = False
+
+ def savepoint(self, transaction):
+ """Provide the ability to rollback transaction state
+
+ Savepoints provide a way to:
+
+ - Save partial transaction work. For some data managers, this
+ could allow resources to be used more efficiently.
+
+ - Provide the ability to revert state to a point in a
+ transaction without aborting the entire transaction. In
+ other words, savepoints support partial aborts.
+
+ Savepoints don't use two-phase commit. If there are errors in
+ setting or rolling back to savepoints, the application should
+ abort the containing transaction. This is *not* the
+ responsibility of the data manager.
+
+ Savepoints are always associated with a transaction. Any work
+ done in a savepoint's transaction is tentative until the
+ transaction is committed using two-phase commit.
+
+ >>> dm = DataManager()
+ >>> dm.inc()
+ >>> t1 = '1'
+ >>> r = dm.savepoint(t1)
+ >>> dm.state, dm.delta
+ (0, 1)
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 2)
+ >>> r.rollback()
+ >>> dm.state, dm.delta
+ (0, 1)
+ >>> dm.prepare(t1)
+ >>> dm.commit(t1)
+ >>> dm.state, dm.delta
+ (1, 0)
+
+ Savepoints must have the same transaction:
+
+ >>> r1 = dm.savepoint(t1)
+ >>> dm.state, dm.delta
+ (1, 0)
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (1, 1)
+ >>> t2 = '2'
+ >>> r2 = dm.savepoint(t2)
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Transaction missmatch', '2', '1')
+
+ >>> r2 = dm.savepoint(t1)
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (1, 2)
+
+ If we rollback to an earlier savepoint, we discard all work
+ done later:
+
+ >>> r1.rollback()
+ >>> dm.state, dm.delta
+ (1, 0)
+
+ and we can no longer rollback to the later savepoint:
+
+ >>> r2.rollback()
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Attempt to roll back to invalid save point', 3, 2)
+
+ We can roll back to a savepoint as often as we like:
+
+ >>> r1.rollback()
+ >>> r1.rollback()
+ >>> r1.rollback()
+ >>> dm.state, dm.delta
+ (1, 0)
+
+ >>> dm.inc()
+ >>> dm.inc()
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (1, 3)
+ >>> r1.rollback()
+ >>> dm.state, dm.delta
+ (1, 0)
+
+ But we can't rollback to a savepoint after it has been
+ committed:
+
+ >>> dm.prepare(t1)
+ >>> dm.commit(t1)
+
+ >>> r1.rollback()
+ Traceback (most recent call last):
+ ...
+ TypeError: Attempt to rollback stale rollback
+
+ """
+ if self.prepared:
+ raise TypeError("Can't get savepoint during two-phase commit")
+ self._checkTransaction(transaction)
+ self.transaction = transaction
+ self.sp += 1
+ return Rollback(self)
+
+class Rollback(object):
+
+ def __init__(self, dm):
+ self.dm = dm
+ self.sp = dm.sp
+ self.delta = dm.delta
+ self.transaction = dm.transaction
+
+ def rollback(self):
+ if self.transaction is not self.dm.transaction:
+ raise TypeError("Attempt to rollback stale rollback")
+ if self.dm.sp < self.sp:
+ raise TypeError("Attempt to roll back to invalid save point",
+ self.sp, self.dm.sp)
+ self.dm.sp = self.sp
+ self.dm.delta = self.delta
+
+
+def test_suite():
+ from doctest import DocTestSuite
+ return DocTestSuite()
+
+if __name__ == '__main__':
+ unittest.main()
=== Zope3/src/transaction/tests/test_util.py 1.1 => 1.2 ===
--- /dev/null Fri Feb 20 11:57:58 2004
+++ Zope3/src/transaction/tests/test_util.py Fri Feb 20 11:56:58 2004
@@ -0,0 +1,25 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+"""Test transaction utilities
+
+$Id$
+"""
+import unittest
+from doctest import DocTestSuite
+
+def test_suite():
+ return DocTestSuite('transaction.util')
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
=== Zope3/src/transaction/tests/__init__.py 1.2 => 1.3 ===
--- Zope3/src/transaction/tests/__init__.py:1.2 Wed Dec 25 09:12:15 2002
+++ Zope3/src/transaction/tests/__init__.py Fri Feb 20 11:56:57 2004
@@ -1,14 +1 @@
-##############################################################################
#
-# 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.
-#
-##############################################################################
-# Make this directory a package
=== Zope3/src/transaction/tests/abstestIDataManager.py 1.3 => 1.4 ===
--- Zope3/src/transaction/tests/abstestIDataManager.py:1.3 Thu May 1 15:34:57 2003
+++ Zope3/src/transaction/tests/abstestIDataManager.py Fri Feb 20 11:56:57 2004
@@ -31,6 +31,7 @@
"""
from unittest import TestCase
+from transaction.interfaces import IRollback
class IDataManagerTests(TestCase, object):
@@ -59,5 +60,4 @@
def testRollback(self):
tran = self.get_transaction()
rb = self.datamgr.savepoint(tran)
- if rb is not None:
- rb.rollback()
+ self.assert_(IRollback.isImplementedBy(rb))
=== Removed File Zope3/src/transaction/tests/test_txn.py ===
More information about the Zope3-Checkins
mailing list