root / trunk / twisted / trial / runner.py

Revision 26734, 27.6 kB (checked in by exarkun, 2 months ago)

Merge leave-threadpool-alone-3786

Author: exarkun
Reviewer: jml
Fixes: #3786

Remove the trial TestSuite cleanup code which resizes the reactor's thread
pool to 0. The reactors take care of their own thread pool cleanup and this
resize is not only unnecessary, but it interfers with the process.

Line 
1 # -*- test-case-name: twisted.trial.test.test_runner -*-
2 # Copyright (c) 2001-2008 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
12 import pdb, shutil
13 import os, types, warnings, sys, inspect, imp
14 import random, doctest, time
15
16 from twisted.python import reflect, log, failure, modules
17 from twisted.python.util import dsu
18 from twisted.python.compat import set
19 from twisted.python.lockfile import FilesystemLock
20
21 from twisted.internet import defer
22 from twisted.trial import util, unittest
23 from twisted.trial.itrial import ITestCase
24 from twisted.trial.reporter import UncleanWarningsReporterWrapper
25
26 # These are imported so that they remain in the public API for t.trial.runner
27 from twisted.trial.unittest import suiteVisit, TestSuite
28
29 from zope.interface import implements
30
31 pyunit = __import__('unittest')
32
33
34
35 class _WorkingDirectoryBusy(Exception):
36     """
37     A working directory was specified to the runner, but another test run is
38     currently using that directory.
39     """
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 def filenameToModule(fn):
70     """
71     Given a filename, do whatever possible to return a module object matching
72     that file.
73
74     If the file in question is a module in Python path, properly import and
75     return that module. Otherwise, load the source manually.
76
77     @param fn: A filename.
78     @return: A module object.
79     @raise ValueError: If C{fn} does not exist.
80     """
81     if not os.path.exists(fn):
82         raise ValueError("%r doesn't exist" % (fn,))
83     try:
84         ret = reflect.namedAny(reflect.filenameToModuleName(fn))
85     except (ValueError, AttributeError):
86         # Couldn't find module.  The file 'fn' is not in PYTHONPATH
87         return _importFromFile(fn)
88     # ensure that the loaded module matches the file
89     retFile = os.path.splitext(ret.__file__)[0] + '.py'
90     # not all platforms (e.g. win32) have os.path.samefile
91     same = getattr(os.path, 'samefile', samefile)
92     if os.path.isfile(fn) and not same(fn, retFile):
93         del sys.modules[ret.__name__]
94         ret = _importFromFile(fn)
95     return ret
96
97
98 def _importFromFile(fn, moduleName=None):
99     fn = _resolveDirectory(fn)
100     if not moduleName:
101         moduleName = os.path.splitext(os.path.split(fn)[-1])[0]
102     if moduleName in sys.modules:
103         return sys.modules[moduleName]
104     fd = open(fn, 'r')
105     try:
106         module = imp.load_source(moduleName, fn, fd)
107     finally:
108         fd.close()
109     return module
110
111
112 def _resolveDirectory(fn):
113     if os.path.isdir(fn):
114         initFile = isPackageDirectory(fn)
115         if initFile:
116             fn = os.path.join(fn, initFile)
117         else:
118             raise ValueError('%r is not a package directory' % (fn,))
119     return fn
120
121
122
123 class DestructiveTestSuite(TestSuite):
124     """
125     A test suite which remove the tests once run, to minimize memory usage.
126     """
127
128     def run(self, result):
129         """
130         Almost the same as L{TestSuite.run}, but with C{self._tests} being
131         empty at the end.
132         """
133         while self._tests:
134             if result.shouldStop:
135                 break
136             test = self._tests.pop(0)
137             test(result)
138         return result
139
140
141
142 # When an error occurs outside of any test, the user will see this string
143 # in place of a test's name.
144 NOT_IN_TEST = "<not in test>"
145
146
147
148 class LoggedSuite(TestSuite):
149     """
150     Any errors logged in this suite will be reported to the L{TestResult}
151     object.
152     """
153
154     def run(self, result):
155         """
156         Run the suite, storing all errors in C{result}. If an error is logged
157         while no tests are running, then it will be added as an error to
158         C{result}.
159
160         @param result: A L{TestResult} object.
161         """
162         observer = unittest._logObserver
163         observer._add()
164         super(LoggedSuite, self).run(result)
165         observer._remove()
166         for error in observer.getErrors():
167             result.addError(TestHolder(NOT_IN_TEST), error)
168         observer.flushErrors()
169
170
171
172 class DocTestSuite(TestSuite):
173     """
174     DEPRECATED in Twisted 8.0.
175
176     Behaves like doctest.DocTestSuite, but decorates individual TestCases so
177     they support visit and so that id() behaviour is meaningful and consistent
178     between Python versions.
179     """
180
181     def __init__(self, testModule):
182         warnings.warn("DocTestSuite is deprecated in Twisted 8.0.",
183                       category=DeprecationWarning, stacklevel=2)
184         TestSuite.__init__(self)
185         suite = doctest.DocTestSuite(testModule)
186         for test in suite._tests: #yay encapsulation
187             self.addTest(ITestCase(test))
188
189
190
191 class PyUnitTestCase(object):
192     """
193     DEPRECATED in Twisted 8.0.
194
195     This class decorates the pyunit.TestCase class, mainly to work around the
196     differences between unittest in Python 2.3, 2.4, and 2.5. These
197     differences are::
198
199         - The way doctest unittests describe themselves
200         - Where the implementation of TestCase.run is (used to be in __call__)
201         - Where the test method name is kept (mangled-private or non-mangled
202           private variable)
203
204     It also implements visit, which we like.
205     """
206
207     def __init__(self, test):
208         warnings.warn("Deprecated in Twisted 8.0.",
209                       category=DeprecationWarning)
210         self._test = test
211         test.id = self.id
212
213     def id(self):
214         cls = self._test.__class__
215         tmn = getattr(self._test, '_TestCase__testMethodName', None)
216         if tmn is None:
217             # python2.5's 'unittest' module is more sensible; but different.
218             tmn = self._test._testMethodName
219         return (cls.__module__ + '.' + cls.__name__ + '.' +
220                 tmn)
221
222     def __repr__(self):
223         return 'PyUnitTestCase<%r>'%(self.id(),)
224
225     def __call__(self, results):
226         return self._test(results)
227
228
229     def visit(self, visitor):
230         """
231         Call the given visitor with the original, standard library, test case
232         that C{self} wraps. See L{unittest.TestCase.visit}.
233
234         Deprecated in Twisted 8.0.
235         """
236         warnings.warn("Test visitors deprecated in Twisted 8.0",
237                       category=DeprecationWarning)
238         visitor(self._test)
239
240
241     def __getattr__(self, name):
242         return getattr(self._test, name)
243
244
245
246 class DocTestCase(PyUnitTestCase):
247     """
248     DEPRECATED in Twisted 8.0.
249     """
250
251     def id(self):
252         """
253         In Python 2.4, doctests have correct id() behaviour. In Python 2.3,
254         id() returns 'runit'.
255
256         Here we override id() so that at least it will always contain the
257         fully qualified Python name of the doctest.
258         """
259         return self._test.shortDescription()
260
261
262 class TrialSuite(TestSuite):
263     """
264     Suite to wrap around every single test in a C{trial} run. Used internally
265     by Trial to set up things necessary for Trial tests to work, regardless of
266     what context they are run in.
267     """
268
269     def __init__(self, tests=()):
270         suite = LoggedSuite(tests)
271         super(TrialSuite, self).__init__([suite])
272
273
274     def _bail(self):
275         from twisted.internet import reactor
276         d = defer.Deferred()
277         reactor.addSystemEventTrigger('after', 'shutdown',
278                                       lambda: d.callback(None))
279         reactor.fireSystemEvent('shutdown') # radix's suggestion
280         # As long as TestCase does crap stuff with the reactor we need to
281         # manually shutdown the reactor here, and that requires util.wait
282         # :(
283         # so that the shutdown event completes
284         unittest.TestCase('mktemp')._wait(d)
285
286     def run(self, result):
287         try:
288             TestSuite.run(self, result)
289         finally:
290             self._bail()
291
292
293 def name(thing):
294     """
295     @param thing: an object from modules (instance of PythonModule,
296     PythonAttribute), a TestCase subclass, or an instance of a TestCase.
297     """
298     if isTestCase(thing):
299         # TestCase subclass
300         theName = reflect.qual(thing)
301     else:
302         # thing from trial, or thing from modules.
303         # this monstrosity exists so that modules' objects do not have to
304         # implement id(). -jml
305         try:
306             theName = thing.id()
307         except AttributeError:
308             theName = thing.name
309     return theName
310
311
312 def isTestCase(obj):
313     """
314     Returns C{True} if C{obj} is a class that contains test cases, C{False}
315     otherwise. Used to find all the tests in a module.
316     """
317     try:
318         return issubclass(obj, pyunit.TestCase)
319     except TypeError:
320         return False
321
322
323
324 class TestHolder(object):
325     """
326     Placeholder for a L{TestCase} inside a reporter. As far as a L{TestResult}
327     is concerned, this looks exactly like a unit test.
328     """
329
330     implements(ITestCase)
331
332     def __init__(self, description):
333         """
334         @param description: A string to be displayed L{TestResult}.
335         """
336         self.description = description
337
338
339     def id(self):
340         return self.description
341
342
343     def shortDescription(self):
344         return self.description
345
346
347
348 class ErrorHolder(TestHolder):
349     """
350     Used to insert arbitrary errors into a test suite run. Provides enough
351     methods to look like a C{TestCase}, however, when it is run, it simply adds
352     an error to the C{TestResult}. The most common use-case is for when a
353     module fails to import.
354     """
355
356     def __init__(self, description, error):
357         """
358         @param description: A string used by C{TestResult}s to identify this
359         error. Generally, this is the name of a module that failed to import.
360
361         @param error: The error to be added to the result. Can be an exc_info
362         tuple or a L{twisted.python.failure.Failure}.
363         """
364         super(ErrorHolder, self).__init__(description)
365         self.error = error
366
367
368     def __repr__(self):
369         return "<ErrorHolder description=%r error=%r>" % (self.description,
370                                                           self.error)
371
372
373     def run(self, result):
374         result.addError(self, self.error)
375
376
377     def __call__(self, result):
378         return self.run(result)
379
380
381     def countTestCases(self):
382         return 0
383
384
385     def visit(self, visitor):
386         """
387         See L{unittest.TestCase.visit}.
388         """
389         visitor(self)
390
391
392
393 class TestLoader(object):
394     """
395     I find tests inside function, modules, files -- whatever -- then return
396     them wrapped inside a Test (either a L{TestSuite} or a L{TestCase}).
397
398     @ivar methodPrefix: A string prefix. C{TestLoader} will assume that all the
399     methods in a class that begin with C{methodPrefix} are test cases.
400
401     @ivar modulePrefix: A string prefix. Every module in a package that begins
402     with C{modulePrefix} is considered a module full of tests.
403
404     @ivar forceGarbageCollection: A flag applied to each C{TestCase} loaded.
405     See L{unittest.TestCase} for more information.
406
407     @ivar sorter: A key function used to sort C{TestCase}s, test classes,
408     modules and packages.
409
410     @ivar suiteFactory: A callable which is passed a list of tests (which
411     themselves may be suites of tests). Must return a test suite.
412     """
413
414     methodPrefix = 'test'
415     modulePrefix = 'test_'
416
417     def __init__(self):
418         self.suiteFactory = TestSuite
419         self.sorter = name
420         self._importErrors = []
421
422     def sort(self, xs):
423         """
424         Sort the given things using L{sorter}.
425
426         @param xs: A list of test cases, class or modules.
427         """
428         return dsu(xs, self.sorter)
429
430     def findTestClasses(self, module):
431         """Given a module, return all Trial test classes"""
432         classes = []
433         for name, val in inspect.getmembers(module):
434             if isTestCase(val):
435                 classes.append(val)
436         return self.sort(classes)
437
438     def findByName(self, name):
439         """
440         Return a Python object given a string describing it.
441
442         @param name: a string which may be either a filename or a
443         fully-qualified Python name.
444
445         @return: If C{name} is a filename, return the module. If C{name} is a
446         fully-qualified Python name, return the object it refers to.
447         """
448         if os.path.exists(name):
449             return filenameToModule(name)
450         return reflect.namedAny(name)
451
452     def loadModule(self, module):
453         """
454         Return a test suite with all the tests from a module.
455
456         Included are TestCase subclasses and doctests listed in the module's
457         __doctests__ module. If that's not good for you, put a function named
458         either C{testSuite} or C{test_suite} in your module that returns a
459         TestSuite, and I'll use the results of that instead.
460
461         If C{testSuite} and C{test_suite} are both present, then I'll use
462         C{testSuite}.
463         """
464         ## XXX - should I add an optional parameter to disable the check for
465         ## a custom suite.
466         ## OR, should I add another method
467         if not isinstance(module, types.ModuleType):
468             raise TypeError("%r is not a module" % (module,))
469         if hasattr(module, 'testSuite'):
470             return module.testSuite()
471         elif hasattr(module, 'test_suite'):
472             return module.test_suite()
473         suite = self.suiteFactory()
474         for testClass in self.findTestClasses(module):
475             suite.addTest(self.loadClass(testClass))
476         if not hasattr(module, '__doctests__'):
477             return suite
478         docSuite = self.suiteFactory()
479         for doctest in module.__doctests__:
480             docSuite.addTest(self.loadDoctests(doctest))
481         return self.suiteFactory([suite, docSuite])
482     loadTestsFromModule = loadModule
483
484     def loadClass(self, klass):
485         """
486         Given a class which contains test cases, return a sorted list of
487         C{TestCase} instances.
488         """
489         if not (isinstance(klass, type) or isinstance(klass, types.ClassType)):
490             raise TypeError("%r is not a class" % (klass,))
491         if not isTestCase(klass):
492             raise ValueError("%r is not a test case" % (klass,))
493         names = self.getTestCaseNames(klass)
494         tests = self.sort([self._makeCase(klass, self.methodPrefix+name)
495                            for name in names])
496         return self.suiteFactory(tests)
497     loadTestsFromTestCase = loadClass
498
499     def getTestCaseNames(self, klass):
500         """
501         Given a class that contains C{TestCase}s, return a list of names of
502         methods that probably contain tests.
503         """
504         return reflect.prefixedMethodNames(klass, self.methodPrefix)
505
506     def loadMethod(self, method):
507         """
508         Given a method of a C{TestCase} that represents a test, return a
509         C{TestCase} instance for that test.
510         """
511         if not isinstance(method, types.MethodType):
512             raise TypeError("%r not a method" % (method,))
513         return self._makeCase(method.im_class, method.__name__)
514
515     def _makeCase(self, klass, methodName):
516         return klass(methodName)
517
518     def loadPackage(self, package, recurse=False):
519         """
520         Load tests from a module object representing a package, and return a
521         TestSuite containing those tests.
522
523         Tests are only loaded from modules whose name begins with 'test_'
524         (or whatever C{modulePrefix} is set to).
525
526         @param package: a types.ModuleType object (or reasonable facsimilie
527         obtained by importing) which may contain tests.
528
529         @param recurse: A boolean.  If True, inspect modules within packages
530         within the given package (and so on), otherwise, only inspect modules
531         in the package itself.
532
533         @raise: TypeError if 'package' is not a package.
534
535         @return: a TestSuite created with my suiteFactory, containing all the
536         tests.
537         """
538         if not isPackage(package):
539             raise TypeError("%r is not a package" % (package,))
540         pkgobj = modules.getModule(package.__name__)
541         if recurse:
542             discovery = pkgobj.walkModules()
543         else:
544             discovery = pkgobj.iterModules()
545         discovered = []
546         for disco in discovery:
547             if disco.name.split(".")[-1].startswith(self.modulePrefix):
548                 discovered.append(disco)
549         suite = self.suiteFactory()
550         for modinfo in self.sort(discovered):
551             try:
552                 module = modinfo.load()
553             except:
554                 thingToAdd = ErrorHolder(modinfo.name, failure.Failure())
555             else:
556                 thingToAdd = self.loadModule(module)
557             suite.addTest(thingToAdd)
558         return suite
559
560     def loadDoctests(self, module):
561         """
562         Return a suite of tests for all the doctests defined in C{module}.
563
564         @param module: A module object or a module name.
565         """
566         if isinstance(module, str):
567             try:
568                 module = reflect.namedAny(module)
569             except:
570                 return ErrorHolder(module, failure.Failure())
571         if not inspect.ismodule(module):
572             warnings.warn("trial only supports doctesting modules")
573             return
574         extraArgs = {}
575         if sys.version_info > (2, 4):
576             # Work around Python issue2604: DocTestCase.tearDown clobbers globs
577             def saveGlobals(test):
578                 """
579                 Save C{test.globs} and replace it with a copy so that if
580                 necessary, the original will be available for the next test
581                 run.
582                 """
583                 test._savedGlobals = getattr(test, '_savedGlobals', test.globs)
584                 test.globs = test._savedGlobals.copy()
585             extraArgs['setUp'] = saveGlobals
586         return doctest.DocTestSuite(module, **extraArgs)
587
588     def loadAnything(self, thing, recurse=False):
589         """
590         Given a Python object, return whatever tests that are in it. Whatever
591         'in' might mean.
592
593         @param thing: A Python object. A module, method, class or package.
594         @param recurse: Whether or not to look in subpackages of packages.
595         Defaults to False.
596
597         @return: A C{TestCase} or C{TestSuite}.
598         """
599         if isinstance(thing, types.ModuleType):
600             if isPackage(thing):
601                 return self.loadPackage(thing, recurse)
602             return self.loadModule(thing)
603         elif isinstance(thing, types.ClassType):
604             return self.loadClass(thing)
605         elif isinstance(thing, type):
606             return self.loadClass(thing)
607         elif isinstance(thing, types.MethodType):
608             return self.loadMethod(thing)
609         raise TypeError("No loader for %r. Unrecognized type" % (thing,))
610
611     def loadByName(self, name, recurse=False):
612         """
613         Given a string representing a Python object, return whatever tests
614         are in that object.
615
616         If C{name} is somehow inaccessible (e.g. the module can't be imported,
617         there is no Python object with that name etc) then return an
618         L{ErrorHolder}.
619
620         @param name: The fully-qualified name of a Python object.
621         """
622         try:
623             thing = self.findByName(name)
624         except:
625             return ErrorHolder(name, failure.Failure())
626         return self.loadAnything(thing, recurse)
627     loadTestsFromName = loadByName
628
629     def loadByNames(self, names, recurse=False):
630         """
631         Construct a TestSuite containing all the tests found in 'names', where
632         names is a list of fully qualified python names and/or filenames. The
633         suite returned will have no duplicate tests, even if the same object
634         is named twice.
635         """
636         things = []
637         errors = []
638         for name in names:
639             try:
640                 things.append(self.findByName(name))
641             except:
642                 errors.append(ErrorHolder(name, failure.Failure()))
643         suites = [self.loadAnything(thing, recurse)
644                   for thing in set(things)]
645         suites.extend(errors)
646         return self.suiteFactory(suites)
647
648
649
650 class DryRunVisitor(object):
651     """
652     A visitor that makes a reporter think that every test visited has run
653     successfully.
654     """
655
656     def __init__(self, reporter):
657         """
658         @param reporter: A C{TestResult} object.
659         """
660         self.reporter = reporter
661
662
663     def markSuccessful(self, testCase):
664         """
665         Convince the reporter that this test has been run successfully.
666         """
667         self.reporter.startTest(testCase)
668         self.reporter.addSuccess(testCase)
669         self.reporter.stopTest(testCase)
670
671
672
673 class TrialRunner(object):
674     """
675     A specialised runner that the trial front end uses.
676     """
677
678     DEBUG = 'debug'
679     DRY_RUN = 'dry-run'
680
681     def _getDebugger(self):
682         dbg = pdb.Pdb()
683         try:
684             import readline
685         except ImportError:
686             print "readline module not available"
687             hasattr(sys, 'exc_clear') and sys.exc_clear()
688         for path in ('.pdbrc', 'pdbrc'):
689             if os.path.exists(path):
690                 try:
691                     rcFile = file(path, 'r')
692                 except IOError:
693                     hasattr(sys, 'exc_clear') and sys.exc_clear()
694                 else:
695                     dbg.rcLines.extend(rcFile.readlines())
696         return dbg
697
698
699     def _removeSafely(self, path):
700         try:
701             shutil.rmtree(path)
702         except OSError, e:
703             print ("could not remove %r, caught OSError [Errno %s]: %s"
704                    % (path, e.errno,e.strerror))
705             try:
706                 os.rename(path,
707                           os.path.abspath("_trial_temp_old%s"
708                                           % random.randint(0, 99999999)))
709             except OSError, e:
710                 print ("could not rename path, caught OSError [Errno %s]: %s"
711                        % (e.errno,e.strerror))
712                 raise
713
714
715     def _setUpTestdir(self):
716         self._tearDownLogFile()
717         currentDir = os.getcwd()
718         base = os.path.normpath(os.path.abspath(self.workingDirectory))
719         counter = 0
720         while True:
721             if counter:
722                 testdir = '%s-%d' % (base, counter)
723             else:
724                 testdir = base
725
726             self._testDirLock = FilesystemLock(testdir + '.lock')
727             if self._testDirLock.lock():
728                 # It is not in use
729                 if os.path.exists(testdir):
730                     # It exists though - delete it
731                     self._removeSafely(testdir)
732                 break
733             else:
734                 # It is in use
735                 if self.workingDirectory == '_trial_temp':
736                     counter += 1
737                 else:
738                     raise _WorkingDirectoryBusy()
739
740         os.mkdir(testdir)
741         os.chdir(testdir)
742         return currentDir
743
744
745     def _tearDownTestdir(self, oldDir):
746         os.chdir(oldDir)
747         self._testDirLock.unlock()
748
749
750     _log = log
751     def _makeResult(self):
752         reporter = self.reporterFactory(self.stream, self.tbformat,
753                                         self.rterrors, self._log)
754         if self.uncleanWarnings:
755             reporter = UncleanWarningsReporterWrapper(reporter)
756         return reporter
757
758     def __init__(self, reporterFactory,
759                  mode=None,
760                  logfile='test.log',
761                  stream=sys.stdout,
762                  profile=False,
763                  tracebackFormat='default',
764                  realTimeErrors=False,
765                  uncleanWarnings=False,
766                  workingDirectory=None,
767                  forceGarbageCollection=False):
768         self.reporterFactory = reporterFactory
769         self.logfile = logfile
770         self.mode = mode
771         self.stream = stream
772         self.tbformat = tracebackFormat
773         self.rterrors = realTimeErrors
774         self.uncleanWarnings = uncleanWarnings
775         self._result = None
776         self.workingDirectory = workingDirectory or '_trial_temp'
777         self._logFileObserver = None
778         self._logFileObject = None
779         self._forceGarbageCollection = forceGarbageCollection
780         if profile:
781             self.run = util.profiled(self.run, 'profile.data')
782
783     def _tearDownLogFile(self):
784         if self._logFileObserver is not None:
785             log.removeObserver(self._logFileObserver.emit)
786             self._logFileObserver = None
787         if self._logFileObject is not None:
788             self._logFileObject.close()
789             self._logFileObject = None
790
791     def _setUpLogFile(self):
792         self._tearDownLogFile()
793         if self.logfile == '-':
794             logFile = sys.stdout
795         else:
796             logFile = file(self.logfile, 'a')
797         self._logFileObject = logFile
798         self._logFileObserver = log.FileLogObserver(logFile)
799         log.startLoggingWithObserver(self._logFileObserver.emit, 0)
800
801
802     def run(self, test):
803         """
804         Run the test or suite and return a result object.
805         """
806         test = unittest.decorate(test, ITestCase)
807         if self._forceGarbageCollection:
808             test = unittest.decorate(
809                 test, unittest._ForceGarbageCollectionDecorator)
810         return self._runWithoutDecoration(test)
811
812
813     def _runWithoutDecoration(self, test):
814         """
815         Private helper that runs the given test but doesn't decorate it.
816         """
817         result = self._makeResult()
818         # decorate the suite with reactor cleanup and log starting
819         # This should move out of the runner and be presumed to be
820         # present
821         suite = TrialSuite([test])
822         startTime = time.time()
823         if self.mode == self.DRY_RUN:
824             suite.visit(DryRunVisitor(result).markSuccessful)
825         else:
826             if self.mode == self.DEBUG:
827                 # open question - should this be self.debug() instead.
828                 debugger = self._getDebugger()
829                 run = lambda: debugger.runcall(suite.run, result)
830             else:
831                 run = lambda: suite.run(result)
832
833             oldDir = self._setUpTestdir()
834             try:
835                 self._setUpLogFile()
836                 run()
837             finally:
838                 self._tearDownLogFile()
839                 self._tearDownTestdir(oldDir)
840
841         endTime = time.time()
842         done = getattr(result, 'done', None)
843         if done is None:
844             warnings.warn(
845                 "%s should implement done() but doesn't. Falling back to "
846                 "printErrors() and friends." % reflect.qual(result.__class__),
847                 category=DeprecationWarning, stacklevel=3)
848             result.printErrors()
849             result.writeln(result.separator)
850             result.writeln('Ran %d tests in %.3fs', result.testsRun,
851                            endTime - startTime)
852             result.write('\n')
853             result.printSummary()
854         else:
855             result.done()
856         return result
857
858
859     def runUntilFailure(self, test):
860         """
861         Repeatedly run C{test} until it fails.
862         """
863         count = 0
864         while True:
865             count += 1
866             self.stream.write("Test Pass %d\n" % (count,))
867             if count == 1:
868                 result = self.run(test)
869             else:
870                 result = self._runWithoutDecoration(test)
871             if result.testsRun == 0:
872                 break
873             if not result.wasSuccessful():
874                 break
875         return result
876
Note: See TracBrowser for help on using the browser.