1 # -*- test-case-name: twisted.trial.test.test_runner -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
6 A miscellany of code used to run Trial tests.
8 Maintainer: Jonathan Lange
12 'suiteVisit', 'TestSuite',
14 'DestructiveTestSuite', 'DocTestCase', 'DryRunVisitor',
15 'ErrorHolder', 'LoggedSuite', 'PyUnitTestCase',
16 'TestHolder', 'TestLoader', 'TrialRunner', 'TrialSuite',
18 'filenameToModule', 'isPackage', 'isPackageDirectory', 'isTestCase',
19 'name', 'samefile', 'NOT_IN_TEST',
23 import os, types, warnings, sys, inspect, imp
26 from twisted.python import reflect, log, failure, modules, filepath
27 from twisted.python.compat import set
29 from twisted.internet import defer
30 from twisted.trial import util, unittest
31 from twisted.trial.itrial import ITestCase
32 from twisted.trial.reporter import UncleanWarningsReporterWrapper
34 # These are imported so that they remain in the public API for t.trial.runner
35 from twisted.trial.unittest import suiteVisit, TestSuite
37 from zope.interface import implements
39 pyunit = __import__('unittest')
43 def isPackage(module):
44 """Given an object return True if the object looks like a package"""
45 if not isinstance(module, types.ModuleType):
47 basename = os.path.splitext(os.path.basename(module.__file__))[0]
48 return basename == '__init__'
51 def isPackageDirectory(dirname):
52 """Is the directory at path 'dirname' a Python package directory?
53 Returns the name of the __init__ file (it may have a weird extension)
54 if dirname is a package directory. Otherwise, returns False"""
55 for ext in zip(*imp.get_suffixes())[0]:
56 initFile = '__init__' + ext
57 if os.path.exists(os.path.join(dirname, initFile)):
62 def samefile(filename1, filename2):
64 A hacky implementation of C{os.path.samefile}. Used by L{filenameToModule}
65 when the platform doesn't provide C{os.path.samefile}. Do not use this.
67 return os.path.abspath(filename1) == os.path.abspath(filename2)
70 def filenameToModule(fn):
72 Given a filename, do whatever possible to return a module object matching
75 If the file in question is a module in Python path, properly import and
76 return that module. Otherwise, load the source manually.
78 @param fn: A filename.
79 @return: A module object.
80 @raise ValueError: If C{fn} does not exist.
82 if not os.path.exists(fn):
83 raise ValueError("%r doesn't exist" % (fn,))
85 ret = reflect.namedAny(reflect.filenameToModuleName(fn))
86 except (ValueError, AttributeError):
87 # Couldn't find module. The file 'fn' is not in PYTHONPATH
88 return _importFromFile(fn)
89 # ensure that the loaded module matches the file
90 retFile = os.path.splitext(ret.__file__)[0] + '.py'
91 # not all platforms (e.g. win32) have os.path.samefile
92 same = getattr(os.path, 'samefile', samefile)
93 if os.path.isfile(fn) and not same(fn, retFile):
94 del sys.modules[ret.__name__]
95 ret = _importFromFile(fn)
99 def _importFromFile(fn, moduleName=None):
100 fn = _resolveDirectory(fn)
102 moduleName = os.path.splitext(os.path.split(fn)[-1])[0]
103 if moduleName in sys.modules:
104 return sys.modules[moduleName]
107 module = imp.load_source(moduleName, fn, fd)
113 def _resolveDirectory(fn):
114 if os.path.isdir(fn):
115 initFile = isPackageDirectory(fn)
117 fn = os.path.join(fn, initFile)
119 raise ValueError('%r is not a package directory' % (fn,))
123 def _getMethodNameInClass(method):
125 Find the attribute name on the method's class which refers to the method.
127 For some methods, notably decorators which have not had __name__ set correctly:
129 getattr(method.im_class, method.__name__) != method
131 if getattr(method.im_class, method.__name__, object()) != method:
132 for alias in dir(method.im_class):
133 if getattr(method.im_class, alias, object()) == method:
135 return method.__name__
138 class DestructiveTestSuite(TestSuite):
140 A test suite which remove the tests once run, to minimize memory usage.
143 def run(self, result):
145 Almost the same as L{TestSuite.run}, but with C{self._tests} being
149 if result.shouldStop:
151 test = self._tests.pop(0)
157 # When an error occurs outside of any test, the user will see this string
158 # in place of a test's name.
159 NOT_IN_TEST = "<not in test>"
163 class LoggedSuite(TestSuite):
165 Any errors logged in this suite will be reported to the L{TestResult}
169 def run(self, result):
171 Run the suite, storing all errors in C{result}. If an error is logged
172 while no tests are running, then it will be added as an error to
175 @param result: A L{TestResult} object.
177 observer = unittest._logObserver
179 super(LoggedSuite, self).run(result)
181 for error in observer.getErrors():
182 result.addError(TestHolder(NOT_IN_TEST), error)
183 observer.flushErrors()
187 class PyUnitTestCase(object):
189 DEPRECATED in Twisted 8.0.
191 This class decorates the pyunit.TestCase class, mainly to work around the
192 differences between unittest in Python 2.3, 2.4, and 2.5. These
195 - The way doctest unittests describe themselves
196 - Where the implementation of TestCase.run is (used to be in __call__)
197 - Where the test method name is kept (mangled-private or non-mangled
200 It also implements visit, which we like.
203 def __init__(self, test):
204 warnings.warn("Deprecated in Twisted 8.0.",
205 category=DeprecationWarning)
210 cls = self._test.__class__
211 tmn = getattr(self._test, '_TestCase__testMethodName', None)
213 # python2.5's 'unittest' module is more sensible; but different.
214 tmn = self._test._testMethodName
215 return (cls.__module__ + '.' + cls.__name__ + '.' +
219 return 'PyUnitTestCase<%r>'%(self.id(),)
221 def __call__(self, results):
222 return self._test(results)
225 def visit(self, visitor):
227 Call the given visitor with the original, standard library, test case
228 that C{self} wraps. See L{unittest.TestCase.visit}.
230 Deprecated in Twisted 8.0.
232 warnings.warn("Test visitors deprecated in Twisted 8.0",
233 category=DeprecationWarning)
237 def __getattr__(self, name):
238 return getattr(self._test, name)
242 class DocTestCase(PyUnitTestCase):
244 DEPRECATED in Twisted 8.0.
249 In Python 2.4, doctests have correct id() behaviour. In Python 2.3,
250 id() returns 'runit'.
252 Here we override id() so that at least it will always contain the
253 fully qualified Python name of the doctest.
255 return self._test.shortDescription()
258 class TrialSuite(TestSuite):
260 Suite to wrap around every single test in a C{trial} run. Used internally
261 by Trial to set up things necessary for Trial tests to work, regardless of
262 what context they are run in.
265 def __init__(self, tests=()):
266 suite = LoggedSuite(tests)
267 super(TrialSuite, self).__init__([suite])
271 from twisted.internet import reactor
273 reactor.addSystemEventTrigger('after', 'shutdown',
274 lambda: d.callback(None))
275 reactor.fireSystemEvent('shutdown') # radix's suggestion
276 # As long as TestCase does crap stuff with the reactor we need to
277 # manually shutdown the reactor here, and that requires util.wait
279 # so that the shutdown event completes
280 unittest.TestCase('mktemp')._wait(d)
282 def run(self, result):
284 TestSuite.run(self, result)
291 @param thing: an object from modules (instance of PythonModule,
292 PythonAttribute), a TestCase subclass, or an instance of a TestCase.
294 if isTestCase(thing):
296 theName = reflect.qual(thing)
298 # thing from trial, or thing from modules.
299 # this monstrosity exists so that modules' objects do not have to
300 # implement id(). -jml
303 except AttributeError:
310 @return: C{True} if C{obj} is a class that contains test cases, C{False}
311 otherwise. Used to find all the tests in a module.
314 return issubclass(obj, pyunit.TestCase)
320 class TestHolder(object):
322 Placeholder for a L{TestCase} inside a reporter. As far as a L{TestResult}
323 is concerned, this looks exactly like a unit test.
326 implements(ITestCase)
328 failureException = None
330 def __init__(self, description):
332 @param description: A string to be displayed L{TestResult}.
334 self.description = description
337 def __call__(self, result):
338 return self.run(result)
342 return self.description
345 def countTestCases(self):
349 def run(self, result):
351 This test is just a placeholder. Run the test successfully.
353 @param result: The C{TestResult} to store the results in.
354 @type result: L{twisted.trial.itrial.ITestResult}.
356 result.startTest(self)
357 result.addSuccess(self)
358 result.stopTest(self)
361 def shortDescription(self):
362 return self.description
366 class ErrorHolder(TestHolder):
368 Used to insert arbitrary errors into a test suite run. Provides enough
369 methods to look like a C{TestCase}, however, when it is run, it simply adds
370 an error to the C{TestResult}. The most common use-case is for when a
371 module fails to import.
374 def __init__(self, description, error):
376 @param description: A string used by C{TestResult}s to identify this
377 error. Generally, this is the name of a module that failed to import.
379 @param error: The error to be added to the result. Can be an `exc_info`
380 tuple or a L{twisted.python.failure.Failure}.
382 super(ErrorHolder, self).__init__(description)
383 self.error = util.excInfoOrFailureToExcInfo(error)
387 return "<ErrorHolder description=%r error=%s%s>" % (
388 # Format the exception type and arguments explicitly, as exception
389 # objects do not have nice looking string formats on Python 2.4.
390 self.description, self.error[0].__name__, self.error[1].args)
393 def run(self, result):
395 Run the test, reporting the error.
397 @param result: The C{TestResult} to store the results in.
398 @type result: L{twisted.trial.itrial.ITestResult}.
400 result.startTest(self)
401 result.addError(self, self.error)
402 result.stopTest(self)
405 def visit(self, visitor):
407 See L{unittest.TestCase.visit}.
413 class TestLoader(object):
415 I find tests inside function, modules, files -- whatever -- then return
416 them wrapped inside a Test (either a L{TestSuite} or a L{TestCase}).
418 @ivar methodPrefix: A string prefix. C{TestLoader} will assume that all the
419 methods in a class that begin with C{methodPrefix} are test cases.
421 @ivar modulePrefix: A string prefix. Every module in a package that begins
422 with C{modulePrefix} is considered a module full of tests.
424 @ivar forceGarbageCollection: A flag applied to each C{TestCase} loaded.
425 See L{unittest.TestCase} for more information.
427 @ivar sorter: A key function used to sort C{TestCase}s, test classes,
428 modules and packages.
430 @ivar suiteFactory: A callable which is passed a list of tests (which
431 themselves may be suites of tests). Must return a test suite.
434 methodPrefix = 'test'
435 modulePrefix = 'test_'
438 self.suiteFactory = TestSuite
440 self._importErrors = []
444 Sort the given things using L{sorter}.
446 @param xs: A list of test cases, class or modules.
448 return sorted(xs, key=self.sorter)
450 def findTestClasses(self, module):
451 """Given a module, return all Trial test classes"""
453 for name, val in inspect.getmembers(module):
456 return self.sort(classes)
458 def findByName(self, name):
460 Return a Python object given a string describing it.
462 @param name: a string which may be either a filename or a
463 fully-qualified Python name.
465 @return: If C{name} is a filename, return the module. If C{name} is a
466 fully-qualified Python name, return the object it refers to.
468 if os.path.exists(name):
469 return filenameToModule(name)
470 return reflect.namedAny(name)
472 def loadModule(self, module):
474 Return a test suite with all the tests from a module.
476 Included are TestCase subclasses and doctests listed in the module's
477 __doctests__ module. If that's not good for you, put a function named
478 either C{testSuite} or C{test_suite} in your module that returns a
479 TestSuite, and I'll use the results of that instead.
481 If C{testSuite} and C{test_suite} are both present, then I'll use
484 ## XXX - should I add an optional parameter to disable the check for
486 ## OR, should I add another method
487 if not isinstance(module, types.ModuleType):
488 raise TypeError("%r is not a module" % (module,))
489 if hasattr(module, 'testSuite'):
490 return module.testSuite()
491 elif hasattr(module, 'test_suite'):
492 return module.test_suite()
493 suite = self.suiteFactory()
494 for testClass in self.findTestClasses(module):
495 suite.addTest(self.loadClass(testClass))
496 if not hasattr(module, '__doctests__'):
498 docSuite = self.suiteFactory()
499 for doctest in module.__doctests__:
500 docSuite.addTest(self.loadDoctests(doctest))
501 return self.suiteFactory([suite, docSuite])
502 loadTestsFromModule = loadModule
504 def loadClass(self, klass):
506 Given a class which contains test cases, return a sorted list of
507 C{TestCase} instances.
509 if not (isinstance(klass, type) or isinstance(klass, types.ClassType)):
510 raise TypeError("%r is not a class" % (klass,))
511 if not isTestCase(klass):
512 raise ValueError("%r is not a test case" % (klass,))
513 names = self.getTestCaseNames(klass)
514 tests = self.sort([self._makeCase(klass, self.methodPrefix+name)
516 return self.suiteFactory(tests)
517 loadTestsFromTestCase = loadClass
519 def getTestCaseNames(self, klass):
521 Given a class that contains C{TestCase}s, return a list of names of
522 methods that probably contain tests.
524 return reflect.prefixedMethodNames(klass, self.methodPrefix)
526 def loadMethod(self, method):
528 Given a method of a C{TestCase} that represents a test, return a
529 C{TestCase} instance for that test.
531 if not isinstance(method, types.MethodType):
532 raise TypeError("%r not a method" % (method,))
533 return self._makeCase(method.im_class, _getMethodNameInClass(method))
535 def _makeCase(self, klass, methodName):
536 return klass(methodName)
538 def loadPackage(self, package, recurse=False):
540 Load tests from a module object representing a package, and return a
541 TestSuite containing those tests.
543 Tests are only loaded from modules whose name begins with 'test_'
544 (or whatever C{modulePrefix} is set to).
546 @param package: a types.ModuleType object (or reasonable facsimilie
547 obtained by importing) which may contain tests.
549 @param recurse: A boolean. If True, inspect modules within packages
550 within the given package (and so on), otherwise, only inspect modules
551 in the package itself.
553 @raise: TypeError if 'package' is not a package.
555 @return: a TestSuite created with my suiteFactory, containing all the
558 if not isPackage(package):
559 raise TypeError("%r is not a package" % (package,))
560 pkgobj = modules.getModule(package.__name__)
562 discovery = pkgobj.walkModules()
564 discovery = pkgobj.iterModules()
566 for disco in discovery:
567 if disco.name.split(".")[-1].startswith(self.modulePrefix):
568 discovered.append(disco)
569 suite = self.suiteFactory()
570 for modinfo in self.sort(discovered):
572 module = modinfo.load()
574 thingToAdd = ErrorHolder(modinfo.name, failure.Failure())
576 thingToAdd = self.loadModule(module)
577 suite.addTest(thingToAdd)
580 def loadDoctests(self, module):
582 Return a suite of tests for all the doctests defined in C{module}.
584 @param module: A module object or a module name.
586 if isinstance(module, str):
588 module = reflect.namedAny(module)
590 return ErrorHolder(module, failure.Failure())
591 if not inspect.ismodule(module):
592 warnings.warn("trial only supports doctesting modules")
595 if sys.version_info > (2, 4):
596 # Work around Python issue2604: DocTestCase.tearDown clobbers globs
597 def saveGlobals(test):
599 Save C{test.globs} and replace it with a copy so that if
600 necessary, the original will be available for the next test
603 test._savedGlobals = getattr(test, '_savedGlobals', test.globs)
604 test.globs = test._savedGlobals.copy()
605 extraArgs['setUp'] = saveGlobals
606 return doctest.DocTestSuite(module, **extraArgs)
608 def loadAnything(self, thing, recurse=False):
610 Given a Python object, return whatever tests that are in it. Whatever
613 @param thing: A Python object. A module, method, class or package.
614 @param recurse: Whether or not to look in subpackages of packages.
617 @return: A C{TestCase} or C{TestSuite}.
619 if isinstance(thing, types.ModuleType):
621 return self.loadPackage(thing, recurse)
622 return self.loadModule(thing)
623 elif isinstance(thing, types.ClassType):
624 return self.loadClass(thing)
625 elif isinstance(thing, type):
626 return self.loadClass(thing)
627 elif isinstance(thing, types.MethodType):
628 return self.loadMethod(thing)
629 raise TypeError("No loader for %r. Unrecognized type" % (thing,))
631 def loadByName(self, name, recurse=False):
633 Given a string representing a Python object, return whatever tests
636 If C{name} is somehow inaccessible (e.g. the module can't be imported,
637 there is no Python object with that name etc) then return an
640 @param name: The fully-qualified name of a Python object.
643 thing = self.findByName(name)
645 return ErrorHolder(name, failure.Failure())
646 return self.loadAnything(thing, recurse)
647 loadTestsFromName = loadByName
649 def loadByNames(self, names, recurse=False):
651 Construct a TestSuite containing all the tests found in 'names', where
652 names is a list of fully qualified python names and/or filenames. The
653 suite returned will have no duplicate tests, even if the same object
660 things.append(self.findByName(name))
662 errors.append(ErrorHolder(name, failure.Failure()))
663 suites = [self.loadAnything(thing, recurse)
664 for thing in self._uniqueTests(things)]
665 suites.extend(errors)
666 return self.suiteFactory(suites)
669 def _uniqueTests(self, things):
671 Gather unique suite objects from loaded things. This will guarantee
672 uniqueness of inherited methods on TestCases which would otherwise hash
673 to same value and collapse to one test unexpectedly if using simpler
678 if isinstance(thing, types.MethodType):
679 entries.append((thing, thing.im_class))
681 entries.append((thing,))
682 return [entry[0] for entry in set(entries)]
686 class DryRunVisitor(object):
688 A visitor that makes a reporter think that every test visited has run
692 def __init__(self, reporter):
694 @param reporter: A C{TestResult} object.
696 self.reporter = reporter
699 def markSuccessful(self, testCase):
701 Convince the reporter that this test has been run successfully.
703 self.reporter.startTest(testCase)
704 self.reporter.addSuccess(testCase)
705 self.reporter.stopTest(testCase)
709 class TrialRunner(object):
711 A specialised runner that the trial front end uses.
717 def _getDebugger(self):
722 print "readline module not available"
724 for path in ('.pdbrc', 'pdbrc'):
725 if os.path.exists(path):
727 rcFile = file(path, 'r')
731 dbg.rcLines.extend(rcFile.readlines())
735 def _setUpTestdir(self):
736 self._tearDownLogFile()
737 currentDir = os.getcwd()
738 base = filepath.FilePath(self.workingDirectory)
739 testdir, self._testDirLock = util._unusedTestDirectory(base)
740 os.chdir(testdir.path)
744 def _tearDownTestdir(self, oldDir):
746 self._testDirLock.unlock()
750 def _makeResult(self):
751 reporter = self.reporterFactory(self.stream, self.tbformat,
752 self.rterrors, self._log)
753 if self.uncleanWarnings:
754 reporter = UncleanWarningsReporterWrapper(reporter)
757 def __init__(self, reporterFactory,
762 tracebackFormat='default',
763 realTimeErrors=False,
764 uncleanWarnings=False,
765 workingDirectory=None,
766 forceGarbageCollection=False):
767 self.reporterFactory = reporterFactory
768 self.logfile = logfile
771 self.tbformat = tracebackFormat
772 self.rterrors = realTimeErrors
773 self.uncleanWarnings = uncleanWarnings
775 self.workingDirectory = workingDirectory or '_trial_temp'
776 self._logFileObserver = None
777 self._logFileObject = None
778 self._forceGarbageCollection = forceGarbageCollection
780 self.run = util.profiled(self.run, 'profile.data')
782 def _tearDownLogFile(self):
783 if self._logFileObserver is not None:
784 log.removeObserver(self._logFileObserver.emit)
785 self._logFileObserver = None
786 if self._logFileObject is not None:
787 self._logFileObject.close()
788 self._logFileObject = None
790 def _setUpLogFile(self):
791 self._tearDownLogFile()
792 if self.logfile == '-':
795 logFile = file(self.logfile, 'a')
796 self._logFileObject = logFile
797 self._logFileObserver = log.FileLogObserver(logFile)
798 log.startLoggingWithObserver(self._logFileObserver.emit, 0)
803 Run the test or suite and return a result object.
805 test = unittest.decorate(test, ITestCase)
806 if self._forceGarbageCollection:
807 test = unittest.decorate(
808 test, unittest._ForceGarbageCollectionDecorator)
809 return self._runWithoutDecoration(test)
812 def _runWithoutDecoration(self, test):
814 Private helper that runs the given test but doesn't decorate it.
816 result = self._makeResult()
817 # decorate the suite with reactor cleanup and log starting
818 # This should move out of the runner and be presumed to be
820 suite = TrialSuite([test])
821 startTime = time.time()
822 if self.mode == self.DRY_RUN:
823 for single in unittest._iterateTests(suite):
824 result.startTest(single)
825 result.addSuccess(single)
826 result.stopTest(single)
828 if self.mode == self.DEBUG:
829 # open question - should this be self.debug() instead.
830 debugger = self._getDebugger()
831 run = lambda: debugger.runcall(suite.run, result)
833 run = lambda: suite.run(result)
835 oldDir = self._setUpTestdir()
840 self._tearDownLogFile()
841 self._tearDownTestdir(oldDir)
843 endTime = time.time()
844 done = getattr(result, 'done', None)
847 "%s should implement done() but doesn't. Falling back to "
848 "printErrors() and friends." % reflect.qual(result.__class__),
849 category=DeprecationWarning, stacklevel=3)
851 result.writeln(result.separator)
852 result.writeln('Ran %d tests in %.3fs', result.testsRun,
855 result.printSummary()
861 def runUntilFailure(self, test):
863 Repeatedly run C{test} until it fails.
868 self.stream.write("Test Pass %d\n" % (count,))
870 result = self.run(test)
872 result = self._runWithoutDecoration(test)
873 if result.testsRun == 0:
875 if not result.wasSuccessful():