2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
9 from twisted.trial import unittest
12 import cPickle as pickle
17 import cStringIO as StringIO
22 from twisted.persisted import styles, aot, crefutil
25 class VersionTestCase(unittest.TestCase):
26 def testNullVersionUpgrade(self):
30 pkcl = pickle.dumps(NullVersioned())
31 class NullVersioned(styles.Versioned):
32 persistenceVersion = 1
33 def upgradeToVersion1(self):
35 mnv = pickle.loads(pkcl)
37 assert mnv.ok, "initial upgrade not run!"
39 def testVersionUpgrade(self):
41 class MyVersioned(styles.Versioned):
42 persistenceVersion = 2
43 persistenceForgets = ['garbagedata']
49 self.garbagedata = lambda q: 'cant persist'
51 def upgradeToVersion3(self):
54 def upgradeToVersion4(self):
57 assert not (mv.v3 or mv.v4), "hasn't been upgraded yet"
58 pickl = pickle.dumps(mv)
59 MyVersioned.persistenceVersion = 4
60 obj = pickle.loads(pickl)
62 assert obj.v3, "didn't do version 3 upgrade"
63 assert obj.v4, "didn't do version 4 upgrade"
64 pickl = pickle.dumps(obj)
65 obj = pickle.loads(pickl)
67 assert obj.v3 == 1, "upgraded unnecessarily"
68 assert obj.v4 == 1, "upgraded unnecessarily"
70 def testNonIdentityHash(self):
71 global ClassWithCustomHash
72 class ClassWithCustomHash(styles.Versioned):
73 def __init__(self, unique, hash):
79 v1 = ClassWithCustomHash('v1', 0)
80 v2 = ClassWithCustomHash('v2', 0)
82 pkl = pickle.dumps((v1, v2))
84 ClassWithCustomHash.persistenceVersion = 1
85 ClassWithCustomHash.upgradeToVersion1 = lambda self: setattr(self, 'upgraded', True)
86 v1, v2 = pickle.loads(pkl)
88 self.assertEqual(v1.unique, 'v1')
89 self.assertEqual(v2.unique, 'v2')
90 self.failUnless(v1.upgraded)
91 self.failUnless(v2.upgraded)
93 def testUpgradeDeserializesObjectsRequiringUpgrade(self):
94 global ToyClassA, ToyClassB
95 class ToyClassA(styles.Versioned):
97 class ToyClassB(styles.Versioned):
101 pklA, pklB = pickle.dumps(x), pickle.dumps(y)
103 ToyClassA.persistenceVersion = 1
104 def upgradeToVersion1(self):
105 self.y = pickle.loads(pklB)
107 ToyClassA.upgradeToVersion1 = upgradeToVersion1
108 ToyClassB.persistenceVersion = 1
109 ToyClassB.upgradeToVersion1 = lambda self: setattr(self, 'upgraded', True)
111 x = pickle.loads(pklA)
113 self.failUnless(x.y.upgraded)
117 class VersionedSubClass(styles.Versioned):
122 class SecondVersionedSubClass(styles.Versioned):
127 class VersionedSubSubClass(VersionedSubClass):
132 class VersionedDiamondSubClass(VersionedSubSubClass, SecondVersionedSubClass):
137 class AybabtuTests(unittest.TestCase):
139 L{styles._aybabtu} gets all of classes in the inheritance hierarchy of its
140 argument that are strictly between L{Versioned} and the class itself.
143 def test_aybabtuStrictEmpty(self):
145 L{styles._aybabtu} of L{Versioned} itself is an empty list.
147 self.assertEqual(styles._aybabtu(styles.Versioned), [])
150 def test_aybabtuStrictSubclass(self):
152 There are no classes I{between} L{VersionedSubClass} and L{Versioned},
153 so L{styles._aybabtu} returns an empty list.
155 self.assertEqual(styles._aybabtu(VersionedSubClass), [])
158 def test_aybabtuSubsubclass(self):
160 With a sub-sub-class of L{Versioned}, L{styles._aybabtu} returns a list
161 containing the intervening subclass.
163 self.assertEqual(styles._aybabtu(VersionedSubSubClass),
167 def test_aybabtuStrict(self):
169 For a diamond-shaped inheritance graph, L{styles._aybabtu} returns a
170 list containing I{both} intermediate subclasses.
173 styles._aybabtu(VersionedDiamondSubClass),
174 [VersionedSubSubClass, VersionedSubClass, SecondVersionedSubClass])
178 class MyEphemeral(styles.Ephemeral):
180 def __init__(self, x):
184 class EphemeralTestCase(unittest.TestCase):
186 def testEphemeral(self):
188 self.assertEqual(o.__class__, MyEphemeral)
189 self.assertEqual(o.x, 3)
191 pickl = pickle.dumps(o)
192 o = pickle.loads(pickl)
194 self.assertEqual(o.__class__, styles.Ephemeral)
195 self.assert_(not hasattr(o, 'x'))
200 def __init__(self, x):
223 class PicklingTestCase(unittest.TestCase):
224 """Test pickling of extra object types."""
226 def testModule(self):
227 pickl = pickle.dumps(styles)
228 o = pickle.loads(pickl)
229 self.assertEqual(o, styles)
231 def testClassMethod(self):
232 pickl = pickle.dumps(Pickleable.getX)
233 o = pickle.loads(pickl)
234 self.assertEqual(o, Pickleable.getX)
236 def testInstanceMethod(self):
238 pickl = pickle.dumps(obj.getX)
239 o = pickle.loads(pickl)
240 self.assertEqual(o(), 4)
241 self.assertEqual(type(o), type(obj.getX))
243 def testStringIO(self):
244 f = StringIO.StringIO()
246 pickl = pickle.dumps(f)
247 o = pickle.loads(pickl)
248 self.assertEqual(type(o), type(f))
249 self.assertEqual(f.getvalue(), "abc")
253 def __init__(self, x):
259 def __getstate__(self):
261 def __setstate__(self, state):
264 class AOTTestCase(unittest.TestCase):
265 def testSimpleTypes(self):
266 obj = (1, 2.0, 3j, True, slice(1, 2, 3), 'hello', u'world', sys.maxint + 1, None, Ellipsis)
267 rtObj = aot.unjellyFromSource(aot.jellyToSource(obj))
268 self.assertEqual(obj, rtObj)
270 def testMethodSelfIdentity(self):
273 a.bmethod = b.bmethod
275 im_ = aot.unjellyFromSource(aot.jellyToSource(b)).a.bmethod
276 self.assertEqual(im_.im_class, im_.im_self.__class__)
279 def test_methodNotSelfIdentity(self):
281 If a class change after an instance has been created,
282 L{aot.unjellyFromSource} shoud raise a C{TypeError} when trying to
283 unjelly the instance.
287 a.bmethod = b.bmethod
289 savedbmethod = B.bmethod
292 self.assertRaises(TypeError, aot.unjellyFromSource,
293 aot.jellyToSource(b))
295 B.bmethod = savedbmethod
298 def test_unsupportedType(self):
300 L{aot.jellyToSource} should raise a C{TypeError} when trying to jelly
306 from sets import Set as set
307 self.assertRaises(TypeError, aot.jellyToSource, set())
310 def testBasicIdentity(self):
311 # Anyone wanting to make this datastructure more complex, and thus this
312 # test more comprehensive, is welcome to do so.
313 aj = aot.AOTJellier().jellyToAO
314 d = {'hello': 'world', "method": aj}
316 "he\tllo\n\n\"x world!",
317 u"goodbye \n\t\u1010 world!",
318 1, 1.0, 100 ** 100l, unittest, aot.AOTJellier, d,
325 uj = aot.unjellyFromSource(aot.jellyToSource([l, l]))
326 assert uj[0] is uj[1]
327 assert uj[1][0:5] == l[0:5]
330 def testNonDictState(self):
332 a.state = "meringue!"
333 assert aot.unjellyFromSource(aot.jellyToSource(a)).state == a.state
335 def testCopyReg(self):
337 sio = StringIO.StringIO()
339 uj = aot.unjellyFromSource(aot.jellyToSource(sio))
340 # print repr(uj.__dict__)
341 assert uj.getvalue() == s
343 def testFunkyReferences(self):
344 o = EvilSourceror(EvilSourceror([]))
345 j1 = aot.jellyToAOT(o)
346 oj = aot.unjellyFromAOT(j1)
349 assert oj.a.b is oj.b
350 assert oj.c is not oj.c.c
353 class CrefUtilTestCase(unittest.TestCase):
355 Tests for L{crefutil}.
358 def test_dictUnknownKey(self):
360 L{crefutil._DictKeyAndValue} only support keys C{0} and C{1}.
362 d = crefutil._DictKeyAndValue({})
363 self.assertRaises(RuntimeError, d.__setitem__, 2, 3)
366 def test_deferSetMultipleTimes(self):
368 L{crefutil._Defer} can be assigned a key only one time.
370 d = crefutil._Defer()
372 self.assertRaises(RuntimeError, d.__setitem__, 0, 1)
376 testCases = [VersionTestCase, EphemeralTestCase, PicklingTestCase]