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