Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / trial / runner.py
1 # -*- test-case-name: twisted.trial.test.test_runner -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """
6 A miscellany of code used to run Trial tests.
7
8 Maintainer: Jonathan Lange
9 """
10
11 __all__ = [
12     'suiteVisit', 'TestSuite',
13
14     'DestructiveTestSuite', 'DocTestCase', 'DryRunVisitor',
15     'ErrorHolder', 'LoggedSuite', 'PyUnitTestCase',
16     'TestHolder', 'TestLoader', 'TrialRunner', 'TrialSuite',
17
18     'filenameToModule', 'isPackage', 'isPackageDirectory', 'isTestCase',
19     'name', 'samefile', 'NOT_IN_TEST',
20     ]
21
22 import pdb
23 import os, types, warnings, sys, inspect, imp
24 import doctest, time
25
26 from twisted.python import reflect, log, failure, modules, filepath
27 from twisted.python.compat import set
28
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
33
34 # These are imported so that they remain in the public API for t.trial.runner
35 from twisted.trial.unittest import suiteVisit, TestSuite
36
37 from zope.interface import implements
38
39 pyunit = __import__('unittest')
40
41
42
43 def isPackage(module):
44     """Given an object return True if the object looks like a package"""
45     if not isinstance(module, types.ModuleType):
46         return False
47     basename = os.path.splitext(os.path.basename(module.__file__))[0]
48     return basename == '__init__'
49
50
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)):
58             return initFile
59     return False
60
61
62 def samefile(filename1, filename2):
63     """
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.
66     """
67     return os.path.abspath(filename1) == os.path.abspath(filename2)
68
69
70 def filenameToModule(fn):
71     """
72     Given a filename, do whatever possible to return a module object matching
73     that file.
74
75     If the file in question is a module in Python path, properly import and
76     return that module. Otherwise, load the source manually.
77
78     @param fn: A filename.
79     @return: A module object.
80     @raise ValueError: If C{fn} does not exist.
81     """
82     if not os.path.exists(fn):
83         raise ValueError("%r doesn't exist" % (fn,))
84     try:
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)
96     return ret
97
98
99 def _importFromFile(fn, moduleName=None):
100     fn = _resolveDirectory(fn)
101     if not moduleName:
102         moduleName = os.path.splitext(os.path.split(fn)[-1])[0]
103     if moduleName in sys.modules:
104         return sys.modules[moduleName]
105     fd = open(fn, 'r')
106     try:
107         module = imp.load_source(moduleName, fn, fd)
108     finally:
109         fd.close()
110     return module
111
112
113 def _resolveDirectory(fn):
114     if os.path.isdir(fn):
115         initFile = isPackageDirectory(fn)
116         if initFile:
117             fn = os.path.join(fn, initFile)
118         else:
119             raise ValueError('%r is not a package directory' % (fn,))
120     return fn
121
122
123 def _getMethodNameInClass(method):
124     """
125     Find the attribute name on the method's class which refers to the method.
126
127     For some methods, notably decorators which have not had __name__ set correctly:
128
129     getattr(method.im_class, method.__name__) != method
130     """
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:
134                 return alias
135     return method.__name__
136
137
138 class DestructiveTestSuite(TestSuite):
139     """
140     A test suite which remove the tests once run, to minimize memory usage.
141     """
142
143     def run(self, result):
144         """
145         Almost the same as L{TestSuite.run}, but with C{self._tests} being
146         empty at the end.
147         """
148         while self._tests:
149             if result.shouldStop:
150                 break
151             test = self._tests.pop(0)
152             test(result)
153         return result
154
155
156
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>"
160
161
162
163 class LoggedSuite(TestSuite):
164     """
165     Any errors logged in this suite will be reported to the L{TestResult}
166     object.
167     """
168
169     def run(self, result):
170         """
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
173         C{result}.
174
175         @param result: A L{TestResult} object.
176         """
177         observer = unittest._logObserver
178         observer._add()
179         super(LoggedSuite, self).run(result)
180         observer._remove()
181         for error in observer.getErrors():
182             result.addError(TestHolder(NOT_IN_TEST), error)
183         observer.flushErrors()
184
185
186
187 class PyUnitTestCase(object):
188     """
189     DEPRECATED in Twisted 8.0.
190
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
193     differences are::
194
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
198           private variable)
199
200     It also implements visit, which we like.
201     """
202
203     def __init__(self, test):
204         warnings.warn("Deprecated in Twisted 8.0.",
205                       category=DeprecationWarning)
206         self._test = test
207         test.id = self.id
208
209     def id(self):
210         cls = self._test.__class__
211         tmn = getattr(self._test, '_TestCase__testMethodName', None)
212         if tmn is None:
213             # python2.5's 'unittest' module is more sensible; but different.
214             tmn = self._test._testMethodName
215         return (cls.__module__ + '.' + cls.__name__ + '.' +
216                 tmn)
217
218     def __repr__(self):
219         return 'PyUnitTestCase<%r>'%(self.id(),)
220
221     def __call__(self, results):
222         return self._test(results)
223
224
225     def visit(self, visitor):
226         """
227         Call the given visitor with the original, standard library, test case
228         that C{self} wraps. See L{unittest.TestCase.visit}.
229
230         Deprecated in Twisted 8.0.
231         """
232         warnings.warn("Test visitors deprecated in Twisted 8.0",
233                       category=DeprecationWarning)
234         visitor(self._test)
235
236
237     def __getattr__(self, name):
238         return getattr(self._test, name)
239
240
241
242 class DocTestCase(PyUnitTestCase):
243     """
244     DEPRECATED in Twisted 8.0.
245     """
246
247     def id(self):
248         """
249         In Python 2.4, doctests have correct id() behaviour. In Python 2.3,
250         id() returns 'runit'.
251
252         Here we override id() so that at least it will always contain the
253         fully qualified Python name of the doctest.
254         """
255         return self._test.shortDescription()
256
257
258 class TrialSuite(TestSuite):
259     """
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.
263     """
264
265     def __init__(self, tests=()):
266         suite = LoggedSuite(tests)
267         super(TrialSuite, self).__init__([suite])
268
269
270     def _bail(self):
271         from twisted.internet import reactor
272         d = defer.Deferred()
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
278         # :(
279         # so that the shutdown event completes
280         unittest.TestCase('mktemp')._wait(d)
281
282     def run(self, result):
283         try:
284             TestSuite.run(self, result)
285         finally:
286             self._bail()
287
288
289 def name(thing):
290     """
291     @param thing: an object from modules (instance of PythonModule,
292         PythonAttribute), a TestCase subclass, or an instance of a TestCase.
293     """
294     if isTestCase(thing):
295         # TestCase subclass
296         theName = reflect.qual(thing)
297     else:
298         # thing from trial, or thing from modules.
299         # this monstrosity exists so that modules' objects do not have to
300         # implement id(). -jml
301         try:
302             theName = thing.id()
303         except AttributeError:
304             theName = thing.name
305     return theName
306
307
308 def isTestCase(obj):
309     """
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.
312     """
313     try:
314         return issubclass(obj, pyunit.TestCase)
315     except TypeError:
316         return False
317
318
319
320 class TestHolder(object):
321     """
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.
324     """
325
326     implements(ITestCase)
327
328     failureException = None
329
330     def __init__(self, description):
331         """
332         @param description: A string to be displayed L{TestResult}.
333         """
334         self.description = description
335
336
337     def __call__(self, result):
338         return self.run(result)
339
340
341     def id(self):
342         return self.description
343
344
345     def countTestCases(self):
346         return 0
347
348
349     def run(self, result):
350         """
351         This test is just a placeholder. Run the test successfully.
352
353         @param result: The C{TestResult} to store the results in.
354         @type result: L{twisted.trial.itrial.ITestResult}.
355         """
356         result.startTest(self)
357         result.addSuccess(self)
358         result.stopTest(self)
359
360
361     def shortDescription(self):
362         return self.description
363
364
365
366 class ErrorHolder(TestHolder):
367     """
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.
372     """
373
374     def __init__(self, description, error):
375         """
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.
378
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}.
381         """
382         super(ErrorHolder, self).__init__(description)
383         self.error = util.excInfoOrFailureToExcInfo(error)
384
385
386     def __repr__(self):
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)
391
392
393     def run(self, result):
394         """
395         Run the test, reporting the error.
396
397         @param result: The C{TestResult} to store the results in.
398         @type result: L{twisted.trial.itrial.ITestResult}.
399         """
400         result.startTest(self)
401         result.addError(self, self.error)
402         result.stopTest(self)
403
404
405     def visit(self, visitor):
406         """
407         See L{unittest.TestCase.visit}.
408         """
409         visitor(self)
410
411
412
413 class TestLoader(object):
414     """
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}).
417
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.
420
421     @ivar modulePrefix: A string prefix. Every module in a package that begins
422     with C{modulePrefix} is considered a module full of tests.
423
424     @ivar forceGarbageCollection: A flag applied to each C{TestCase} loaded.
425     See L{unittest.TestCase} for more information.
426
427     @ivar sorter: A key function used to sort C{TestCase}s, test classes,
428     modules and packages.
429
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.
432     """
433
434     methodPrefix = 'test'
435     modulePrefix = 'test_'
436
437     def __init__(self):
438         self.suiteFactory = TestSuite
439         self.sorter = name
440         self._importErrors = []
441
442     def sort(self, xs):
443         """
444         Sort the given things using L{sorter}.
445
446         @param xs: A list of test cases, class or modules.
447         """
448         return sorted(xs, key=self.sorter)
449
450     def findTestClasses(self, module):
451         """Given a module, return all Trial test classes"""
452         classes = []
453         for name, val in inspect.getmembers(module):
454             if isTestCase(val):
455                 classes.append(val)
456         return self.sort(classes)
457
458     def findByName(self, name):
459         """
460         Return a Python object given a string describing it.
461
462         @param name: a string which may be either a filename or a
463         fully-qualified Python name.
464
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.
467         """
468         if os.path.exists(name):
469             return filenameToModule(name)
470         return reflect.namedAny(name)
471
472     def loadModule(self, module):
473         """
474         Return a test suite with all the tests from a module.
475
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.
480
481         If C{testSuite} and C{test_suite} are both present, then I'll use
482         C{testSuite}.
483         """
484         ## XXX - should I add an optional parameter to disable the check for
485         ## a custom suite.
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__'):
497             return suite
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
503
504     def loadClass(self, klass):
505         """
506         Given a class which contains test cases, return a sorted list of
507         C{TestCase} instances.
508         """
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)
515                            for name in names])
516         return self.suiteFactory(tests)
517     loadTestsFromTestCase = loadClass
518
519     def getTestCaseNames(self, klass):
520         """
521         Given a class that contains C{TestCase}s, return a list of names of
522         methods that probably contain tests.
523         """
524         return reflect.prefixedMethodNames(klass, self.methodPrefix)
525
526     def loadMethod(self, method):
527         """
528         Given a method of a C{TestCase} that represents a test, return a
529         C{TestCase} instance for that test.
530         """
531         if not isinstance(method, types.MethodType):
532             raise TypeError("%r not a method" % (method,))
533         return self._makeCase(method.im_class, _getMethodNameInClass(method))
534
535     def _makeCase(self, klass, methodName):
536         return klass(methodName)
537
538     def loadPackage(self, package, recurse=False):
539         """
540         Load tests from a module object representing a package, and return a
541         TestSuite containing those tests.
542
543         Tests are only loaded from modules whose name begins with 'test_'
544         (or whatever C{modulePrefix} is set to).
545
546         @param package: a types.ModuleType object (or reasonable facsimilie
547         obtained by importing) which may contain tests.
548
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.
552
553         @raise: TypeError if 'package' is not a package.
554
555         @return: a TestSuite created with my suiteFactory, containing all the
556         tests.
557         """
558         if not isPackage(package):
559             raise TypeError("%r is not a package" % (package,))
560         pkgobj = modules.getModule(package.__name__)
561         if recurse:
562             discovery = pkgobj.walkModules()
563         else:
564             discovery = pkgobj.iterModules()
565         discovered = []
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):
571             try:
572                 module = modinfo.load()
573             except:
574                 thingToAdd = ErrorHolder(modinfo.name, failure.Failure())
575             else:
576                 thingToAdd = self.loadModule(module)
577             suite.addTest(thingToAdd)
578         return suite
579
580     def loadDoctests(self, module):
581         """
582         Return a suite of tests for all the doctests defined in C{module}.
583
584         @param module: A module object or a module name.
585         """
586         if isinstance(module, str):
587             try:
588                 module = reflect.namedAny(module)
589             except:
590                 return ErrorHolder(module, failure.Failure())
591         if not inspect.ismodule(module):
592             warnings.warn("trial only supports doctesting modules")
593             return
594         extraArgs = {}
595         if sys.version_info > (2, 4):
596             # Work around Python issue2604: DocTestCase.tearDown clobbers globs
597             def saveGlobals(test):
598                 """
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
601                 run.
602                 """
603                 test._savedGlobals = getattr(test, '_savedGlobals', test.globs)
604                 test.globs = test._savedGlobals.copy()
605             extraArgs['setUp'] = saveGlobals
606         return doctest.DocTestSuite(module, **extraArgs)
607
608     def loadAnything(self, thing, recurse=False):
609         """
610         Given a Python object, return whatever tests that are in it. Whatever
611         'in' might mean.
612
613         @param thing: A Python object. A module, method, class or package.
614         @param recurse: Whether or not to look in subpackages of packages.
615         Defaults to False.
616
617         @return: A C{TestCase} or C{TestSuite}.
618         """
619         if isinstance(thing, types.ModuleType):
620             if isPackage(thing):
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,))
630
631     def loadByName(self, name, recurse=False):
632         """
633         Given a string representing a Python object, return whatever tests
634         are in that object.
635
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
638         L{ErrorHolder}.
639
640         @param name: The fully-qualified name of a Python object.
641         """
642         try:
643             thing = self.findByName(name)
644         except:
645             return ErrorHolder(name, failure.Failure())
646         return self.loadAnything(thing, recurse)
647     loadTestsFromName = loadByName
648
649     def loadByNames(self, names, recurse=False):
650         """
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
654         is named twice.
655         """
656         things = []
657         errors = []
658         for name in names:
659             try:
660                 things.append(self.findByName(name))
661             except:
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)
667
668
669     def _uniqueTests(self, things):
670         """
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
674         means: e.g. set().
675         """
676         entries = []
677         for thing in things:
678             if isinstance(thing, types.MethodType):
679                 entries.append((thing, thing.im_class))
680             else:
681                 entries.append((thing,))
682         return [entry[0] for entry in set(entries)]
683
684
685
686 class DryRunVisitor(object):
687     """
688     A visitor that makes a reporter think that every test visited has run
689     successfully.
690     """
691
692     def __init__(self, reporter):
693         """
694         @param reporter: A C{TestResult} object.
695         """
696         self.reporter = reporter
697
698
699     def markSuccessful(self, testCase):
700         """
701         Convince the reporter that this test has been run successfully.
702         """
703         self.reporter.startTest(testCase)
704         self.reporter.addSuccess(testCase)
705         self.reporter.stopTest(testCase)
706
707
708
709 class TrialRunner(object):
710     """
711     A specialised runner that the trial front end uses.
712     """
713
714     DEBUG = 'debug'
715     DRY_RUN = 'dry-run'
716
717     def _getDebugger(self):
718         dbg = pdb.Pdb()
719         try:
720             import readline
721         except ImportError:
722             print "readline module not available"
723             sys.exc_clear()
724         for path in ('.pdbrc', 'pdbrc'):
725             if os.path.exists(path):
726                 try:
727                     rcFile = file(path, 'r')
728                 except IOError:
729                     sys.exc_clear()
730                 else:
731                     dbg.rcLines.extend(rcFile.readlines())
732         return dbg
733
734
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)
741         return currentDir
742
743
744     def _tearDownTestdir(self, oldDir):
745         os.chdir(oldDir)
746         self._testDirLock.unlock()
747
748
749     _log = log
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)
755         return reporter
756
757     def __init__(self, reporterFactory,
758                  mode=None,
759                  logfile='test.log',
760                  stream=sys.stdout,
761                  profile=False,
762                  tracebackFormat='default',
763                  realTimeErrors=False,
764                  uncleanWarnings=False,
765                  workingDirectory=None,
766                  forceGarbageCollection=False):
767         self.reporterFactory = reporterFactory
768         self.logfile = logfile
769         self.mode = mode
770         self.stream = stream
771         self.tbformat = tracebackFormat
772         self.rterrors = realTimeErrors
773         self.uncleanWarnings = uncleanWarnings
774         self._result = None
775         self.workingDirectory = workingDirectory or '_trial_temp'
776         self._logFileObserver = None
777         self._logFileObject = None
778         self._forceGarbageCollection = forceGarbageCollection
779         if profile:
780             self.run = util.profiled(self.run, 'profile.data')
781
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
789
790     def _setUpLogFile(self):
791         self._tearDownLogFile()
792         if self.logfile == '-':
793             logFile = sys.stdout
794         else:
795             logFile = file(self.logfile, 'a')
796         self._logFileObject = logFile
797         self._logFileObserver = log.FileLogObserver(logFile)
798         log.startLoggingWithObserver(self._logFileObserver.emit, 0)
799
800
801     def run(self, test):
802         """
803         Run the test or suite and return a result object.
804         """
805         test = unittest.decorate(test, ITestCase)
806         if self._forceGarbageCollection:
807             test = unittest.decorate(
808                 test, unittest._ForceGarbageCollectionDecorator)
809         return self._runWithoutDecoration(test)
810
811
812     def _runWithoutDecoration(self, test):
813         """
814         Private helper that runs the given test but doesn't decorate it.
815         """
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
819         # present
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)
827         else:
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)
832             else:
833                 run = lambda: suite.run(result)
834
835             oldDir = self._setUpTestdir()
836             try:
837                 self._setUpLogFile()
838                 run()
839             finally:
840                 self._tearDownLogFile()
841                 self._tearDownTestdir(oldDir)
842
843         endTime = time.time()
844         done = getattr(result, 'done', None)
845         if done is None:
846             warnings.warn(
847                 "%s should implement done() but doesn't. Falling back to "
848                 "printErrors() and friends." % reflect.qual(result.__class__),
849                 category=DeprecationWarning, stacklevel=3)
850             result.printErrors()
851             result.writeln(result.separator)
852             result.writeln('Ran %d tests in %.3fs', result.testsRun,
853                            endTime - startTime)
854             result.write('\n')
855             result.printSummary()
856         else:
857             result.done()
858         return result
859
860
861     def runUntilFailure(self, test):
862         """
863         Repeatedly run C{test} until it fails.
864         """
865         count = 0
866         while True:
867             count += 1
868             self.stream.write("Test Pass %d\n" % (count,))
869             if count == 1:
870                 result = self.run(test)
871             else:
872                 result = self._runWithoutDecoration(test)
873             if result.testsRun == 0:
874                 break
875             if not result.wasSuccessful():
876                 break
877         return result