OLD | NEW |
| (Empty) |
1 | |
2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
3 # See LICENSE for details. | |
4 | |
5 | |
6 # System Imports | |
7 import sys | |
8 | |
9 from twisted.trial import unittest | |
10 | |
11 try: | |
12 import cPickle as pickle | |
13 except ImportError: | |
14 import pickle | |
15 | |
16 try: | |
17 import cStringIO as StringIO | |
18 except ImportError: | |
19 import StringIO | |
20 | |
21 # Twisted Imports | |
22 from twisted.persisted import styles, aot, crefutil | |
23 | |
24 | |
25 class VersionTestCase(unittest.TestCase): | |
26 def testNullVersionUpgrade(self): | |
27 global NullVersioned | |
28 class NullVersioned: | |
29 ok = 0 | |
30 pkcl = pickle.dumps(NullVersioned()) | |
31 class NullVersioned(styles.Versioned): | |
32 persistenceVersion = 1 | |
33 def upgradeToVersion1(self): | |
34 self.ok = 1 | |
35 mnv = pickle.loads(pkcl) | |
36 styles.doUpgrade() | |
37 assert mnv.ok, "initial upgrade not run!" | |
38 | |
39 def testVersionUpgrade(self): | |
40 global MyVersioned | |
41 class MyVersioned(styles.Versioned): | |
42 persistenceVersion = 2 | |
43 persistenceForgets = ['garbagedata'] | |
44 v3 = 0 | |
45 v4 = 0 | |
46 | |
47 def __init__(self): | |
48 self.somedata = 'xxx' | |
49 self.garbagedata = lambda q: 'cant persist' | |
50 | |
51 def upgradeToVersion3(self): | |
52 self.v3 += 1 | |
53 | |
54 def upgradeToVersion4(self): | |
55 self.v4 += 1 | |
56 mv = MyVersioned() | |
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) | |
61 styles.doUpgrade() | |
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) | |
66 styles.doUpgrade() | |
67 assert obj.v3 == 1, "upgraded unnecessarily" | |
68 assert obj.v4 == 1, "upgraded unnecessarily" | |
69 | |
70 def testNonIdentityHash(self): | |
71 global ClassWithCustomHash | |
72 class ClassWithCustomHash(styles.Versioned): | |
73 def __init__(self, unique, hash): | |
74 self.unique = unique | |
75 self.hash = hash | |
76 def __hash__(self): | |
77 return self.hash | |
78 | |
79 v1 = ClassWithCustomHash('v1', 0) | |
80 v2 = ClassWithCustomHash('v2', 0) | |
81 | |
82 pkl = pickle.dumps((v1, v2)) | |
83 del v1, v2 | |
84 ClassWithCustomHash.persistenceVersion = 1 | |
85 ClassWithCustomHash.upgradeToVersion1 = lambda self: setattr(self, 'upgr
aded', True) | |
86 v1, v2 = pickle.loads(pkl) | |
87 styles.doUpgrade() | |
88 self.assertEquals(v1.unique, 'v1') | |
89 self.assertEquals(v2.unique, 'v2') | |
90 self.failUnless(v1.upgraded) | |
91 self.failUnless(v2.upgraded) | |
92 | |
93 def testUpgradeDeserializesObjectsRequiringUpgrade(self): | |
94 global ToyClassA, ToyClassB | |
95 class ToyClassA(styles.Versioned): | |
96 pass | |
97 class ToyClassB(styles.Versioned): | |
98 pass | |
99 x = ToyClassA() | |
100 y = ToyClassB() | |
101 pklA, pklB = pickle.dumps(x), pickle.dumps(y) | |
102 del x, y | |
103 ToyClassA.persistenceVersion = 1 | |
104 def upgradeToVersion1(self): | |
105 self.y = pickle.loads(pklB) | |
106 styles.doUpgrade() | |
107 ToyClassA.upgradeToVersion1 = upgradeToVersion1 | |
108 ToyClassB.persistenceVersion = 1 | |
109 ToyClassB.upgradeToVersion1 = lambda self: setattr(self, 'upgraded', Tru
e) | |
110 | |
111 x = pickle.loads(pklA) | |
112 styles.doUpgrade() | |
113 self.failUnless(x.y.upgraded) | |
114 | |
115 class MyEphemeral(styles.Ephemeral): | |
116 | |
117 def __init__(self, x): | |
118 self.x = x | |
119 | |
120 | |
121 class EphemeralTestCase(unittest.TestCase): | |
122 | |
123 def testEphemeral(self): | |
124 o = MyEphemeral(3) | |
125 self.assertEquals(o.__class__, MyEphemeral) | |
126 self.assertEquals(o.x, 3) | |
127 | |
128 pickl = pickle.dumps(o) | |
129 o = pickle.loads(pickl) | |
130 | |
131 self.assertEquals(o.__class__, styles.Ephemeral) | |
132 self.assert_(not hasattr(o, 'x')) | |
133 | |
134 | |
135 class Pickleable: | |
136 | |
137 def __init__(self, x): | |
138 self.x = x | |
139 | |
140 def getX(self): | |
141 return self.x | |
142 | |
143 class A: | |
144 """ | |
145 dummy class | |
146 """ | |
147 def amethod(self): | |
148 pass | |
149 | |
150 class B: | |
151 """ | |
152 dummy class | |
153 """ | |
154 def bmethod(self): | |
155 pass | |
156 | |
157 def funktion(): | |
158 pass | |
159 | |
160 try: | |
161 from twisted.persisted import marmalade | |
162 except ImportError: | |
163 pass | |
164 else: | |
165 class Marmaladeable(marmalade.DOMJellyable): | |
166 | |
167 jellyDOMVersion = 1 | |
168 | |
169 def __init__(self, integer, instance, name, sequence): | |
170 self.integer = integer | |
171 self.instance = instance | |
172 self.sequence = sequence | |
173 self.name = name | |
174 | |
175 def jellyToDOM_1(self, jellier, element): | |
176 from twisted.python.reflect import qual | |
177 element.setAttribute("integer", str(self.integer)) | |
178 element.setAttribute("instance", qual(self.instance.__class__)) # no
t l33t enough | |
179 element.setAttribute("name", str(self.name)) | |
180 # oops forgot self.sequence | |
181 | |
182 def unjellyFromDOM_1(self, unjellier, element): | |
183 from twisted.python.reflect import namedClass | |
184 self.integer = int(element.getAttribute("integer")) | |
185 self.instance = namedClass(element.getAttribute("instance"))() | |
186 self.name = element.getAttribute("name") | |
187 # just give us any ol' list | |
188 self.sequence = [self.instance, self.instance] | |
189 | |
190 def jellyToDOM_2(self, jellier, element): | |
191 element.setAttribute("integer", str(self.integer)) | |
192 element.setAttribute("name", str(self.name)) | |
193 instanceNode = jellier.jellyToNode(self.instance) # l33ter! | |
194 instanceNode.setAttribute("parent:role", "instance") | |
195 element.appendChild(instanceNode) | |
196 i = 0 | |
197 for seqel in self.sequence: | |
198 seqNode = jellier.jellyToNode(seqel) | |
199 seqNode.setAttribute("parent:role", "sequence:%d" % i) | |
200 element.appendChild(seqNode) | |
201 i = i + 1 | |
202 | |
203 def unjellyFromDOM_2(self, unjellier, element): | |
204 self.integer = int(element.getAttribute("integer")) | |
205 self.name = element.getAttribute("name") | |
206 | |
207 # Note to people reading this as an example: if you don't use | |
208 # "unjellyInto", and instead use "unjellyFromNode", it will appear t
o | |
209 # work. _however_, it will also have some really surprising results | |
210 # when you have references in your application; i.e. you will get | |
211 # _Dereference instances in places where you thought you should have | |
212 # references to back-referenced data. I am working on making this | |
213 # simpler. | |
214 from twisted.web.microdom import Element | |
215 self.sequence = [] | |
216 i = 0 | |
217 for node in element.childNodes: | |
218 if isinstance(node, Element): | |
219 if node.getAttribute("parent:role") == 'instance': | |
220 unjellier.unjellyAttribute(self, "instance", node) | |
221 else: | |
222 self.sequence.append(None) | |
223 unjellier.unjellyLater(node).addCallback( | |
224 self.gotSequenceItem, i) | |
225 i = i + 1 | |
226 | |
227 def gotSequenceItem(self, seqitem, num): | |
228 self.sequence[num] = seqitem | |
229 | |
230 | |
231 class MarmaladeTestCase(unittest.TestCase): | |
232 | |
233 def testMarmaladeable(self): | |
234 m = Marmaladeable(1, B(), "testing", [1, 2, 3]) | |
235 s = marmalade.jellyToXML(m) | |
236 u = marmalade.unjellyFromXML(s) | |
237 assert u.sequence == [u.instance, u.instance] | |
238 u.sequence.append(u.instance) | |
239 u.jellyDOMVersion = 2 | |
240 s2 = marmalade.jellyToXML(u) | |
241 u2 = marmalade.unjellyFromXML(s2) | |
242 self.assertEquals( u2.sequence, [u2.instance, u2.instance, u2.insta
nce]) | |
243 | |
244 def testCopyReg(self): | |
245 s = "foo_bar" | |
246 sio = StringIO.StringIO() | |
247 sio.write(s) | |
248 assert marmalade.unjellyFromXML(marmalade.jellyToXML({1:sio}))[1].ge
tvalue() == s | |
249 | |
250 def testMethodSelfIdentity(self): | |
251 a = A() | |
252 b = B() | |
253 a.bmethod = b.bmethod | |
254 b.a = a | |
255 im_ = marmalade.unjellyFromXML(marmalade.jellyToXML(b)).a.bmethod | |
256 self.assertEquals(im_.im_class, im_.im_self.__class__) | |
257 | |
258 | |
259 def test_methodNotSelfIdentity(self): | |
260 """ | |
261 If a class change after an instance has been created, | |
262 L{marmalade.unjellyFromXML} shoud raise a C{TypeError} when trying | |
263 to unjelly the instance. | |
264 """ | |
265 a = A() | |
266 b = B() | |
267 a.bmethod = b.bmethod | |
268 b.a = a | |
269 savedbmethod = B.bmethod | |
270 del B.bmethod | |
271 try: | |
272 self.assertRaises(TypeError, marmalade.unjellyFromXML, | |
273 marmalade.jellyToXML(b)) | |
274 finally: | |
275 B.bmethod = savedbmethod | |
276 | |
277 | |
278 def test_unjellyWrongRole(self): | |
279 """ | |
280 When trying to unjelly a dictionnary dump, C{role} attributes | |
281 should have the C{key}. Otherwise, L{marmalade.unjellyFromXML} | |
282 should raise a C{TypeError}. | |
283 """ | |
284 data = marmalade.jellyToXML({"a": 1}) | |
285 data = data.replace('role="key"', 'role="foo"') | |
286 self.assertRaises(TypeError, marmalade.unjellyFromXML, data) | |
287 | |
288 | |
289 def test_unjellyUnknownNodeType(self): | |
290 """ | |
291 L{marmalade.unjellyFromXML} should raise a C{TypeError} when trying | |
292 to unjelly an unknown type. | |
293 """ | |
294 data = marmalade.jellyToXML({}) | |
295 data = data.replace('dictionary', 'unknowntype') | |
296 self.assertRaises(TypeError, marmalade.unjellyFromXML, data) | |
297 | |
298 | |
299 def testBasicIdentity(self): | |
300 # Anyone wanting to make this datastructure more complex, and thus t
his | |
301 # test more comprehensive, is welcome to do so. | |
302 dj = marmalade.DOMJellier().jellyToNode | |
303 d = {'hello': 'world', "method": dj} | |
304 l = [1, 2, 3, | |
305 "he\tllo\n\n\"x world!", | |
306 u"goodbye \n\t\u1010 world!", | |
307 1, 1.0, 100 ** 100l, unittest, marmalade.DOMJellier, d, | |
308 funktion, | |
309 True, False, | |
310 (2, 4, [2]), | |
311 ] | |
312 t = tuple(l) | |
313 l.append(l) | |
314 l.append(t) | |
315 l.append(t) | |
316 uj = marmalade.unjellyFromXML(marmalade.jellyToXML([l, l])) | |
317 assert uj[0] is uj[1] | |
318 assert uj[1][0:5] == l[0:5] | |
319 | |
320 class PicklingTestCase(unittest.TestCase): | |
321 """Test pickling of extra object types.""" | |
322 | |
323 def testModule(self): | |
324 pickl = pickle.dumps(styles) | |
325 o = pickle.loads(pickl) | |
326 self.assertEquals(o, styles) | |
327 | |
328 def testClassMethod(self): | |
329 pickl = pickle.dumps(Pickleable.getX) | |
330 o = pickle.loads(pickl) | |
331 self.assertEquals(o, Pickleable.getX) | |
332 | |
333 def testInstanceMethod(self): | |
334 obj = Pickleable(4) | |
335 pickl = pickle.dumps(obj.getX) | |
336 o = pickle.loads(pickl) | |
337 self.assertEquals(o(), 4) | |
338 self.assertEquals(type(o), type(obj.getX)) | |
339 | |
340 def testStringIO(self): | |
341 f = StringIO.StringIO() | |
342 f.write("abc") | |
343 pickl = pickle.dumps(f) | |
344 o = pickle.loads(pickl) | |
345 self.assertEquals(type(o), type(f)) | |
346 self.assertEquals(f.getvalue(), "abc") | |
347 | |
348 | |
349 class EvilSourceror: | |
350 def __init__(self, x): | |
351 self.a = self | |
352 self.a.b = self | |
353 self.a.b.c = x | |
354 | |
355 class NonDictState: | |
356 def __getstate__(self): | |
357 return self.state | |
358 def __setstate__(self, state): | |
359 self.state = state | |
360 | |
361 class AOTTestCase(unittest.TestCase): | |
362 def testSimpleTypes(self): | |
363 obj = (1, 2.0, 3j, True, slice(1, 2, 3), 'hello', u'world', sys.maxint +
1, None, Ellipsis) | |
364 rtObj = aot.unjellyFromSource(aot.jellyToSource(obj)) | |
365 self.assertEquals(obj, rtObj) | |
366 | |
367 def testMethodSelfIdentity(self): | |
368 a = A() | |
369 b = B() | |
370 a.bmethod = b.bmethod | |
371 b.a = a | |
372 im_ = aot.unjellyFromSource(aot.jellyToSource(b)).a.bmethod | |
373 self.assertEquals(im_.im_class, im_.im_self.__class__) | |
374 | |
375 | |
376 def test_methodNotSelfIdentity(self): | |
377 """ | |
378 If a class change after an instance has been created, | |
379 L{aot.unjellyFromSource} shoud raise a C{TypeError} when trying to | |
380 unjelly the instance. | |
381 """ | |
382 a = A() | |
383 b = B() | |
384 a.bmethod = b.bmethod | |
385 b.a = a | |
386 savedbmethod = B.bmethod | |
387 del B.bmethod | |
388 try: | |
389 self.assertRaises(TypeError, aot.unjellyFromSource, | |
390 aot.jellyToSource(b)) | |
391 finally: | |
392 B.bmethod = savedbmethod | |
393 | |
394 | |
395 def test_unsupportedType(self): | |
396 """ | |
397 L{aot.jellyToSource} should raise a C{TypeError} when trying to jelly | |
398 an unknown type. | |
399 """ | |
400 try: | |
401 set | |
402 except: | |
403 from sets import Set as set | |
404 self.assertRaises(TypeError, aot.jellyToSource, set()) | |
405 | |
406 | |
407 def testBasicIdentity(self): | |
408 # Anyone wanting to make this datastructure more complex, and thus this | |
409 # test more comprehensive, is welcome to do so. | |
410 aj = aot.AOTJellier().jellyToAO | |
411 d = {'hello': 'world', "method": aj} | |
412 l = [1, 2, 3, | |
413 "he\tllo\n\n\"x world!", | |
414 u"goodbye \n\t\u1010 world!", | |
415 1, 1.0, 100 ** 100l, unittest, aot.AOTJellier, d, | |
416 funktion | |
417 ] | |
418 t = tuple(l) | |
419 l.append(l) | |
420 l.append(t) | |
421 l.append(t) | |
422 uj = aot.unjellyFromSource(aot.jellyToSource([l, l])) | |
423 assert uj[0] is uj[1] | |
424 assert uj[1][0:5] == l[0:5] | |
425 | |
426 | |
427 def testNonDictState(self): | |
428 a = NonDictState() | |
429 a.state = "meringue!" | |
430 assert aot.unjellyFromSource(aot.jellyToSource(a)).state == a.state | |
431 | |
432 def testCopyReg(self): | |
433 s = "foo_bar" | |
434 sio = StringIO.StringIO() | |
435 sio.write(s) | |
436 uj = aot.unjellyFromSource(aot.jellyToSource(sio)) | |
437 # print repr(uj.__dict__) | |
438 assert uj.getvalue() == s | |
439 | |
440 def testFunkyReferences(self): | |
441 o = EvilSourceror(EvilSourceror([])) | |
442 j1 = aot.jellyToAOT(o) | |
443 oj = aot.unjellyFromAOT(j1) | |
444 | |
445 assert oj.a is oj | |
446 assert oj.a.b is oj.b | |
447 assert oj.c is not oj.c.c | |
448 | |
449 | |
450 class CrefUtilTestCase(unittest.TestCase): | |
451 """ | |
452 Tests for L{crefutil}. | |
453 """ | |
454 | |
455 def test_dictUnknownKey(self): | |
456 """ | |
457 L{crefutil._DictKeyAndValue} only support keys C{0} and C{1}. | |
458 """ | |
459 d = crefutil._DictKeyAndValue({}) | |
460 self.assertRaises(RuntimeError, d.__setitem__, 2, 3) | |
461 | |
462 | |
463 def test_deferSetMultipleTimes(self): | |
464 """ | |
465 L{crefutil._Defer} can be assigned a key only one time. | |
466 """ | |
467 d = crefutil._Defer() | |
468 d[0] = 1 | |
469 self.assertRaises(RuntimeError, d.__setitem__, 0, 1) | |
470 | |
471 | |
472 | |
473 testCases = [VersionTestCase, EphemeralTestCase, PicklingTestCase] | |
474 | |
OLD | NEW |