~soren/nova/iptables-security-groups

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/trial/runner.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

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