| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2001-2007 Twisted Matrix Laboratories. | |
| 2 # See LICENSE for details. | |
| 3 | |
| 4 | |
| 5 import sys, os | |
| 6 import new | |
| 7 | |
| 8 from twisted.trial import unittest | |
| 9 from twisted.python import rebuild | |
| 10 | |
| 11 import crash_test_dummy | |
| 12 f = crash_test_dummy.foo | |
| 13 | |
| 14 class Foo: pass | |
| 15 class Bar(Foo): pass | |
| 16 class Baz(object): pass | |
| 17 class Buz(Bar, Baz): pass | |
| 18 | |
| 19 class RebuildTestCase(unittest.TestCase): | |
| 20 """ | |
| 21 Simple testcase for rebuilding, to at least exercise the code. | |
| 22 """ | |
| 23 def setUp(self): | |
| 24 self.libPath = self.mktemp() | |
| 25 os.mkdir(self.libPath) | |
| 26 self.fakelibPath = os.path.join(self.libPath, 'twisted_rebuild_fakelib') | |
| 27 os.mkdir(self.fakelibPath) | |
| 28 file(os.path.join(self.fakelibPath, '__init__.py'), 'w').close() | |
| 29 sys.path.insert(0, self.libPath) | |
| 30 | |
| 31 def tearDown(self): | |
| 32 sys.path.remove(self.libPath) | |
| 33 | |
| 34 def testFileRebuild(self): | |
| 35 from twisted.python.util import sibpath | |
| 36 import shutil, time | |
| 37 shutil.copyfile(sibpath(__file__, "myrebuilder1.py"), | |
| 38 os.path.join(self.fakelibPath, "myrebuilder.py")) | |
| 39 from twisted_rebuild_fakelib import myrebuilder | |
| 40 a = myrebuilder.A() | |
| 41 try: | |
| 42 object | |
| 43 except NameError: | |
| 44 pass | |
| 45 else: | |
| 46 from twisted.test import test_rebuild | |
| 47 b = myrebuilder.B() | |
| 48 class C(myrebuilder.B): | |
| 49 pass | |
| 50 test_rebuild.C = C | |
| 51 c = C() | |
| 52 i = myrebuilder.Inherit() | |
| 53 self.assertEquals(a.a(), 'a') | |
| 54 # necessary because the file has not "changed" if a second has not gone | |
| 55 # by in unix. This sucks, but it's not often that you'll be doing more | |
| 56 # than one reload per second. | |
| 57 time.sleep(1.1) | |
| 58 shutil.copyfile(sibpath(__file__, "myrebuilder2.py"), | |
| 59 os.path.join(self.fakelibPath, "myrebuilder.py")) | |
| 60 rebuild.rebuild(myrebuilder) | |
| 61 try: | |
| 62 object | |
| 63 except NameError: | |
| 64 pass | |
| 65 else: | |
| 66 b2 = myrebuilder.B() | |
| 67 self.assertEquals(b2.b(), 'c') | |
| 68 self.assertEquals(b.b(), 'c') | |
| 69 self.assertEquals(i.a(), 'd') | |
| 70 self.assertEquals(a.a(), 'b') | |
| 71 # more work to be done on new-style classes | |
| 72 # self.assertEquals(c.b(), 'c') | |
| 73 | |
| 74 def testRebuild(self): | |
| 75 """ | |
| 76 Rebuilding an unchanged module. | |
| 77 """ | |
| 78 # This test would actually pass if rebuild was a no-op, but it | |
| 79 # ensures rebuild doesn't break stuff while being a less | |
| 80 # complex test than testFileRebuild. | |
| 81 | |
| 82 x = crash_test_dummy.X('a') | |
| 83 | |
| 84 rebuild.rebuild(crash_test_dummy, doLog=False) | |
| 85 # Instance rebuilding is triggered by attribute access. | |
| 86 x.do() | |
| 87 self.failUnlessIdentical(x.__class__, crash_test_dummy.X) | |
| 88 | |
| 89 self.failUnlessIdentical(f, crash_test_dummy.foo) | |
| 90 | |
| 91 def testComponentInteraction(self): | |
| 92 x = crash_test_dummy.XComponent() | |
| 93 x.setAdapter(crash_test_dummy.IX, crash_test_dummy.XA) | |
| 94 oldComponent = x.getComponent(crash_test_dummy.IX) | |
| 95 rebuild.rebuild(crash_test_dummy, 0) | |
| 96 newComponent = x.getComponent(crash_test_dummy.IX) | |
| 97 | |
| 98 newComponent.method() | |
| 99 | |
| 100 self.assertEquals(newComponent.__class__, crash_test_dummy.XA) | |
| 101 | |
| 102 # Test that a duplicate registerAdapter is not allowed | |
| 103 from twisted.python import components | |
| 104 self.failUnlessRaises(ValueError, components.registerAdapter, | |
| 105 crash_test_dummy.XA, crash_test_dummy.X, | |
| 106 crash_test_dummy.IX) | |
| 107 | |
| 108 def testUpdateInstance(self): | |
| 109 global Foo, Buz | |
| 110 | |
| 111 b = Buz() | |
| 112 | |
| 113 class Foo: | |
| 114 def foo(self): | |
| 115 pass | |
| 116 class Buz(Bar, Baz): | |
| 117 x = 10 | |
| 118 | |
| 119 rebuild.updateInstance(b) | |
| 120 assert hasattr(b, 'foo'), "Missing method on rebuilt instance" | |
| 121 assert hasattr(b, 'x'), "Missing class attribute on rebuilt instance" | |
| 122 | |
| 123 def testBananaInteraction(self): | |
| 124 from twisted.python import rebuild | |
| 125 from twisted.spread import banana | |
| 126 rebuild.latestClass(banana.Banana) | |
| 127 | |
| 128 | |
| 129 | |
| 130 class NewStyleTestCase(unittest.TestCase): | |
| 131 """ | |
| 132 Tests for rebuilding new-style classes of various sorts. | |
| 133 """ | |
| 134 def setUp(self): | |
| 135 self.m = new.module('whipping') | |
| 136 sys.modules['whipping'] = self.m | |
| 137 | |
| 138 | |
| 139 def tearDown(self): | |
| 140 del sys.modules['whipping'] | |
| 141 del self.m | |
| 142 | |
| 143 | |
| 144 def test_slots(self): | |
| 145 """ | |
| 146 Try to rebuild a new style class with slots defined. | |
| 147 """ | |
| 148 classDefinition = ( | |
| 149 "class SlottedClass(object):\n" | |
| 150 " __slots__ = ['a']\n") | |
| 151 | |
| 152 exec classDefinition in self.m.__dict__ | |
| 153 inst = self.m.SlottedClass() | |
| 154 inst.a = 7 | |
| 155 exec classDefinition in self.m.__dict__ | |
| 156 rebuild.updateInstance(inst) | |
| 157 self.assertEqual(inst.a, 7) | |
| 158 self.assertIdentical(type(inst), self.m.SlottedClass) | |
| 159 | |
| 160 if sys.version_info < (2, 6): | |
| 161 test_slots.skip = "__class__ assignment for class with slots is only ava
ilable starting Python 2.6" | |
| 162 | |
| 163 | |
| 164 def test_errorSlots(self): | |
| 165 """ | |
| 166 Try to rebuild a new style class with slots defined: this should fail. | |
| 167 """ | |
| 168 classDefinition = ( | |
| 169 "class SlottedClass(object):\n" | |
| 170 " __slots__ = ['a']\n") | |
| 171 | |
| 172 exec classDefinition in self.m.__dict__ | |
| 173 inst = self.m.SlottedClass() | |
| 174 inst.a = 7 | |
| 175 exec classDefinition in self.m.__dict__ | |
| 176 self.assertRaises(rebuild.RebuildError, rebuild.updateInstance, inst) | |
| 177 | |
| 178 if sys.version_info >= (2, 6): | |
| 179 test_errorSlots.skip = "__class__ assignment for class with slots should
work starting Python 2.6" | |
| 180 | |
| 181 | |
| 182 def test_typeSubclass(self): | |
| 183 """ | |
| 184 Try to rebuild a base type subclass. | |
| 185 """ | |
| 186 classDefinition = ( | |
| 187 "class ListSubclass(list):\n" | |
| 188 " pass\n") | |
| 189 | |
| 190 exec classDefinition in self.m.__dict__ | |
| 191 inst = self.m.ListSubclass() | |
| 192 inst.append(2) | |
| 193 exec classDefinition in self.m.__dict__ | |
| 194 rebuild.updateInstance(inst) | |
| 195 self.assertEqual(inst[0], 2) | |
| 196 self.assertIdentical(type(inst), self.m.ListSubclass) | |
| 197 | |
| 198 | |
| 199 def test_instanceSlots(self): | |
| 200 """ | |
| 201 Test that when rebuilding an instance with a __slots__ attribute, it | |
| 202 fails accurately instead of giving a L{rebuild.RebuildError}. | |
| 203 """ | |
| 204 classDefinition = ( | |
| 205 "class NotSlottedClass(object):\n" | |
| 206 " pass\n") | |
| 207 | |
| 208 exec classDefinition in self.m.__dict__ | |
| 209 inst = self.m.NotSlottedClass() | |
| 210 inst.__slots__ = ['a'] | |
| 211 classDefinition = ( | |
| 212 "class NotSlottedClass:\n" | |
| 213 " pass\n") | |
| 214 exec classDefinition in self.m.__dict__ | |
| 215 # Moving from new-style class to old-style should fail. | |
| 216 self.assertRaises(TypeError, rebuild.updateInstance, inst) | |
| 217 | |
| OLD | NEW |