1
# Copyright (c) 2008-2009 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.failUnlessEqual(sample1, sample2)
46
def test_findModule(self):
47
sample1 = self.loader.findByName('twisted.trial.test.sample')
48
import sample as sample2
49
self.failUnlessEqual(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.failUnlessEqual(sample1, sample2)
57
def test_findObject(self):
58
sample1 = self.loader.findByName('twisted.trial.test.sample.FooTest')
60
self.failUnlessEqual(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.failUnlessEqual(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.failUnlessEqual(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.failUnlessEqual(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.failUnlessEqual(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.failUnlessEqual(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):
186
self.loader = runner.TestLoader()
187
packages.SysPathManglingTest.setUp(self)
189
def test_sortCases(self):
191
suite = self.loader.loadClass(sample.AlphabetTest)
192
self.failUnlessEqual(['test_a', 'test_b', 'test_c'],
193
[test._testMethodName for test in suite._tests])
194
newOrder = ['test_b', 'test_c', 'test_a']
195
sortDict = dict(zip(newOrder, range(3)))
196
self.loader.sorter = lambda x : sortDict.get(x.shortDescription(), -1)
197
suite = self.loader.loadClass(sample.AlphabetTest)
198
self.failUnlessEqual(newOrder,
199
[test._testMethodName for test in suite._tests])
201
def test_loadMethod(self):
203
suite = self.loader.loadMethod(sample.FooTest.test_foo)
204
self.failUnlessEqual(1, suite.countTestCases())
205
self.failUnlessEqual('test_foo', suite._testMethodName)
207
def test_loadFailingMethod(self):
208
# test added for issue1353
210
suite = self.loader.loadMethod(erroneous.TestRegularFail.test_fail)
211
result = reporter.TestResult()
213
self.failUnlessEqual(result.testsRun, 1)
214
self.failUnlessEqual(len(result.failures), 1)
216
def test_loadNonMethod(self):
218
self.failUnlessRaises(TypeError, self.loader.loadMethod, sample)
219
self.failUnlessRaises(TypeError,
220
self.loader.loadMethod, sample.FooTest)
221
self.failUnlessRaises(TypeError, self.loader.loadMethod, "string")
222
self.failUnlessRaises(TypeError,
223
self.loader.loadMethod, ('foo', 'bar'))
225
def test_loadClass(self):
227
suite = self.loader.loadClass(sample.FooTest)
228
self.failUnlessEqual(2, suite.countTestCases())
229
self.failUnlessEqual(['test_bar', 'test_foo'],
230
[test._testMethodName for test in suite._tests])
233
def test_loadNonClass(self):
235
self.failUnlessRaises(TypeError, self.loader.loadClass, sample)
236
self.failUnlessRaises(TypeError,
237
self.loader.loadClass, sample.FooTest.test_foo)
238
self.failUnlessRaises(TypeError, self.loader.loadClass, "string")
239
self.failUnlessRaises(TypeError,
240
self.loader.loadClass, ('foo', 'bar'))
242
def test_loadNonTestCase(self):
244
self.failUnlessRaises(ValueError, self.loader.loadClass,
247
def test_loadModule(self):
249
suite = self.loader.loadModule(sample)
250
self.failUnlessEqual(7, suite.countTestCases())
252
def test_loadNonModule(self):
254
self.failUnlessRaises(TypeError,
255
self.loader.loadModule, sample.FooTest)
256
self.failUnlessRaises(TypeError,
257
self.loader.loadModule, sample.FooTest.test_foo)
258
self.failUnlessRaises(TypeError, self.loader.loadModule, "string")
259
self.failUnlessRaises(TypeError,
260
self.loader.loadModule, ('foo', 'bar'))
262
def test_loadPackage(self):
264
suite = self.loader.loadPackage(goodpackage)
265
self.failUnlessEqual(7, suite.countTestCases())
267
def test_loadNonPackage(self):
269
self.failUnlessRaises(TypeError,
270
self.loader.loadPackage, sample.FooTest)
271
self.failUnlessRaises(TypeError,
272
self.loader.loadPackage, sample.FooTest.test_foo)
273
self.failUnlessRaises(TypeError, self.loader.loadPackage, "string")
274
self.failUnlessRaises(TypeError,
275
self.loader.loadPackage, ('foo', 'bar'))
277
def test_loadModuleAsPackage(self):
279
## XXX -- should this instead raise a ValueError? -- jml
280
self.failUnlessRaises(TypeError, self.loader.loadPackage, sample)
282
def test_loadPackageRecursive(self):
284
suite = self.loader.loadPackage(goodpackage, recurse=True)
285
self.failUnlessEqual(14, suite.countTestCases())
287
def test_loadAnythingOnModule(self):
289
suite = self.loader.loadAnything(sample)
290
self.failUnlessEqual(sample.__name__,
291
suite._tests[0]._tests[0].__class__.__module__)
293
def test_loadAnythingOnClass(self):
295
suite = self.loader.loadAnything(sample.FooTest)
296
self.failUnlessEqual(2, suite.countTestCases())
298
def test_loadAnythingOnMethod(self):
300
suite = self.loader.loadAnything(sample.FooTest.test_foo)
301
self.failUnlessEqual(1, suite.countTestCases())
303
def test_loadAnythingOnPackage(self):
305
suite = self.loader.loadAnything(goodpackage)
306
self.failUnless(isinstance(suite, self.loader.suiteFactory))
307
self.failUnlessEqual(7, suite.countTestCases())
309
def test_loadAnythingOnPackageRecursive(self):
311
suite = self.loader.loadAnything(goodpackage, recurse=True)
312
self.failUnless(isinstance(suite, self.loader.suiteFactory))
313
self.failUnlessEqual(14, suite.countTestCases())
315
def test_loadAnythingOnString(self):
316
# the important thing about this test is not the string-iness
317
# but the non-handledness.
318
self.failUnlessRaises(TypeError,
319
self.loader.loadAnything, "goodpackage")
321
def test_importErrors(self):
323
suite = self.loader.loadPackage(package, recurse=True)
324
result = reporter.Reporter()
326
self.failUnlessEqual(False, result.wasSuccessful())
327
self.failUnlessEqual(2, len(result.errors))
328
errors = [test.id() for test, error in result.errors]
330
self.failUnlessEqual(errors, ['package.test_bad_module',
331
'package.test_import_module'])
334
def test_differentInstances(self):
336
L{TestLoader.loadClass} returns a suite with each test method
337
represented by a different instances of the L{TestCase} they are
340
class DistinctInstances(unittest.TestCase):
342
self.first = 'test1Run'
345
self.assertFalse(hasattr(self, 'first'))
347
suite = self.loader.loadClass(DistinctInstances)
348
result = reporter.Reporter()
350
self.assertTrue(result.wasSuccessful())
353
def test_loadModuleWith_test_suite(self):
355
Check that C{test_suite} is used when present and other L{TestCase}s are
358
from twisted.trial.test import mockcustomsuite
359
suite = self.loader.loadModule(mockcustomsuite)
360
self.failUnlessEqual(0, suite.countTestCases())
361
self.failUnlessEqual("MyCustomSuite", getattr(suite, 'name', None))
364
def test_loadModuleWith_testSuite(self):
366
Check that C{testSuite} is used when present and other L{TestCase}s are
369
from twisted.trial.test import mockcustomsuite2
370
suite = self.loader.loadModule(mockcustomsuite2)
371
self.assertEqual(0, suite.countTestCases())
372
self.assertEqual("MyCustomSuite", getattr(suite, 'name', None))
375
def test_loadModuleWithBothCustom(self):
377
Check that if C{testSuite} and C{test_suite} are both present in a
378
module then C{testSuite} gets priority.
380
from twisted.trial.test import mockcustomsuite3
381
suite = self.loader.loadModule(mockcustomsuite3)
382
self.assertEqual('testSuite', getattr(suite, 'name', None))
385
def test_customLoadRaisesAttributeError(self):
387
Make sure that any C{AttributeError}s raised by C{testSuite} are not
388
swallowed by L{TestLoader}.
391
raise AttributeError('should be reraised')
392
from twisted.trial.test import mockcustomsuite2
393
mockcustomsuite2.testSuite, original = (testSuite,
394
mockcustomsuite2.testSuite)
396
self.assertRaises(AttributeError, self.loader.loadModule,
399
mockcustomsuite2.testSuite = original
402
# XXX - duplicated and modified from test_script
403
def assertSuitesEqual(self, test1, test2):
404
names1 = testNames(test1)
405
names2 = testNames(test2)
408
self.assertEqual(names1, names2)
410
def test_loadByNamesDuplicate(self):
412
Check that loadByNames ignores duplicate names
414
module = 'twisted.trial.test.test_test_visitor'
415
suite1 = self.loader.loadByNames([module, module], True)
416
suite2 = self.loader.loadByName(module, True)
417
self.assertSuitesEqual(suite1, suite2)
419
def test_loadDifferentNames(self):
421
Check that loadByNames loads all the names that it is given
423
modules = ['goodpackage', 'package.test_module']
424
suite1 = self.loader.loadByNames(modules)
425
suite2 = runner.TestSuite(map(self.loader.loadByName, modules))
426
self.assertSuitesEqual(suite1, suite2)
430
class ZipLoadingTest(LoaderTest):
432
from twisted.test.test_paths import zipit
433
LoaderTest.setUp(self)
434
zipit(self.parent, self.parent+'.zip')
435
self.parent += '.zip'
436
self.mangleSysPath(self.oldPath+[self.parent])
440
class PackageOrderingTest(packages.SysPathManglingTest):
441
if sys.version_info < (2, 4):
443
"Python 2.3 import semantics make this behavior incorrect on that "
444
"version of Python as well as difficult to test. The second "
445
"import of a package which raised an exception the first time it "
446
"was imported will succeed on Python 2.3, whereas it will fail on "
447
"later versions of Python. Trial does not account for this, so "
448
"this test fails with inconsistencies between the expected and "
449
"the received loader errors.")
452
self.loader = runner.TestLoader()
453
self.topDir = self.mktemp()
454
parent = os.path.join(self.topDir, "uberpackage")
456
file(os.path.join(parent, "__init__.py"), "wb").close()
457
packages.SysPathManglingTest.setUp(self, parent)
458
self.mangleSysPath(self.oldPath + [self.topDir])
460
def _trialSortAlgorithm(self, sorter):
462
Right now, halfway by accident, trial sorts like this:
464
1. all modules are grouped together in one list and sorted.
466
2. within each module, the classes are grouped together in one list
469
3. finally within each class, each test method is grouped together
470
in a list and sorted.
472
This attempts to return a sorted list of testable thingies following
473
those rules, so that we can compare the behavior of loadPackage.
475
The things that show as 'cases' are errors from modules which failed to
476
import, and test methods. Let's gather all those together.
478
pkg = getModule('uberpackage')
480
for testModule in pkg.walkModules():
481
if testModule.name.split(".")[-1].startswith("test_"):
482
testModules.append(testModule)
483
sortedModules = util.dsu(testModules, sorter) # ONE
484
for modinfo in sortedModules:
485
# Now let's find all the classes.
486
module = modinfo.load(None)
491
for attrib in modinfo.iterAttributes():
492
if runner.isTestCase(attrib.load()):
493
testClasses.append(attrib)
494
sortedClasses = util.dsu(testClasses, sorter) # TWO
495
for clsinfo in sortedClasses:
497
for attr in clsinfo.iterAttributes():
498
if attr.name.split(".")[-1].startswith('test'):
499
testMethods.append(attr)
500
sortedMethods = util.dsu(testMethods, sorter) # THREE
501
for methinfo in sortedMethods:
505
def loadSortedPackages(self, sorter=runner.name):
507
Verify that packages are loaded in the correct order.
510
self.loader.sorter = sorter
511
suite = self.loader.loadPackage(uberpackage, recurse=True)
512
# XXX: Work around strange, unexplained Zope crap.
514
suite = unittest.decorate(suite, ITestCase)
515
resultingTests = list(unittest._iterateTests(suite))
516
manifest = list(self._trialSortAlgorithm(sorter))
517
for number, (manifestTest, actualTest) in enumerate(
518
zip(manifest, resultingTests)):
520
manifestTest.name, actualTest.id(),
522
(number, manifestTest.name, actualTest.id()))
523
self.assertEqual(len(manifest), len(resultingTests))
526
def test_sortPackagesDefaultOrder(self):
527
self.loadSortedPackages()
530
def test_sortPackagesSillyOrder(self):
532
# This has to work on fully-qualified class names and class
533
# objects, which is silly, but it's the "spec", such as it is.
534
# if isinstance(s, type) or isinstance(s, types.ClassType):
535
# return s.__module__+'.'+s.__name__
537
d = md5(n).hexdigest()
539
self.loadSortedPackages(sillySorter)