1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
5 Tests for loading tests by name.
12 from twisted.python import util
13 from twisted.python.hashlib import md5
14 from twisted.trial.test import packages
15 from twisted.trial import runner, reporter, unittest
16 from twisted.trial.itrial import ITestCase
18 from twisted.python.modules import getModule
24 Return the id of each test within the given test suite or case.
27 for test in unittest._iterateTests(tests):
28 names.append(test.id())
33 class FinderTest(packages.PackageTest):
35 packages.PackageTest.setUp(self)
36 self.loader = runner.TestLoader()
39 packages.PackageTest.tearDown(self)
41 def test_findPackage(self):
42 sample1 = self.loader.findByName('twisted')
43 import twisted as sample2
44 self.assertEqual(sample1, sample2)
46 def test_findModule(self):
47 sample1 = self.loader.findByName('twisted.trial.test.sample')
48 import sample as sample2
49 self.assertEqual(sample1, sample2)
51 def test_findFile(self):
52 path = util.sibpath(__file__, 'sample.py')
53 sample1 = self.loader.findByName(path)
54 import sample as sample2
55 self.assertEqual(sample1, sample2)
57 def test_findObject(self):
58 sample1 = self.loader.findByName('twisted.trial.test.sample.FooTest')
60 self.assertEqual(sample.FooTest, sample1)
62 def test_findNonModule(self):
63 self.failUnlessRaises(AttributeError,
64 self.loader.findByName,
65 'twisted.trial.test.nonexistent')
67 def test_findNonPackage(self):
68 self.failUnlessRaises(ValueError,
69 self.loader.findByName,
72 def test_findNonFile(self):
73 path = util.sibpath(__file__, 'nonexistent.py')
74 self.failUnlessRaises(ValueError, self.loader.findByName, path)
78 class FileTest(packages.SysPathManglingTest):
80 Tests for L{runner.filenameToModule}.
82 def test_notFile(self):
83 self.failUnlessRaises(ValueError,
84 runner.filenameToModule, 'doesntexist')
86 def test_moduleInPath(self):
87 sample1 = runner.filenameToModule(util.sibpath(__file__, 'sample.py'))
88 import sample as sample2
89 self.assertEqual(sample2, sample1)
92 def test_moduleNotInPath(self):
94 If passed the path to a file containing the implementation of a
95 module within a package which is not on the import path,
96 L{runner.filenameToModule} returns a module object loosely
97 resembling the module defined by that file anyway.
99 # "test_sample" isn't actually the name of this module. However,
100 # filenameToModule can't seem to figure that out. So clean up this
101 # mis-named module. It would be better if this weren't necessary
102 # and filenameToModule either didn't exist or added a correctly
103 # named module to sys.modules.
104 self.addCleanup(sys.modules.pop, 'test_sample', None)
106 self.mangleSysPath(self.oldPath)
107 sample1 = runner.filenameToModule(
108 os.path.join(self.parent, 'goodpackage', 'test_sample.py'))
109 self.mangleSysPath(self.newPath)
110 from goodpackage import test_sample as sample2
111 self.assertEqual(os.path.splitext(sample2.__file__)[0],
112 os.path.splitext(sample1.__file__)[0])
115 def test_packageInPath(self):
116 package1 = runner.filenameToModule(os.path.join(self.parent,
119 self.assertEqual(goodpackage, package1)
122 def test_packageNotInPath(self):
124 If passed the path to a directory which represents a package which
125 is not on the import path, L{runner.filenameToModule} returns a
126 module object loosely resembling the package defined by that
129 # "__init__" isn't actually the name of the package! However,
130 # filenameToModule is pretty stupid and decides that is its name
131 # after all. Make sure it gets cleaned up. See the comment in
132 # test_moduleNotInPath for possible courses of action related to
134 self.addCleanup(sys.modules.pop, "__init__")
136 self.mangleSysPath(self.oldPath)
137 package1 = runner.filenameToModule(
138 os.path.join(self.parent, 'goodpackage'))
139 self.mangleSysPath(self.newPath)
141 self.assertEqual(os.path.splitext(goodpackage.__file__)[0],
142 os.path.splitext(package1.__file__)[0])
145 def test_directoryNotPackage(self):
146 self.failUnlessRaises(ValueError, runner.filenameToModule,
147 util.sibpath(__file__, 'directory'))
149 def test_filenameNotPython(self):
150 self.failUnlessRaises(ValueError, runner.filenameToModule,
151 util.sibpath(__file__, 'notpython.py'))
153 def test_filenameMatchesPackage(self):
154 filename = os.path.join(self.parent, 'goodpackage.py')
155 fd = open(filename, 'w')
156 fd.write(packages.testModule)
159 module = runner.filenameToModule(filename)
160 self.assertEqual(filename, module.__file__)
164 def test_directory(self):
166 Test loader against a filesystem directory. It should handle
167 'path' and 'path/' the same way.
169 path = util.sibpath(__file__, 'goodDirectory')
171 f = file(os.path.join(path, '__init__.py'), "w")
174 module = runner.filenameToModule(path)
175 self.assert_(module.__name__.endswith('goodDirectory'))
176 module = runner.filenameToModule(path + os.path.sep)
177 self.assert_(module.__name__.endswith('goodDirectory'))
183 class LoaderTest(packages.SysPathManglingTest):
185 Tests for L{trial.TestLoader}.
189 self.loader = runner.TestLoader()
190 packages.SysPathManglingTest.setUp(self)
193 def test_sortCases(self):
195 suite = self.loader.loadClass(sample.AlphabetTest)
196 self.assertEqual(['test_a', 'test_b', 'test_c'],
197 [test._testMethodName for test in suite._tests])
198 newOrder = ['test_b', 'test_c', 'test_a']
199 sortDict = dict(zip(newOrder, range(3)))
200 self.loader.sorter = lambda x : sortDict.get(x.shortDescription(), -1)
201 suite = self.loader.loadClass(sample.AlphabetTest)
202 self.assertEqual(newOrder,
203 [test._testMethodName for test in suite._tests])
206 def test_loadMethod(self):
208 suite = self.loader.loadMethod(sample.FooTest.test_foo)
209 self.assertEqual(1, suite.countTestCases())
210 self.assertEqual('test_foo', suite._testMethodName)
213 def test_loadFailingMethod(self):
214 # test added for issue1353
216 suite = self.loader.loadMethod(erroneous.TestRegularFail.test_fail)
217 result = reporter.TestResult()
219 self.assertEqual(result.testsRun, 1)
220 self.assertEqual(len(result.failures), 1)
223 def test_loadNonMethod(self):
225 self.failUnlessRaises(TypeError, self.loader.loadMethod, sample)
226 self.failUnlessRaises(TypeError,
227 self.loader.loadMethod, sample.FooTest)
228 self.failUnlessRaises(TypeError, self.loader.loadMethod, "string")
229 self.failUnlessRaises(TypeError,
230 self.loader.loadMethod, ('foo', 'bar'))
233 def test_loadBadDecorator(self):
235 A decorated test method for which the decorator has failed to set the
236 method's __name__ correctly is loaded and its name in the class scope
240 suite = self.loader.loadMethod(sample.DecorationTest.test_badDecorator)
241 self.assertEqual(1, suite.countTestCases())
242 self.assertEqual('test_badDecorator', suite._testMethodName)
245 def test_loadGoodDecorator(self):
247 A decorated test method for which the decorator has set the method's
248 __name__ correctly is loaded and the only name by which it goes is used.
251 suite = self.loader.loadMethod(
252 sample.DecorationTest.test_goodDecorator)
253 self.assertEqual(1, suite.countTestCases())
254 self.assertEqual('test_goodDecorator', suite._testMethodName)
257 def test_loadRenamedDecorator(self):
259 Load a decorated method which has been copied to a new name inside the
260 class. Thus its __name__ and its key in the class's __dict__ no
264 suite = self.loader.loadMethod(
265 sample.DecorationTest.test_renamedDecorator)
266 self.assertEqual(1, suite.countTestCases())
267 self.assertEqual('test_renamedDecorator', suite._testMethodName)
270 def test_loadClass(self):
272 suite = self.loader.loadClass(sample.FooTest)
273 self.assertEqual(2, suite.countTestCases())
274 self.assertEqual(['test_bar', 'test_foo'],
275 [test._testMethodName for test in suite._tests])
278 def test_loadNonClass(self):
280 self.failUnlessRaises(TypeError, self.loader.loadClass, sample)
281 self.failUnlessRaises(TypeError,
282 self.loader.loadClass, sample.FooTest.test_foo)
283 self.failUnlessRaises(TypeError, self.loader.loadClass, "string")
284 self.failUnlessRaises(TypeError,
285 self.loader.loadClass, ('foo', 'bar'))
288 def test_loadNonTestCase(self):
290 self.failUnlessRaises(ValueError, self.loader.loadClass,
294 def test_loadModule(self):
296 suite = self.loader.loadModule(sample)
297 self.assertEqual(10, suite.countTestCases())
300 def test_loadNonModule(self):
302 self.failUnlessRaises(TypeError,
303 self.loader.loadModule, sample.FooTest)
304 self.failUnlessRaises(TypeError,
305 self.loader.loadModule, sample.FooTest.test_foo)
306 self.failUnlessRaises(TypeError, self.loader.loadModule, "string")
307 self.failUnlessRaises(TypeError,
308 self.loader.loadModule, ('foo', 'bar'))
311 def test_loadPackage(self):
313 suite = self.loader.loadPackage(goodpackage)
314 self.assertEqual(7, suite.countTestCases())
317 def test_loadNonPackage(self):
319 self.failUnlessRaises(TypeError,
320 self.loader.loadPackage, sample.FooTest)
321 self.failUnlessRaises(TypeError,
322 self.loader.loadPackage, sample.FooTest.test_foo)
323 self.failUnlessRaises(TypeError, self.loader.loadPackage, "string")
324 self.failUnlessRaises(TypeError,
325 self.loader.loadPackage, ('foo', 'bar'))
328 def test_loadModuleAsPackage(self):
330 ## XXX -- should this instead raise a ValueError? -- jml
331 self.failUnlessRaises(TypeError, self.loader.loadPackage, sample)
334 def test_loadPackageRecursive(self):
336 suite = self.loader.loadPackage(goodpackage, recurse=True)
337 self.assertEqual(14, suite.countTestCases())
340 def test_loadAnythingOnModule(self):
342 suite = self.loader.loadAnything(sample)
343 self.assertEqual(sample.__name__,
344 suite._tests[0]._tests[0].__class__.__module__)
347 def test_loadAnythingOnClass(self):
349 suite = self.loader.loadAnything(sample.FooTest)
350 self.assertEqual(2, suite.countTestCases())
353 def test_loadAnythingOnMethod(self):
355 suite = self.loader.loadAnything(sample.FooTest.test_foo)
356 self.assertEqual(1, suite.countTestCases())
359 def test_loadAnythingOnPackage(self):
361 suite = self.loader.loadAnything(goodpackage)
362 self.failUnless(isinstance(suite, self.loader.suiteFactory))
363 self.assertEqual(7, suite.countTestCases())
366 def test_loadAnythingOnPackageRecursive(self):
368 suite = self.loader.loadAnything(goodpackage, recurse=True)
369 self.failUnless(isinstance(suite, self.loader.suiteFactory))
370 self.assertEqual(14, suite.countTestCases())
373 def test_loadAnythingOnString(self):
374 # the important thing about this test is not the string-iness
375 # but the non-handledness.
376 self.failUnlessRaises(TypeError,
377 self.loader.loadAnything, "goodpackage")
380 def test_importErrors(self):
382 suite = self.loader.loadPackage(package, recurse=True)
383 result = reporter.Reporter()
385 self.assertEqual(False, result.wasSuccessful())
386 self.assertEqual(2, len(result.errors))
387 errors = [test.id() for test, error in result.errors]
389 self.assertEqual(errors, ['package.test_bad_module',
390 'package.test_import_module'])
393 def test_differentInstances(self):
395 L{TestLoader.loadClass} returns a suite with each test method
396 represented by a different instances of the L{TestCase} they are
399 class DistinctInstances(unittest.TestCase):
401 self.first = 'test1Run'
404 self.assertFalse(hasattr(self, 'first'))
406 suite = self.loader.loadClass(DistinctInstances)
407 result = reporter.Reporter()
409 self.assertTrue(result.wasSuccessful())
412 def test_loadModuleWith_test_suite(self):
414 Check that C{test_suite} is used when present and other L{TestCase}s are
417 from twisted.trial.test import mockcustomsuite
418 suite = self.loader.loadModule(mockcustomsuite)
419 self.assertEqual(0, suite.countTestCases())
420 self.assertEqual("MyCustomSuite", getattr(suite, 'name', None))
423 def test_loadModuleWith_testSuite(self):
425 Check that C{testSuite} is used when present and other L{TestCase}s are
428 from twisted.trial.test import mockcustomsuite2
429 suite = self.loader.loadModule(mockcustomsuite2)
430 self.assertEqual(0, suite.countTestCases())
431 self.assertEqual("MyCustomSuite", getattr(suite, 'name', None))
434 def test_loadModuleWithBothCustom(self):
436 Check that if C{testSuite} and C{test_suite} are both present in a
437 module then C{testSuite} gets priority.
439 from twisted.trial.test import mockcustomsuite3
440 suite = self.loader.loadModule(mockcustomsuite3)
441 self.assertEqual('testSuite', getattr(suite, 'name', None))
444 def test_customLoadRaisesAttributeError(self):
446 Make sure that any C{AttributeError}s raised by C{testSuite} are not
447 swallowed by L{TestLoader}.
450 raise AttributeError('should be reraised')
451 from twisted.trial.test import mockcustomsuite2
452 mockcustomsuite2.testSuite, original = (testSuite,
453 mockcustomsuite2.testSuite)
455 self.assertRaises(AttributeError, self.loader.loadModule,
458 mockcustomsuite2.testSuite = original
461 # XXX - duplicated and modified from test_script
462 def assertSuitesEqual(self, test1, test2):
463 names1 = testNames(test1)
464 names2 = testNames(test2)
467 self.assertEqual(names1, names2)
470 def test_loadByNamesDuplicate(self):
472 Check that loadByNames ignores duplicate names
474 module = 'twisted.trial.test.test_test_visitor'
475 suite1 = self.loader.loadByNames([module, module], True)
476 suite2 = self.loader.loadByName(module, True)
477 self.assertSuitesEqual(suite1, suite2)
480 def test_loadDifferentNames(self):
482 Check that loadByNames loads all the names that it is given
484 modules = ['goodpackage', 'package.test_module']
485 suite1 = self.loader.loadByNames(modules)
486 suite2 = runner.TestSuite(map(self.loader.loadByName, modules))
487 self.assertSuitesEqual(suite1, suite2)
489 def test_loadInheritedMethods(self):
491 Check that test methods names which are inherited from are all
492 loaded rather than just one.
494 methods = ['inheritancepackage.test_x.A.test_foo',
495 'inheritancepackage.test_x.B.test_foo']
496 suite1 = self.loader.loadByNames(methods)
497 suite2 = runner.TestSuite(map(self.loader.loadByName, methods))
498 self.assertSuitesEqual(suite1, suite2)
502 class ZipLoadingTest(LoaderTest):
504 from twisted.test.test_paths import zipit
505 LoaderTest.setUp(self)
506 zipit(self.parent, self.parent+'.zip')
507 self.parent += '.zip'
508 self.mangleSysPath(self.oldPath+[self.parent])
512 class PackageOrderingTest(packages.SysPathManglingTest):
513 if sys.version_info < (2, 4):
515 "Python 2.3 import semantics make this behavior incorrect on that "
516 "version of Python as well as difficult to test. The second "
517 "import of a package which raised an exception the first time it "
518 "was imported will succeed on Python 2.3, whereas it will fail on "
519 "later versions of Python. Trial does not account for this, so "
520 "this test fails with inconsistencies between the expected and "
521 "the received loader errors.")
524 self.loader = runner.TestLoader()
525 self.topDir = self.mktemp()
526 parent = os.path.join(self.topDir, "uberpackage")
528 file(os.path.join(parent, "__init__.py"), "wb").close()
529 packages.SysPathManglingTest.setUp(self, parent)
530 self.mangleSysPath(self.oldPath + [self.topDir])
532 def _trialSortAlgorithm(self, sorter):
534 Right now, halfway by accident, trial sorts like this:
536 1. all modules are grouped together in one list and sorted.
538 2. within each module, the classes are grouped together in one list
541 3. finally within each class, each test method is grouped together
542 in a list and sorted.
544 This attempts to return a sorted list of testable thingies following
545 those rules, so that we can compare the behavior of loadPackage.
547 The things that show as 'cases' are errors from modules which failed to
548 import, and test methods. Let's gather all those together.
550 pkg = getModule('uberpackage')
552 for testModule in pkg.walkModules():
553 if testModule.name.split(".")[-1].startswith("test_"):
554 testModules.append(testModule)
555 sortedModules = sorted(testModules, key=sorter) # ONE
556 for modinfo in sortedModules:
557 # Now let's find all the classes.
558 module = modinfo.load(None)
563 for attrib in modinfo.iterAttributes():
564 if runner.isTestCase(attrib.load()):
565 testClasses.append(attrib)
566 sortedClasses = sorted(testClasses, key=sorter) # TWO
567 for clsinfo in sortedClasses:
569 for attr in clsinfo.iterAttributes():
570 if attr.name.split(".")[-1].startswith('test'):
571 testMethods.append(attr)
572 sortedMethods = sorted(testMethods, key=sorter) # THREE
573 for methinfo in sortedMethods:
577 def loadSortedPackages(self, sorter=runner.name):
579 Verify that packages are loaded in the correct order.
582 self.loader.sorter = sorter
583 suite = self.loader.loadPackage(uberpackage, recurse=True)
584 # XXX: Work around strange, unexplained Zope crap.
586 suite = unittest.decorate(suite, ITestCase)
587 resultingTests = list(unittest._iterateTests(suite))
588 manifest = list(self._trialSortAlgorithm(sorter))
589 for number, (manifestTest, actualTest) in enumerate(
590 zip(manifest, resultingTests)):
592 manifestTest.name, actualTest.id(),
594 (number, manifestTest.name, actualTest.id()))
595 self.assertEqual(len(manifest), len(resultingTests))
598 def test_sortPackagesDefaultOrder(self):
599 self.loadSortedPackages()
602 def test_sortPackagesSillyOrder(self):
604 # This has to work on fully-qualified class names and class
605 # objects, which is silly, but it's the "spec", such as it is.
606 # if isinstance(s, type) or isinstance(s, types.ClassType):
607 # return s.__module__+'.'+s.__name__
609 d = md5(n).hexdigest()
611 self.loadSortedPackages(sillySorter)