[Zodb-checkins] CVS: Zope/lib/python/ZODB/tests - sampledm.py:1.2
testConnection.py:1.2 testSerialize.py:1.2
test_datamanageradapter.py:1.2 util.py:1.2
ConflictResolution.py:1.14 StorageTestBase.py:1.33
testCache.py:1.18 testTransaction.py:1.18 testUtils.py:1.6
Jeremy Hylton
jeremy at zope.com
Wed Feb 18 21:59:11 EST 2004
Update of /cvs-repository/Zope/lib/python/ZODB/tests
In directory cvs.zope.org:/tmp/cvs-serv11908/lib/python/ZODB/tests
Modified Files:
ConflictResolution.py StorageTestBase.py testCache.py
testTransaction.py testUtils.py
Added Files:
sampledm.py testConnection.py testSerialize.py
test_datamanageradapter.py util.py
Log Message:
Merge zope3-zodb3-devel-branch to the Zope head (Zope 2 head).
Add support for persistent weak references. The implementation is in
pure Python right now; coptimizations.c was disabled. We need to
restore the C code before going to beta.
The persistent reference format has evolved a little, but the code on
the branch doesn't document it. The ref is usually a an oid-class
pair, where the class is an actual class object. It can also be a
list, for weak references, or a bare oid.
Add support for data managers written against the ZODB4 transaction
API. The transaction implementation provides an adapter between the
two interfaces. There's a substantial doctest test suite for this
feature.
Add add() method to Connection to explicitly add an object and get it
an assigned _p_oid. It's possible to add an unconnected object; this
isn't recommended, because it will be lost at pack time.
=== Zope/lib/python/ZODB/tests/sampledm.py 1.1 => 1.2 ===
--- /dev/null Wed Feb 18 21:59:11 2004
+++ Zope/lib/python/ZODB/tests/sampledm.py Wed Feb 18 21:59:10 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()
=== Zope/lib/python/ZODB/tests/testConnection.py 1.1 => 1.2 ===
--- /dev/null Wed Feb 18 21:59:11 2004
+++ Zope/lib/python/ZODB/tests/testConnection.py Wed Feb 18 21:59:10 2004
@@ -0,0 +1,269 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Unit tests for the Connection class.
+"""
+
+import unittest
+from persistent import Persistent
+
+__metaclass__ = type
+
+class ConnectionDotAdd(unittest.TestCase):
+
+ def setUp(self):
+ from ZODB.Connection import Connection
+ self.datamgr = Connection()
+ self.db = StubDatabase()
+ self.datamgr._setDB(self.db)
+ self.transaction = StubTransaction()
+
+ def check_add(self):
+ from ZODB.POSException import InvalidObjectReference
+ obj = StubObject()
+ self.assert_(obj._p_oid is None)
+ self.assert_(obj._p_jar is None)
+ self.datamgr.add(obj)
+ self.assert_(obj._p_oid is not None)
+ self.assert_(obj._p_jar is self.datamgr)
+ self.assert_(self.datamgr[obj._p_oid] is obj)
+
+ # Only first-class persistent objects may be added.
+ self.assertRaises(TypeError, self.datamgr.add, object())
+
+ # Adding to the same connection does not fail. Object keeps the
+ # same oid.
+ oid = obj._p_oid
+ self.datamgr.add(obj)
+ self.assertEqual(obj._p_oid, oid)
+
+ # Cannot add an object from a diffrerent connection.
+ obj2 = StubObject()
+ obj2._p_jar = object()
+ self.assertRaises(InvalidObjectReference, self.datamgr.add, obj2)
+
+ def checkResetOnAbort(self):
+ # Check that _p_oid and _p_jar are reset when a transaction is
+ # aborted.
+ obj = StubObject()
+ self.datamgr.add(obj)
+ oid = obj._p_oid
+ self.datamgr.abort(obj, self.transaction)
+ self.assert_(obj._p_oid is None)
+ self.assert_(obj._p_jar is None)
+ self.assertRaises(KeyError, self.datamgr.__getitem__, oid)
+
+ def checkResetOnTpcAbort(self):
+ obj = StubObject()
+ self.datamgr.add(obj)
+ oid = obj._p_oid
+
+ # This case simulates when an error occurred committing some other
+ # object, so tpc_abort is called, clearing everything.
+ self.datamgr.tpc_begin(self.transaction)
+ # Let's pretend something bad happens here.
+ self.datamgr.tpc_abort(self.transaction)
+ self.assert_(obj._p_oid is None)
+ self.assert_(obj._p_jar is None)
+ self.assertRaises(KeyError, self.datamgr.__getitem__, oid)
+
+ def checkTcpAbortAfterCommit(self):
+ obj = StubObject()
+ self.datamgr.add(obj)
+ oid = obj._p_oid
+
+ self.datamgr.tpc_begin(self.transaction)
+ self.datamgr.commit(obj, self.transaction)
+ # Let's pretend something bad happened here.
+ self.datamgr.tpc_abort(self.transaction)
+ self.assert_(obj._p_oid is None)
+ self.assert_(obj._p_jar is None)
+ self.assertRaises(KeyError, self.datamgr.__getitem__, oid)
+ self.assertEquals(self.db._storage._stored, [oid])
+
+ def checkCommit(self):
+ obj = StubObject()
+ self.datamgr.add(obj)
+ oid = obj._p_oid
+
+ self.datamgr.tpc_begin(self.transaction)
+ self.datamgr.commit(obj, self.transaction)
+ self.datamgr.tpc_finish(self.transaction)
+ self.assert_(obj._p_oid is oid)
+ self.assert_(obj._p_jar is self.datamgr)
+
+ # This next assert_ is covered by an assert in tpc_finish.
+ ##self.assert_(not self.datamgr._added)
+
+ self.assertEquals(self.db._storage._stored, [oid])
+ self.assertEquals(self.db._storage._finished, [oid])
+
+ def checkModifyOnGetstate(self):
+ subobj = StubObject()
+ obj = ModifyOnGetStateObject(subobj)
+
+ self.datamgr.tpc_begin(self.transaction)
+ self.datamgr.commit(obj, self.transaction)
+ self.datamgr.tpc_finish(self.transaction)
+ storage = self.db._storage
+ self.assert_(obj._p_oid in storage._stored, "object was not stored")
+ self.assert_(subobj._p_oid in storage._stored,
+ "subobject was not stored")
+ self.assert_(self.datamgr._added_during_commit is None)
+
+ def checkErrorDuringCommit(self):
+ # We need to check that _added_during_commit still gets set to None
+ # when there is an error during commit()/
+ obj = ErrorOnGetstateObject()
+
+ self.datamgr.tpc_begin(self.transaction)
+ self.assertRaises(ErrorOnGetstateException,
+ self.datamgr.commit, obj, self.transaction)
+ self.assert_(self.datamgr._added_during_commit is None)
+
+ def checkUnusedAddWorks(self):
+ # When an object is added, but not committed, it shouldn't be stored,
+ # but also it should be an error.
+ obj = StubObject()
+ self.datamgr.add(obj)
+ self.datamgr.tpc_begin(self.transaction)
+ self.datamgr.tpc_finish(self.transaction)
+ self.assert_(obj._p_oid not in self.datamgr._storage._stored)
+
+# ---- stubs
+
+class StubObject(Persistent):
+ pass
+
+
+class StubTransaction:
+ pass
+
+class ErrorOnGetstateException(Exception):
+ pass
+
+class ErrorOnGetstateObject(Persistent):
+
+ def __getstate__(self):
+ raise ErrorOnGetstateException
+
+class ModifyOnGetStateObject(Persistent):
+
+ def __init__(self, p):
+ self._v_p = p
+
+ def __getstate__(self):
+ self._p_jar.add(self._v_p)
+ self.p = self._v_p
+ return Persistent.__getstate__(self)
+
+
+class StubStorage:
+ """Very simple in-memory storage that does *just* enough to support tests.
+
+ Only one concurrent transaction is supported.
+ Voting is not supported.
+ Versions are not supported.
+
+ Inspect self._stored and self._finished to see how the storage has been
+ used during a unit test. Whenever an object is stored in the store()
+ method, its oid is appended to self._stored. When a transaction is
+ finished, the oids that have been stored during the transaction are
+ appended to self._finished.
+ """
+
+ sortKey = 'StubStorage sortKey'
+
+ # internal
+ _oid = 1
+ _transaction = None
+
+ def __init__(self):
+ # internal
+ self._stored = []
+ self._finished = []
+ self._data = {}
+ self._transdata = {}
+ self._transstored = []
+
+ def new_oid(self):
+ oid = str(self._oid)
+ self._oid += 1
+ return oid
+
+ def tpc_begin(self, transaction):
+ if transaction is None:
+ raise TypeError('transaction may not be None')
+ elif self._transaction is None:
+ self._transaction = transaction
+ elif self._transaction != transaction:
+ raise RuntimeError(
+ 'StubStorage uses only one transaction at a time')
+
+ def tpc_abort(self, transaction):
+ if transaction is None:
+ raise TypeError('transaction may not be None')
+ elif self._transaction != transaction:
+ raise RuntimeError(
+ 'StubStorage uses only one transaction at a time')
+ del self._transaction
+ self._transdata.clear()
+
+ def tpc_finish(self, transaction, callback):
+ if transaction is None:
+ raise TypeError('transaction may not be None')
+ elif self._transaction != transaction:
+ raise RuntimeError(
+ 'StubStorage uses only one transaction at a time')
+ self._finished.extend(self._transstored)
+ self._data.update(self._transdata)
+ callback(transaction)
+ del self._transaction
+ self._transdata.clear()
+ self._transstored = []
+
+ def load(self, oid, version):
+ if version != '':
+ raise TypeError('StubStorage does not support versions.')
+ return self._data[oid]
+
+ def store(self, oid, serial, p, version, transaction):
+ if version != '':
+ raise TypeError('StubStorage does not support versions.')
+ if transaction is None:
+ raise TypeError('transaction may not be None')
+ elif self._transaction != transaction:
+ raise RuntimeError(
+ 'StubStorage uses only one transaction at a time')
+ self._stored.append(oid)
+ self._transstored.append(oid)
+ self._transdata[oid] = (p, serial)
+ # Explicitly returing None, as we're not pretending to be a ZEO
+ # storage
+ return None
+
+
+class StubDatabase:
+
+ def __init__(self):
+ self._storage = StubStorage()
+
+ _classFactory = None
+
+ def invalidate(self, transaction, dict_with_oid_keys, connection):
+ pass
+
+
+def test_suite():
+ s = unittest.makeSuite(ConnectionDotAdd, 'check')
+ return s
=== Zope/lib/python/ZODB/tests/testSerialize.py 1.1 => 1.2 ===
--- /dev/null Wed Feb 18 21:59:11 2004
+++ Zope/lib/python/ZODB/tests/testSerialize.py Wed Feb 18 21:59:10 2004
@@ -0,0 +1,121 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Tests of the serializer."""
+
+import cPickle
+import cStringIO as StringIO
+import sys
+import unittest
+
+from ZODB import serialize
+
+
+class ClassWithNewargs(int):
+ def __new__(cls, value):
+ return int.__new__(cls, value)
+
+ def __getnewargs__(self):
+ return int(self),
+
+class ClassWithoutNewargs(object):
+ def __init__(self, value):
+ self.value = value
+
+def make_pickle(ob):
+ sio = StringIO.StringIO()
+ p = cPickle.Pickler(sio, 1)
+ p.dump(ob)
+ return sio.getvalue()
+
+
+class SerializerTestCase(unittest.TestCase):
+
+ # old format: (module, name), None
+ old_style_without_newargs = make_pickle(
+ ((__name__, "ClassWithoutNewargs"), None))
+
+ # old format: (module, name), argtuple
+ old_style_with_newargs = make_pickle(
+ ((__name__, "ClassWithNewargs"), (1,)))
+
+ # new format: klass
+ new_style_without_newargs = make_pickle(
+ ClassWithoutNewargs)
+
+ # new format: klass, argtuple
+ new_style_with_newargs = make_pickle(
+ (ClassWithNewargs, (1,)))
+
+ def test_getClassName(self):
+ r = serialize.BaseObjectReader()
+ eq = self.assertEqual
+ eq(r.getClassName(self.old_style_with_newargs),
+ __name__ + ".ClassWithNewargs")
+ eq(r.getClassName(self.new_style_with_newargs),
+ __name__ + ".ClassWithNewargs")
+ eq(r.getClassName(self.old_style_without_newargs),
+ __name__ + ".ClassWithoutNewargs")
+ eq(r.getClassName(self.new_style_without_newargs),
+ __name__ + ".ClassWithoutNewargs")
+
+ def test_getGhost(self):
+ # Use a TestObjectReader since we need _get_class() to be
+ # implemented; otherwise this is just a BaseObjectReader.
+
+ class TestObjectReader(serialize.BaseObjectReader):
+ # A production object reader would optimize this, but we
+ # don't need to in a test
+ def _get_class(self, module, name):
+ __import__(module)
+ return getattr(sys.modules[module], name)
+
+ r = TestObjectReader()
+ g = r.getGhost(self.old_style_with_newargs)
+ self.assert_(isinstance(g, ClassWithNewargs))
+ self.assertEqual(g, 1)
+ g = r.getGhost(self.old_style_without_newargs)
+ self.assert_(isinstance(g, ClassWithoutNewargs))
+ g = r.getGhost(self.new_style_with_newargs)
+ self.assert_(isinstance(g, ClassWithNewargs))
+ g = r.getGhost(self.new_style_without_newargs)
+ self.assert_(isinstance(g, ClassWithoutNewargs))
+
+ def test_myhasattr(self):
+
+ class OldStyle:
+ bar = "bar"
+ def __getattr__(self, name):
+ if name == "error":
+ raise ValueError("whee!")
+ else:
+ raise AttributeError, name
+
+ class NewStyle(object):
+ bar = "bar"
+ def _raise(self):
+ raise ValueError("whee!")
+ error = property(_raise)
+
+ self.assertRaises(ValueError,
+ serialize.myhasattr, OldStyle(), "error")
+ self.assertRaises(ValueError,
+ serialize.myhasattr, NewStyle(), "error")
+ self.assert_(serialize.myhasattr(OldStyle(), "bar"))
+ self.assert_(serialize.myhasattr(NewStyle(), "bar"))
+ self.assert_(not serialize.myhasattr(OldStyle(), "rat"))
+ self.assert_(not serialize.myhasattr(NewStyle(), "rat"))
+
+
+def test_suite():
+ return unittest.makeSuite(SerializerTestCase)
=== Zope/lib/python/ZODB/tests/test_datamanageradapter.py 1.1 => 1.2 ===
--- /dev/null Wed Feb 18 21:59:11 2004
+++ Zope/lib/python/ZODB/tests/test_datamanageradapter.py Wed Feb 18 21:59:10 2004
@@ -0,0 +1,810 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+"""XXX short summary goes here.
+
+$Id$
+"""
+import unittest
+from doctest import DocTestSuite
+from ZODB.Transaction import DataManagerAdapter
+from ZODB.tests.sampledm import DataManager
+
+def test_normal_commit():
+ """
+ So, we have a data manager:
+
+ >>> dm = DataManager()
+
+ and we do some work that modifies uncommited state:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ Now we'll commit the changes. When the data manager joins a transaction,
+ the transaction will create an adapter.
+
+ >>> dma = DataManagerAdapter(dm)
+
+ and register it as a modified object. At commit time, the
+ transaction will get the "jar" like this:
+
+ >>> jar = getattr(dma, '_p_jar', dma)
+
+ and, of course, the jar and the adapter will be the same:
+
+ >>> jar is dma
+ True
+
+ The transaction will call tpc_begin:
+
+ >>> t1 = '1'
+ >>> jar.tpc_begin(t1)
+
+ Then the transaction will call commit on the jar:
+
+ >>> jar.commit(dma, t1)
+
+ This doesn't actually do anything. :)
+
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ The transaction will then call tpc_vote:
+
+ >>> jar.tpc_vote(t1)
+
+ This prepares the data manager:
+
+ >>> dm.state, dm.delta
+ (1, 1)
+ >>> dm.prepared
+ True
+
+ Finally, tpc_finish is called:
+
+ >>> jar.tpc_finish(t1)
+
+ and the data manager finishes the two-phase commit:
+
+ >>> dm.state, dm.delta
+ (1, 0)
+ >>> dm.prepared
+ False
+ """
+
+def test_abort():
+ """
+ So, we have a data manager:
+
+ >>> dm = DataManager()
+
+ and we do some work that modifies uncommited state:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ When the data manager joins a transaction,
+ the transaction will create an adapter.
+
+ >>> dma = DataManagerAdapter(dm)
+
+ and register it as a modified object.
+
+ Now we'll abort the transaction. The transaction will get the
+ "jar" like this:
+
+ >>> jar = getattr(dma, '_p_jar', dma)
+
+ and, of course, the jar and the adapter will be the same:
+
+ >>> jar is dma
+ True
+
+ Then the transaction will call abort on the jar:
+
+ >>> t1 = '1'
+ >>> jar.abort(dma, t1)
+
+ Which aborts the changes in the data manager:
+
+ >>> dm.state, dm.delta
+ (0, 0)
+ """
+
+def test_tpc_abort_phase1():
+ """
+ So, we have a data manager:
+
+ >>> dm = DataManager()
+
+ and we do some work that modifies uncommited state:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ Now we'll commit the changes. When the data manager joins a transaction,
+ the transaction will create an adapter.
+
+ >>> dma = DataManagerAdapter(dm)
+
+ and register it as a modified object. At commit time, the
+ transaction will get the "jar" like this:
+
+ >>> jar = getattr(dma, '_p_jar', dma)
+
+ and, of course, the jar and the adapter will be the same:
+
+ >>> jar is dma
+ True
+
+ The transaction will call tpc_begin:
+
+ >>> t1 = '1'
+ >>> jar.tpc_begin(t1)
+
+ Then the transaction will call commit on the jar:
+
+ >>> jar.commit(dma, t1)
+
+ This doesn't actually do anything. :)
+
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ At this point, the transaction decides to abort. It calls tpc_abort:
+
+ >>> jar.tpc_abort(t1)
+
+ Which causes the state of the data manager to be restored:
+
+ >>> dm.state, dm.delta
+ (0, 0)
+ """
+
+def test_tpc_abort_phase2():
+ """
+ So, we have a data manager:
+
+ >>> dm = DataManager()
+
+ and we do some work that modifies uncommited state:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ Now we'll commit the changes. When the data manager joins a transaction,
+ the transaction will create an adapter.
+
+ >>> dma = DataManagerAdapter(dm)
+
+ and register it as a modified object. At commit time, the
+ transaction will get the "jar" like this:
+
+ >>> jar = getattr(dma, '_p_jar', dma)
+
+ and, of course, the jar and the adapter will be the same:
+
+ >>> jar is dma
+ True
+
+ The transaction will call tpc_begin:
+
+ >>> t1 = '1'
+ >>> jar.tpc_begin(t1)
+
+ Then the transaction will call commit on the jar:
+
+ >>> jar.commit(dma, t1)
+
+ This doesn't actually do anything. :)
+
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ The transaction calls vote:
+
+ >>> jar.tpc_vote(t1)
+
+ This prepares the data manager:
+
+ >>> dm.state, dm.delta
+ (1, 1)
+ >>> dm.prepared
+ True
+
+ At this point, the transaction decides to abort. It calls tpc_abort:
+
+ >>> jar.tpc_abort(t1)
+
+ Which causes the state of the data manager to be restored:
+
+ >>> dm.state, dm.delta
+ (0, 0)
+ >>> dm.prepared
+ False
+ """
+
+def test_commit_w_subtransactions():
+ """
+ So, we have a data manager:
+
+ >>> dm = DataManager()
+
+ and we do some work that modifies uncommited state:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ Now we'll commit the changes in a subtransaction. When the data
+ manager joins a transaction, the transaction will create an
+ adapter.
+
+ >>> dma = DataManagerAdapter(dm)
+
+ and register it as a modified object. At commit time, the
+ transaction will get the "jar" like this:
+
+ >>> jar = getattr(dma, '_p_jar', dma)
+
+ and, of course, the jar and the adapter will be the same:
+
+ >>> jar is dma
+ True
+
+ The transaction will call tpc_begin:
+
+ >>> t1 = '1'
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+
+ Then the transaction will call commit on the jar:
+
+ >>> jar.commit(dma, t1)
+
+ This doesn't actually do anything. :)
+
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ The transaction will then call tpc_vote:
+
+ >>> jar.tpc_vote(t1)
+
+ This doesn't do anything either, because zodb4 data managers don't
+ actually do two-phase commit for subtransactions.
+
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ Finally, we call tpc_finish. This does actally create a savepoint,
+ but we can't really tell that from outside.
+
+ >>> jar.tpc_finish(t1)
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ We'll do more of the above:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 2)
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+ >>> jar.commit(dma, t1)
+ >>> jar.tpc_vote(t1)
+ >>> jar.tpc_finish(t1)
+ >>> dm.state, dm.delta
+ (0, 2)
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 3)
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+ >>> jar.commit(dma, t1)
+ >>> jar.tpc_vote(t1)
+ >>> jar.tpc_finish(t1)
+ >>> dm.state, dm.delta
+ (0, 3)
+
+ Note that the bove works *because* the same transaction is used
+ for each subtransaction.
+
+ Finally, we'll do a little more work:
+
+ >>> dm.inc()
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 5)
+
+ and then commit the top-level transaction.
+
+ The transaction will actually go through the steps for a subtransaction:
+
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+ >>> jar.commit(dma, t1)
+ >>> jar.tpc_vote(t1)
+ >>> jar.tpc_finish(t1)
+
+ And then call commit_sub:
+
+ >>> jar.commit_sub(t1)
+
+ As usual, this doesn't actually do anything. ;)
+
+ >>> dm.state, dm.delta
+ (0, 5)
+
+ The transaction manager doesn's call tpc_begin, because commit_sub
+ implies the start of two-phase commit. Next, it does call commit:
+
+ >>> jar.commit(dma, t1)
+
+ which doesn't do anything.
+
+ Finally, the transaction calls tpc_vote:
+
+ >>> jar.tpc_vote(t1)
+
+ which actually does something (because this is the top-level txn):
+
+ >>> dm.state, dm.delta
+ (5, 5)
+ >>> dm.prepared
+ True
+
+ Finally, tpc_finish is called:
+
+ >>> jar.tpc_finish(t1)
+
+ and the data manager finishes the two-phase commit:
+
+ >>> dm.state, dm.delta
+ (5, 0)
+ >>> dm.prepared
+ False
+ """
+
+def test_commit_w_subtransactions_featuring_subtransaction_abort():
+ """
+ So, we have a data manager:
+
+ >>> dm = DataManager()
+
+ and we do some work that modifies uncommited state:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ Now we'll commit the changes in a subtransaction. When the data
+ manager joins a transaction, the transaction will create an
+ adapter.
+
+ >>> dma = DataManagerAdapter(dm)
+
+ and register it as a modified object. At commit time, the
+ transaction will get the "jar" like this:
+
+ >>> jar = getattr(dma, '_p_jar', dma)
+
+ and, of course, the jar and the adapter will be the same:
+
+ >>> jar is dma
+ True
+
+ The transaction will call tpc_begin:
+
+ >>> t1 = '1'
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+
+ Then the transaction will call commit on the jar:
+
+ >>> jar.commit(dma, t1)
+
+ This doesn't actually do anything. :)
+
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ The transaction will then call tpc_vote:
+
+ >>> jar.tpc_vote(t1)
+
+ This doesn't do anything either, because zodb4 data managers don't
+ actually do two-phase commit for subtransactions.
+
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ Finally, we call tpc_finish. This does actally create a savepoint,
+ but we can't really tell that from outside.
+
+ >>> jar.tpc_finish(t1)
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ We'll do more of the above:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 2)
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+ >>> jar.commit(dma, t1)
+ >>> jar.tpc_vote(t1)
+ >>> jar.tpc_finish(t1)
+ >>> dm.state, dm.delta
+ (0, 2)
+
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 3)
+
+ But then we'll decide to abort a subtransaction.
+
+ The transaction will just call abort as usual:
+
+ >>> jar.abort(dma, t1)
+
+ This will cause a rollback to the last savepoint:
+ >>> dm.state, dm.delta
+ (0, 2)
+
+ Then we do more work:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 3)
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+ >>> jar.commit(dma, t1)
+ >>> jar.tpc_vote(t1)
+ >>> jar.tpc_finish(t1)
+ >>> dm.state, dm.delta
+ (0, 3)
+
+ Note that the bove works *because* the same transaction is used
+ for each subtransaction.
+
+ Finally, we'll do a little more work:
+
+ >>> dm.inc()
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 5)
+
+ and then commit the top-level transaction.
+
+ The transaction will actually go through the steps for a subtransaction:
+
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+ >>> jar.commit(dma, t1)
+ >>> jar.tpc_vote(t1)
+ >>> jar.tpc_finish(t1)
+
+ And then call commit_sub:
+
+ >>> jar.commit_sub(t1)
+
+ As usual, this doesn't actually do anything. ;)
+
+ >>> dm.state, dm.delta
+ (0, 5)
+
+ The transaction manager doesn's call tpc_begin, because commit_sub
+ implies the start of two-phase commit. Next, it does call commit:
+
+ >>> jar.commit(dma, t1)
+
+ which doesn't do anything.
+
+ Finally, the transaction calls tpc_vote:
+
+ >>> jar.tpc_vote(t1)
+
+ which actually does something (because this is the top-level txn):
+
+ >>> dm.state, dm.delta
+ (5, 5)
+ >>> dm.prepared
+ True
+
+ Finally, tpc_finish is called:
+
+ >>> jar.tpc_finish(t1)
+
+ and the data manager finishes the two-phase commit:
+
+ >>> dm.state, dm.delta
+ (5, 0)
+ >>> dm.prepared
+ False
+ """
+
+def test_abort_w_subtransactions():
+ """
+ So, we have a data manager:
+
+ >>> dm = DataManager()
+
+ and we do some work that modifies uncommited state:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ Now we'll commit the changes in a subtransaction. When the data
+ manager joins a transaction, the transaction will create an
+ adapter.
+
+ >>> dma = DataManagerAdapter(dm)
+
+ and register it as a modified object. At commit time, the
+ transaction will get the "jar" like this:
+
+ >>> jar = getattr(dma, '_p_jar', dma)
+
+ and, of course, the jar and the adapter will be the same:
+
+ >>> jar is dma
+ True
+
+ The transaction will call tpc_begin:
+
+ >>> t1 = '1'
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+
+ Then the transaction will call commit on the jar:
+
+ >>> jar.commit(dma, t1)
+
+ This doesn't actually do anything. :)
+
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ The transaction will then call tpc_vote:
+
+ >>> jar.tpc_vote(t1)
+
+ This doesn't do anything either, because zodb4 data managers don't
+ actually do two-phase commit for subtransactions.
+
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ Finally, we call tpc_finish. This does actally create a savepoint,
+ but we can't really tell that from outside.
+
+ >>> jar.tpc_finish(t1)
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ We'll do more of the above:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 2)
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+ >>> jar.commit(dma, t1)
+ >>> jar.tpc_vote(t1)
+ >>> jar.tpc_finish(t1)
+ >>> dm.state, dm.delta
+ (0, 2)
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 3)
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+ >>> jar.commit(dma, t1)
+ >>> jar.tpc_vote(t1)
+ >>> jar.tpc_finish(t1)
+ >>> dm.state, dm.delta
+ (0, 3)
+
+ Note that the bove works *because* the same transaction is used
+ for each subtransaction.
+
+ Finally, we'll do a little more work:
+
+ >>> dm.inc()
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 5)
+
+ and then abort the top-level transaction.
+
+ The transaction first call abort on the jar:
+
+ >>> jar.abort(dma, t1)
+
+ This will have the effect of aborting the subtrancation:
+
+ >>> dm.state, dm.delta
+ (0, 3)
+
+ Then the transaction will call abort_sub:
+
+ >>> jar.abort_sub(t1)
+
+ This will abort all of the subtransactions:
+
+ >>> dm.state, dm.delta
+ (0, 0)
+ """
+
+
+def test_tpc_abort_w_subtransactions_featuring_subtransaction_abort():
+ """
+ So, we have a data manager:
+
+ >>> dm = DataManager()
+
+ and we do some work that modifies uncommited state:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ Now we'll commit the changes in a subtransaction. When the data
+ manager joins a transaction, the transaction will create an
+ adapter.
+
+ >>> dma = DataManagerAdapter(dm)
+
+ and register it as a modified object. At commit time, the
+ transaction will get the "jar" like this:
+
+ >>> jar = getattr(dma, '_p_jar', dma)
+
+ and, of course, the jar and the adapter will be the same:
+
+ >>> jar is dma
+ True
+
+ The transaction will call tpc_begin:
+
+ >>> t1 = '1'
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+
+ Then the transaction will call commit on the jar:
+
+ >>> jar.commit(dma, t1)
+
+ This doesn't actually do anything. :)
+
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ The transaction will then call tpc_vote:
+
+ >>> jar.tpc_vote(t1)
+
+ This doesn't do anything either, because zodb4 data managers don't
+ actually do two-phase commit for subtransactions.
+
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ Finally, we call tpc_finish. This does actally create a savepoint,
+ but we can't really tell that from outside.
+
+ >>> jar.tpc_finish(t1)
+ >>> dm.state, dm.delta
+ (0, 1)
+
+ We'll do more of the above:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 2)
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+ >>> jar.commit(dma, t1)
+ >>> jar.tpc_vote(t1)
+ >>> jar.tpc_finish(t1)
+ >>> dm.state, dm.delta
+ (0, 2)
+
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 3)
+
+ But then we'll decide to abort a subtransaction.
+
+ The transaction will just call abort as usual:
+
+ >>> jar.abort(dma, t1)
+
+ This will cause a rollback to the last savepoint:
+ >>> dm.state, dm.delta
+ (0, 2)
+
+ Then we do more work:
+
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 3)
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+ >>> jar.commit(dma, t1)
+ >>> jar.tpc_vote(t1)
+ >>> jar.tpc_finish(t1)
+ >>> dm.state, dm.delta
+ (0, 3)
+
+ Note that the bove works *because* the same transaction is used
+ for each subtransaction.
+
+ Finally, we'll do a little more work:
+
+ >>> dm.inc()
+ >>> dm.inc()
+ >>> dm.state, dm.delta
+ (0, 5)
+
+ and then commit the top-level transaction.
+
+ The transaction will actually go through the steps for a subtransaction:
+
+ >>> jar.tpc_begin(t1, 1) # 1 -> subtxn
+ >>> jar.commit(dma, t1)
+ >>> jar.tpc_vote(t1)
+ >>> jar.tpc_finish(t1)
+
+ And then call commit_sub:
+
+ >>> jar.commit_sub(t1)
+
+ As usual, this doesn't actually do anything. ;)
+
+ >>> dm.state, dm.delta
+ (0, 5)
+
+ The transaction manager doesn's call tpc_begin, because commit_sub
+ implies the start of two-phase commit. Next, it does call commit:
+
+ >>> jar.commit(dma, t1)
+
+ which doesn't do anything.
+
+ Finally, the transaction calls tpc_vote:
+
+ >>> jar.tpc_vote(t1)
+
+ which actually does something (because this is the top-level txn):
+
+ >>> dm.state, dm.delta
+ (5, 5)
+ >>> dm.prepared
+ True
+
+ Now, at the last minute, the transaction is aborted (possibly due
+ to a "no vote" from another data manager):
+
+ >>> jar.tpc_abort(t1)
+
+ An the changes are undone:
+
+ >>> dm.state, dm.delta
+ (0, 0)
+ >>> dm.prepared
+ False
+ """
+
+def test_suite():
+ return unittest.TestSuite((
+ DocTestSuite(),
+ ))
+
+if __name__ == '__main__': unittest.main()
=== Zope/lib/python/ZODB/tests/util.py 1.1 => 1.2 ===
--- /dev/null Wed Feb 18 21:59:11 2004
+++ Zope/lib/python/ZODB/tests/util.py Wed Feb 18 21:59:10 2004
@@ -0,0 +1,43 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+"""Conventience function for creating test databases
+
+$Id$
+"""
+
+import time
+import persistent
+from ZODB.DemoStorage import DemoStorage
+from ZODB.DB import DB as _DB
+try:
+ from transaction import get_transaction
+except ImportError:
+ pass # else assume ZODB will install it as a builtin
+
+def DB(name='Test'):
+ return _DB(DemoStorage(name))
+
+def commit():
+ get_transaction().commit()
+
+def pack(db):
+ db.pack(time.time()+1)
+
+class P(persistent.Persistent):
+
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return 'P(%s)' % self.name
=== Zope/lib/python/ZODB/tests/ConflictResolution.py 1.13 => 1.14 ===
--- Zope/lib/python/ZODB/tests/ConflictResolution.py:1.13 Tue Feb 17 20:12:59 2004
+++ Zope/lib/python/ZODB/tests/ConflictResolution.py Wed Feb 18 21:59:10 2004
@@ -102,9 +102,9 @@
self.fail("Expected ConflictError")
def checkZClassesArentResolved(self):
- from ZODB.ConflictResolution import bad_class
+ from ZODB.ConflictResolution import find_global, BadClassName
dummy_class_tuple = ('*foobar', ())
- assert bad_class(dummy_class_tuple) == 1
+ self.assertRaises(BadClassName, find_global, '*foobar', ())
def checkBuggyResolve1(self):
obj = PCounter3()
=== Zope/lib/python/ZODB/tests/StorageTestBase.py 1.32 => 1.33 ===
--- Zope/lib/python/ZODB/tests/StorageTestBase.py:1.32 Tue Feb 17 20:12:59 2004
+++ Zope/lib/python/ZODB/tests/StorageTestBase.py Wed Feb 18 21:59:10 2004
@@ -79,19 +79,24 @@
u.persistent_load = persistent_load
klass_info = u.load()
if isinstance(klass_info, types.TupleType):
- if isinstance(klass_info[0], types.TupleType):
- modname, klassname = klass_info[0]
+ if isinstance(klass_info[0], type):
+ # XXX what is the second part of klass_info?
+ klass, xxx = klass_info
+ assert not xxx
else:
- modname, klassname = klass_info
- if modname == "__main__":
- ns = globals()
- else:
- mod = import_helper(modname)
- ns = mod.__dict__
- try:
- klass = ns[klassname]
- except KeyError:
- print >> sys.stderr, "can't find %s in %r" % (klassname, ns)
+ if isinstance(klass_info[0], tuple):
+ modname, klassname = klass_info[0]
+ else:
+ modname, klassname = klass_info
+ if modname == "__main__":
+ ns = globals()
+ else:
+ mod = import_helper(modname)
+ ns = mod.__dict__
+ try:
+ klass = ns[klassname]
+ except KeyError:
+ print >> sys.stderr, "can't find %s in %r" % (klassname, ns)
inst = klass()
else:
raise ValueError, "expected class info: %s" % repr(klass_info)
=== Zope/lib/python/ZODB/tests/testCache.py 1.17 => 1.18 ===
--- Zope/lib/python/ZODB/tests/testCache.py:1.17 Fri Nov 28 11:44:54 2003
+++ Zope/lib/python/ZODB/tests/testCache.py Wed Feb 18 21:59:10 2004
@@ -21,6 +21,7 @@
import time
import types
import unittest
+import gc
import ZODB
import ZODB.MappingStorage
@@ -102,14 +103,14 @@
def checkFullSweep(self):
old_size = self.db.cacheSize()
time.sleep(3)
- self.db.cacheFullSweep(0)
+ self.db.cacheFullSweep()
new_size = self.db.cacheSize()
self.assert_(new_size < old_size, "%s < %s" % (old_size, new_size))
def checkMinimize(self):
old_size = self.db.cacheSize()
time.sleep(3)
- self.db.cacheMinimize(0)
+ self.db.cacheMinimize()
new_size = self.db.cacheSize()
self.assert_(new_size < old_size, "%s < %s" % (old_size, new_size))
@@ -178,13 +179,19 @@
CONNS = 3
for i in range(CONNS):
self.noodle_new_connection()
-
+
self.assertEquals(self.db.cacheSize(), CACHE_SIZE * CONNS)
details = self.db.cacheDetailSize()
self.assertEquals(len(details), CONNS)
for d in details:
self.assertEquals(d['ngsize'], CACHE_SIZE)
- self.assertEquals(d['size'], CACHE_SIZE)
+
+ # The assertion below is non-sensical
+ # The (poorly named) cache size is a target for non-ghosts.
+ # The cache *usually* contains non-ghosts, so that the
+ # size normally exceeds the target size.
+
+ #self.assertEquals(d['size'], CACHE_SIZE)
def checkDetail(self):
CACHE_SIZE = 10
@@ -193,6 +200,28 @@
CONNS = 3
for i in range(CONNS):
self.noodle_new_connection()
+
+ gc.collect()
+
+ # XXX The above gc.collect call is necessary to make this test
+ # pass.
+ #
+ # This test then only works because the other of computations
+ # and object accesses in the "noodle" calls is such that the
+ # persistent mapping containing the MinPO objects is
+ # deactivated before the MinPO objects.
+ #
+ # - Without the gc call, the cache will contain ghost MinPOs
+ # and the check of the MinPO count below will fail. That's
+ # because the counts returned by cacheDetail include ghosts.
+ #
+ # - If the mapping object containing the MinPOs isn't
+ # deactivated, there will be one fewer non-ghost MinPO and
+ # the test will fail anyway.
+ #
+ # This test really needs to be thought through and documented
+ # better.
+
for klass, count in self.db.cacheDetail():
if klass.endswith('MinPO'):
=== Zope/lib/python/ZODB/tests/testTransaction.py 1.17 => 1.18 ===
--- Zope/lib/python/ZODB/tests/testTransaction.py:1.17 Wed Dec 24 11:01:58 2003
+++ Zope/lib/python/ZODB/tests/testTransaction.py Wed Feb 18 21:59:10 2004
@@ -628,9 +628,39 @@
HoserJar.committed += 1
+def test_join():
+ """White-box test of the join method
+
+ The join method is provided for "backward-compatability" with ZODB 4
+ data managers.
+
+ The argument to join must be a zodb4 data manager,
+ transaction.interfaces.IDataManager.
+
+ >>> from ZODB.tests.sampledm import DataManager
+ >>> from ZODB.Transaction import DataManagerAdapter
+ >>> t = Transaction.Transaction()
+ >>> dm = DataManager()
+ >>> t.join(dm)
+
+ The end result is that a data manager adapter is one of the
+ transaction's objects:
+
+ >>> isinstance(t._objects[0], DataManagerAdapter)
+ True
+ >>> t._objects[0]._datamanager is dm
+ True
+
+ """
+
def test_suite():
+ from doctest import DocTestSuite
+ return unittest.TestSuite((
+ DocTestSuite(),
+ unittest.makeSuite(TransactionTests),
+ ))
- return unittest.makeSuite(TransactionTests)
if __name__ == '__main__':
unittest.TextTestRunner().run(test_suite())
+
=== Zope/lib/python/ZODB/tests/testUtils.py 1.5 => 1.6 ===
--- Zope/lib/python/ZODB/tests/testUtils.py:1.5 Thu Dec 11 00:16:50 2003
+++ Zope/lib/python/ZODB/tests/testUtils.py Wed Feb 18 21:59:10 2004
@@ -16,7 +16,6 @@
import random
import unittest
from persistent import Persistent
-import ZODB.coptimizations
NUM = 100
@@ -47,11 +46,12 @@
self.assertEquals(U64("\000\000\000\001\000\000\000\000"), 1L<<32)
def checkPersistentIdHandlesDescriptor(self):
+ from ZODB.serialize import BaseObjectWriter
class P(Persistent):
pass
- L = []
- pid = ZODB.coptimizations.new_persistent_id(None, L)
- pid(P)
+
+ writer = BaseObjectWriter(None)
+ self.assertEqual(writer.persistent_id(P), None)
def test_suite():
return unittest.makeSuite(TestUtils, 'check')
More information about the Zodb-checkins
mailing list