# Copyright (c) 2001-2004 Twisted Matrix Laboratories. # See LICENSE for details. """Test cases for 'jelly' object serialization. """ import types from twisted.trial import unittest from twisted.spread import newjelly, pb class A: """ dummy class """ def amethod(self): pass def afunc(self): pass class B: """ dummy class """ def bmethod(self): pass class C: """ dummy class """ def cmethod(self): pass class D(object): """ newstyle class """ class SimpleJellyTest: def __init__(self, x, y): self.x = x self.y = y def isTheSameAs(self, other): return self.__dict__ == other.__dict__ try: object haveObject = 0 # 1 # more work to be done before this really works except: haveObject = 0 else: class NewStyle(object): pass try: import datetime haveDatetime = 1 except ImportError: haveDatetime = 0 class JellyTestCase(unittest.TestCase): """ testcases for `jelly' module serialization. """ jc = newjelly def testMethodSelfIdentity(self): a = A() b = B() a.bmethod = b.bmethod b.a = a im_ = self.jc.unjelly(self.jc.jelly(b)).a.bmethod self.assertEquals(im_.im_class, im_.im_self.__class__) if haveObject: def testNewStyle(self): n = NewStyle() n.x = 1 n2 = NewStyle() n.n2 = n2 n.n3 = n2 c = self.jc.jelly(n) m = self.jc.unjelly(c) self.failUnless(isinstance(m, NewStyle)) self.assertIdentical(m.n2, m.n3) if haveDatetime: def testDateTime(self): dtn = datetime.datetime.now() dtd = datetime.datetime.now() - dtn input = [dtn, dtd] c = self.jc.jelly(input) output = self.jc.unjelly(c) self.assertEquals(input, output) self.assertNotIdentical(input, output) testDateTime.todo = 'Newjelly does not support datetime yet.' def testSimple(self): """ simplest test case """ self.failUnless(SimpleJellyTest('a', 'b').isTheSameAs(SimpleJellyTest('a', 'b'))) a = SimpleJellyTest(1, 2) cereal = self.jc.jelly(a) b = self.jc.unjelly(cereal) self.failUnless(a.isTheSameAs(b)) def testIdentity(self): """ test to make sure that objects retain identity properly """ x = [] y = (x) x.append(y) x.append(y) self.assertIdentical(x[0], x[1]) self.assertIdentical(x[0][0], x) s = self.jc.jelly(x) z = self.jc.unjelly(s) self.assertIdentical(z[0], z[1]) self.assertIdentical(z[0][0], z) def testUnicode(self): if hasattr(types, 'UnicodeType'): x = unicode('blah') y = self.jc.unjelly(self.jc.jelly(x)) self.assertEquals(x, y) self.assertEquals(type(x), type(y)) def testStressReferences(self): reref = [] toplevelTuple = ({'list': reref}, reref) reref.append(toplevelTuple) s = self.jc.jelly(toplevelTuple) z = self.jc.unjelly(s) self.assertIdentical(z[0]['list'], z[1]) self.assertIdentical(z[0]['list'][0], z) def testMoreReferences(self): a = [] t = (a,) a.append((t,)) s = self.jc.jelly(t) z = self.jc.unjelly(s) self.assertIdentical(z[0][0][0], z) def testTypeSecurity(self): """ test for type-level security of serialization """ taster = self.jc.SecurityOptions() dct = self.jc.jelly({}) try: self.jc.unjelly(dct, taster) self.fail("Insecure Jelly unjellied successfully.") except self.jc.InsecureJelly: # OK, works pass def testNewStyleClasses(self): j = self.jc.jelly(D) uj = self.jc.unjelly(D) self.assertIdentical(D, uj) def testLotsaTypes(self): """ test for all types currently supported in jelly """ a = A() self.jc.unjelly(self.jc.jelly(a)) self.jc.unjelly(self.jc.jelly(a.amethod)) items = [afunc, [1, 2, 3], not bool(1), bool(1), 'test', 20.3, (1,2,3), None, A, unittest, {'a':1}, A.amethod] for i in items: self.assertEquals(i, self.jc.unjelly(self.jc.jelly(i))) def testSetState(self): global TupleState class TupleState: def __init__(self, other): self.other = other def __getstate__(self): return (self.other,) def __setstate__(self, state): self.other = state[0] def __hash__(self): return hash(self.other) a = A() t1 = TupleState(a) t2 = TupleState(a) t3 = TupleState((t1, t2)) d = {t1: t1, t2: t2, t3: t3, "t3": t3} t3prime = self.jc.unjelly(self.jc.jelly(d))["t3"] self.assertIdentical(t3prime.other[0].other, t3prime.other[1].other) def testClassSecurity(self): """ test for class-level security of serialization """ taster = self.jc.SecurityOptions() taster.allowInstancesOf(A, B) a = A() b = B() c = C() # add a little complexity to the data a.b = b a.c = c # and a backreference a.x = b b.c = c # first, a friendly insecure serialization friendly = self.jc.jelly(a, taster) x = self.jc.unjelly(friendly, taster) self.failUnless(isinstance(x.c, self.jc.Unpersistable), "C came back: %s" % x.c.__class__) # now, a malicious one mean = self.jc.jelly(a) try: x = self.jc.unjelly(mean, taster) self.fail("x came back: %s" % x) except self.jc.InsecureJelly: # OK pass self.assertIdentical(x.x, x.b, "Identity mismatch") #test class serialization friendly = self.jc.jelly(A, taster) x = self.jc.unjelly(friendly, taster) self.assertIdentical(x, A, "A came back: %s" % x) class ClassA(pb.Copyable, pb.RemoteCopy): def __init__(self): self.ref = ClassB(self) class ClassB(pb.Copyable, pb.RemoteCopy): def __init__(self, ref): self.ref = ref class CircularReferenceTestCase(unittest.TestCase): jc = newjelly def testSimpleCircle(self): self.jc.setUnjellyableForClass(ClassA, ClassA) self.jc.setUnjellyableForClass(ClassB, ClassB) a = self.jc.unjelly(self.jc.jelly(ClassA())) self.failUnless(a.ref.ref is a, "Identity not preserved in circular reference") def testCircleWithInvoker(self): class dummyInvokerClass: pass dummyInvoker = dummyInvokerClass() dummyInvoker.serializingPerspective = None a0 = ClassA() self.jc.setUnjellyableForClass(ClassA, ClassA) self.jc.setUnjellyableForClass(ClassB, ClassB) j = self.jc.jelly(a0, invoker=dummyInvoker) a1 = self.jc.unjelly(j) self.failUnlessIdentical(a1.ref.ref, a1, "Identity not preserved in circular reference") testCases = [JellyTestCase, CircularReferenceTestCase]