Commit 859e180b authored by Jeremy Hylton's avatar Jeremy Hylton

Restructure relationship between persistent.txt and test_persistent.py.

Move some of the test framework code from .py to .txt, and add a more
detailed comment at the top about the contract between .txt and the
.py file that uses it.

Move the interface test into .txt.  I guess it's not so bad.

Change some tests that displayed the contents of __dict__.  Other
persistent implementations may have extra state in __dict__.

Remove B, it is unused.

Change DocFileSuite() to accept an explicit set of globals.

Remove the pickling/P2 test, because it was too hard to make work with
the framework.  Include comment explaining how such a test should be
added.
parent 3ed59c28
...@@ -2,8 +2,42 @@ Tests for persistent.Persistent ...@@ -2,8 +2,42 @@ Tests for persistent.Persistent
=============================== ===============================
This document is an extended doc test that covers the basics of the This document is an extended doc test that covers the basics of the
Persistent base class. It depends on a few base classes defined in Persistent base class. The test expects a class named 'P' to be
test_persistent.py. provided in its globals. The P class implements the Persistent
interface.
Test framework
--------------
The class P needs to behave like ExampleP. (Note that the code below
is *not* part of the tests.)
class ExampleP(Persistent):
def __init__(self):
self.x = 0
def inc(self):
self.x += 1
The tests use stub data managers. A data manager is responsible for
loading and storing the state of a persistent object. It's stored in
the _p_jar attribute of a persistent object.
>>> class DM:
... def __init__(self):
... self.called = 0
... def register(self, ob):
... self.called += 1
... def setstate(self, ob):
... ob.__setstate__({'x': 42})
>>> class BrokenDM(DM):
... def register(self,ob):
... self.called += 1
... raise NotImplementedError
... def setstate(self,ob):
... raise NotImplementedError
>>> from persistent import Persistent
Test Persistent without Data Manager Test Persistent without Data Manager
------------------------------------ ------------------------------------
...@@ -56,7 +90,7 @@ a simple testing stub. ...@@ -56,7 +90,7 @@ a simple testing stub.
>>> p = P() >>> p = P()
>>> dm = DM() >>> dm = DM()
>>> p._p_oid = oid >>> p._p_oid = "00000012"
>>> p._p_jar = dm >>> p._p_jar = dm
>>> p._p_changed >>> p._p_changed
0 0
...@@ -127,8 +161,11 @@ The next several tests cover the __getstate__() and __setstate__() ...@@ -127,8 +161,11 @@ The next several tests cover the __getstate__() and __setstate__()
implementations. implementations.
>>> p = P() >>> p = P()
>>> p.__getstate__() >>> state = p.__getstate__()
{'x': 0} >>> isinstance(state, dict)
True
>>> state['x']
0
>>> p._p_state >>> p._p_state
0 0
...@@ -255,8 +292,8 @@ have different locations. ...@@ -255,8 +292,8 @@ have different locations.
>>> p = P() >>> p = P()
>>> p.inc() >>> p.inc()
>>> p.inc() >>> p.inc()
>>> p.__dict__ >>> 'x' in p.__dict__
{'x': 2} True
>>> p._p_jar >>> p._p_jar
...@@ -360,8 +397,8 @@ If the most-derived class does not specify ...@@ -360,8 +397,8 @@ If the most-derived class does not specify
>>> p_shouldHaveDict.__dictoffset__ > 0 >>> p_shouldHaveDict.__dictoffset__ > 0
True True
>>> x = p_shouldHaveDict() >>> x = p_shouldHaveDict()
>>> x.__dict__ >>> isinstance(x.__dict__, dict)
{} True
Pickling Pickling
...@@ -381,11 +418,31 @@ True ...@@ -381,11 +418,31 @@ True
>>> p2.x == p.x >>> p2.x == p.x
True True
The P2 class has a custom __getstate__ and __setstate__. We should also test that pickle works with custom getstate and
setstate. Perhaps even reduce. The problem is that pickling depends
>>> p = P2() on finding the class in a particular module, and classes defined here
>>> p2 = pickle.loads(pickle.dumps(p)) won't appear in any module. We could require each user of the tests
>>> p2.__class__ is P2 to define a base class, but that might be tedious.
True
>>> p2.__dict__ Interfaces
{'v': 42} ----------
Some versions of Zope and ZODB have the zope.interfaces package
available. If it is available, then persistent will be associated
with several interfaces. It's hard to write a doctest test that runs
the tests only if zope.interface is available, so this test looks a
little unusual. One problem is that the assert statements won't do
anything if you run with -O.
>>> try:
... import zope.interface
... except ImportError:
... pass
... else:
... from persistent.interfaces import IPersistent
... assert IPersistent.isImplementedByInstancesOf(Persistent)
... p = Persistent()
... assert IPersistent.isImplementedBy(p)
... assert IPersistent.isImplementedByInstancesOf(P)
... p = P()
... assert IPersistent.isImplementedBy(p)
...@@ -12,23 +12,12 @@ ...@@ -12,23 +12,12 @@
# #
############################################################################## ##############################################################################
import doctest import doctest
import new
import os import os
import sys import sys
import unittest import unittest
from persistent import Persistent
from persistent.interfaces import IPersistent
import persistent.tests import persistent.tests
from persistent import Persistent
try:
import zope.interface
except ImportError:
interfaces = False
else:
interfaces = True
oid = "\0\0\0\0\0\0hi"
class P(Persistent): class P(Persistent):
def __init__(self): def __init__(self):
...@@ -36,72 +25,16 @@ class P(Persistent): ...@@ -36,72 +25,16 @@ class P(Persistent):
def inc(self): def inc(self):
self.x += 1 self.x += 1
class P2(P): def DocFileSuite(path, globs=None):
def __getstate__(self):
return 42
def __setstate__(self, v):
self.v = v
class B(Persistent):
__slots__ = ["x", "_p_serial"]
def __init__(self):
self.x = 0
def inc(self):
self.x += 1
def __getstate__(self):
return {'x': self.x}
def __setstate__(self, state):
self.x = state['x']
class DM:
def __init__(self):
self.called = 0
def register(self, ob):
self.called += 1
def setstate(self, ob):
ob.__setstate__({'x': 42})
class BrokenDM(DM):
def register(self,ob):
self.called += 1
raise NotImplementedError
def setstate(self,ob):
raise NotImplementedError
class Test(unittest.TestCase):
# XXX This is the only remaining unittest. Figure out how to move
# this into doctest?
if interfaces:
def testInterface(self):
self.assert_(IPersistent.isImplementedByInstancesOf(Persistent),
"%s does not implement IPersistent" % Persistent)
p = Persistent()
self.assert_(IPersistent.isImplementedBy(p),
"%s does not implement IPersistent" % p)
self.assert_(IPersistent.isImplementedByInstancesOf(P),
"%s does not implement IPersistent" % P)
p = P()
self.assert_(IPersistent.isImplementedBy(p),
"%s does not implement IPersistent" % p)
def DocFileSuite(path):
# It's not entirely obvious how to connection this single string # It's not entirely obvious how to connection this single string
# with unittest. For now, re-use the _utest() function that comes # with unittest. For now, re-use the _utest() function that comes
# standard with doctest in Python 2.3. One problem is that the # standard with doctest in Python 2.3. One problem is that the
# error indicator doesn't point to the line of the doctest file # error indicator doesn't point to the line of the doctest file
# that failed. # that failed.
source = open(path).read() source = open(path).read()
t = doctest.Tester(globs=sys._getframe(1).f_globals) if globs is None:
globs = sys._getframe(1).f_globals
t = doctest.Tester(globs=globs)
def runit(): def runit():
doctest._utest(t, path, source, path, 0) doctest._utest(t, path, source, path, 0)
f = unittest.FunctionTestCase(runit, description="doctest from %s" % path) f = unittest.FunctionTestCase(runit, description="doctest from %s" % path)
...@@ -110,7 +43,5 @@ def DocFileSuite(path): ...@@ -110,7 +43,5 @@ def DocFileSuite(path):
return suite return suite
def test_suite(): def test_suite():
p = os.path.join(persistent.tests.__path__[0], "persistent.txt") path = os.path.join(persistent.tests.__path__[0], "persistent.txt")
s = unittest.makeSuite(Test) return DocFileSuite(path, {"P": P})
s.addTest(DocFileSuite(p))
return s
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment