~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to pyunit/unittest.py

  • Committer: Bazaar Package Importer
  • Author(s): Moshe Zadka
  • Date: 2002-03-08 07:14:16 UTC
  • Revision ID: james.westby@ubuntu.com-20020308071416-oxvuw76tpcpi5v1q
Tags: upstream-0.15.5
ImportĀ upstreamĀ versionĀ 0.15.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
'''
 
3
Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's
 
4
Smalltalk testing framework.
 
5
 
 
6
This module contains the core framework classes that form the basis of
 
7
specific test cases and suites (TestCase, TestSuite etc.), and also a
 
8
text-based utility class for running the tests and reporting the results
 
9
(TextTestRunner).
 
10
 
 
11
Simple usage:
 
12
 
 
13
    import unittest
 
14
 
 
15
    class IntegerArithmenticTestCase(unittest.TestCase):
 
16
        def testAdd(self):  ## test method names begin 'test*'
 
17
            self.assertEquals((1 + 2), 3)
 
18
            self.assertEquals(0 + 1, 1)
 
19
        def testMultiply(self);
 
20
            self.assertEquals((0 * 10), 0)
 
21
            self.assertEquals((5 * 8), 40)
 
22
 
 
23
    if __name__ == '__main__':
 
24
        unittest.main()
 
25
 
 
26
Further information is available in the bundled documentation, and from
 
27
 
 
28
  http://pyunit.sourceforge.net/
 
29
 
 
30
Copyright (c) 1999, 2000, 2001 Steve Purcell
 
31
This module is free software, and you may redistribute it and/or modify
 
32
it under the same terms as Python itself, so long as this copyright message
 
33
and disclaimer are retained in their original form.
 
34
 
 
35
IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
 
36
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
 
37
THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 
38
DAMAGE.
 
39
 
 
40
THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
 
41
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 
42
PARTICULAR PURPOSE.  THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
 
43
AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 
44
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 
45
'''
 
46
 
 
47
__author__ = "Steve Purcell"
 
48
__email__ = "stephen_purcell at yahoo dot com"
 
49
__version__ = "Revision: 1.40"
 
50
 
 
51
import time
 
52
import sys
 
53
import traceback
 
54
import string
 
55
import os
 
56
import types
 
57
 
 
58
##############################################################################
 
59
# Test framework core
 
60
##############################################################################
 
61
 
 
62
class TestResult:
 
63
    """Holder for test result information.
 
64
 
 
65
    Test results are automatically managed by the TestCase and TestSuite
 
66
    classes, and do not need to be explicitly manipulated by writers of tests.
 
67
 
 
68
    Each instance holds the total number of tests run, and collections of
 
69
    failures and errors that occurred among those test runs. The collections
 
70
    contain tuples of (testcase, exceptioninfo), where exceptioninfo is a
 
71
    tuple of values as returned by sys.exc_info().
 
72
    """
 
73
    def __init__(self):
 
74
        self.failures = []
 
75
        self.errors = []
 
76
        self.testsRun = 0
 
77
        self.shouldStop = 0
 
78
 
 
79
    def startTest(self, test):
 
80
        "Called when the given test is about to be run"
 
81
        self.testsRun = self.testsRun + 1
 
82
 
 
83
    def stopTest(self, test):
 
84
        "Called when the given test has been run"
 
85
        pass
 
86
 
 
87
    def addError(self, test, err):
 
88
        "Called when an error has occurred"
 
89
        self.errors.append((test, err))
 
90
 
 
91
    def addFailure(self, test, err):
 
92
        "Called when a failure has occurred"
 
93
        self.failures.append((test, err))
 
94
 
 
95
    def addSuccess(self, test):
 
96
        "Called when a test has completed successfully"
 
97
        pass
 
98
 
 
99
    def wasSuccessful(self):
 
100
        "Tells whether or not this result was a success"
 
101
        return len(self.failures) == len(self.errors) == 0
 
102
 
 
103
    def stop(self):
 
104
        "Indicates that the tests should be aborted"
 
105
        self.shouldStop = 1
 
106
 
 
107
    def __repr__(self):
 
108
        return "<%s run=%i errors=%i failures=%i>" % \
 
109
               (self.__class__, self.testsRun, len(self.errors),
 
110
                len(self.failures))
 
111
 
 
112
 
 
113
class TestCase:
 
114
    """A class whose instances are single test cases.
 
115
 
 
116
    By default, the test code itself should be placed in a method named
 
117
    'runTest'.
 
118
 
 
119
    If the fixture may be used for many test cases, create as
 
120
    many test methods as are needed. When instantiating such a TestCase
 
121
    subclass, specify in the constructor arguments the name of the test method
 
122
    that the instance is to execute.
 
123
 
 
124
    Test authors should subclass TestCase for their own tests. Construction
 
125
    and deconstruction of the test's environment ('fixture') can be
 
126
    implemented by overriding the 'setUp' and 'tearDown' methods respectively.
 
127
 
 
128
    If it is necessary to override the __init__ method, the base class
 
129
    __init__ method must always be called. It is important that subclasses
 
130
    should not change the signature of their __init__ method, since instances
 
131
    of the classes are instantiated automatically by parts of the framework
 
132
    in order to be run.
 
133
    """
 
134
 
 
135
    # This attribute determines which exception will be raised when
 
136
    # the instance's assertion methods fail; test methods raising this
 
137
    # exception will be deemed to have 'failed' rather than 'errored'
 
138
 
 
139
    failureException = AssertionError
 
140
 
 
141
    def __init__(self, methodName='runTest'):
 
142
        """Create an instance of the class that will use the named test
 
143
           method when executed. Raises a ValueError if the instance does
 
144
           not have a method with the specified name.
 
145
        """
 
146
        try:
 
147
            self.__testMethodName = methodName
 
148
            testMethod = getattr(self, methodName)
 
149
            self.__testMethodDoc = testMethod.__doc__
 
150
        except AttributeError:
 
151
            raise ValueError, "no such test method in %s: %s" % \
 
152
                  (self.__class__, methodName)
 
153
 
 
154
    def setUp(self):
 
155
        "Hook method for setting up the test fixture before exercising it."
 
156
        pass
 
157
 
 
158
    def tearDown(self):
 
159
        "Hook method for deconstructing the test fixture after testing it."
 
160
        pass
 
161
 
 
162
    def countTestCases(self):
 
163
        return 1
 
164
 
 
165
    def defaultTestResult(self):
 
166
        return TestResult()
 
167
 
 
168
    def shortDescription(self):
 
169
        """Returns a one-line description of the test, or None if no
 
170
        description has been provided.
 
171
 
 
172
        The default implementation of this method returns the first line of
 
173
        the specified test method's docstring.
 
174
        """
 
175
        doc = self.__testMethodDoc
 
176
        return doc and string.strip(string.split(doc, "\n")[0]) or None
 
177
 
 
178
    def id(self):
 
179
        return "%s.%s" % (self.__class__, self.__testMethodName)
 
180
 
 
181
    def __str__(self):
 
182
        return "%s (%s)" % (self.__testMethodName, self.__class__)
 
183
 
 
184
    def __repr__(self):
 
185
        return "<%s testMethod=%s>" % \
 
186
               (self.__class__, self.__testMethodName)
 
187
 
 
188
    def run(self, result=None):
 
189
        return self(result)
 
190
 
 
191
    def __call__(self, result=None):
 
192
        if result is None: result = self.defaultTestResult()
 
193
        result.startTest(self)
 
194
        testMethod = getattr(self, self.__testMethodName)
 
195
        try:
 
196
            try:
 
197
                self.setUp()
 
198
            except:
 
199
                result.addError(self,self.__exc_info())
 
200
                return
 
201
 
 
202
            ok = 0
 
203
            try:
 
204
                testMethod()
 
205
                ok = 1
 
206
            except self.failureException, e:
 
207
                result.addFailure(self,self.__exc_info())
 
208
            except:
 
209
                result.addError(self,self.__exc_info())
 
210
 
 
211
            try:
 
212
                self.tearDown()
 
213
            except:
 
214
                result.addError(self,self.__exc_info())
 
215
                ok = 0
 
216
            if ok: result.addSuccess(self)
 
217
        finally:
 
218
            result.stopTest(self)
 
219
 
 
220
    def debug(self):
 
221
        """Run the test without collecting errors in a TestResult"""
 
222
        self.setUp()
 
223
        getattr(self, self.__testMethodName)()
 
224
        self.tearDown()
 
225
 
 
226
    def __exc_info(self):
 
227
        """Return a version of sys.exc_info() with the traceback frame
 
228
           minimised; usually the top level of the traceback frame is not
 
229
           needed.
 
230
        """
 
231
        exctype, excvalue, tb = sys.exc_info()
 
232
        if sys.platform[:4] == 'java': ## tracebacks look different in Jython
 
233
            return (exctype, excvalue, tb)
 
234
        newtb = tb.tb_next
 
235
        if newtb is None:
 
236
            return (exctype, excvalue, tb)
 
237
        return (exctype, excvalue, newtb)
 
238
 
 
239
    def fail(self, msg=None):
 
240
        """Fail immediately, with the given message."""
 
241
        raise self.failureException, msg
 
242
 
 
243
    def failIf(self, expr, msg=None):
 
244
        "Fail the test if the expression is true."
 
245
        if expr: raise self.failureException, msg
 
246
 
 
247
    def failUnless(self, expr, msg=None):
 
248
        """Fail the test unless the expression is true."""
 
249
        if not expr: raise self.failureException, msg
 
250
 
 
251
    def failUnlessRaises(self, excClass, callableObj, *args, **kwargs):
 
252
        """Fail unless an exception of class excClass is thrown
 
253
           by callableObj when invoked with arguments args and keyword
 
254
           arguments kwargs. If a different type of exception is
 
255
           thrown, it will not be caught, and the test case will be
 
256
           deemed to have suffered an error, exactly as for an
 
257
           unexpected exception.
 
258
        """
 
259
        try:
 
260
            apply(callableObj, args, kwargs)
 
261
        except excClass:
 
262
            return
 
263
        else:
 
264
            if hasattr(excClass,'__name__'): excName = excClass.__name__
 
265
            else: excName = str(excClass)
 
266
            raise self.failureException, excName
 
267
 
 
268
    def failUnlessEqual(self, first, second, msg=None):
 
269
        """Fail if the two objects are unequal as determined by the '!='
 
270
           operator.
 
271
        """
 
272
        if first != second:
 
273
            raise self.failureException, (msg or '%s != %s' % (first, second))
 
274
 
 
275
    def failIfEqual(self, first, second, msg=None):
 
276
        """Fail if the two objects are equal as determined by the '=='
 
277
           operator.
 
278
        """
 
279
        if first == second:
 
280
            raise self.failureException, (msg or '%s == %s' % (first, second))
 
281
 
 
282
    assertEqual = assertEquals = failUnlessEqual
 
283
 
 
284
    assertNotEqual = assertNotEquals = failIfEqual
 
285
 
 
286
    assertRaises = failUnlessRaises
 
287
 
 
288
    assert_ = failUnless
 
289
 
 
290
 
 
291
 
 
292
class TestSuite:
 
293
    """A test suite is a composite test consisting of a number of TestCases.
 
294
 
 
295
    For use, create an instance of TestSuite, then add test case instances.
 
296
    When all tests have been added, the suite can be passed to a test
 
297
    runner, such as TextTestRunner. It will run the individual test cases
 
298
    in the order in which they were added, aggregating the results. When
 
299
    subclassing, do not forget to call the base class constructor.
 
300
    """
 
301
    def __init__(self, tests=()):
 
302
        self._tests = []
 
303
        self.addTests(tests)
 
304
 
 
305
    def __repr__(self):
 
306
        return "<%s tests=%s>" % (self.__class__, self._tests)
 
307
 
 
308
    __str__ = __repr__
 
309
 
 
310
    def countTestCases(self):
 
311
        cases = 0
 
312
        for test in self._tests:
 
313
            cases = cases + test.countTestCases()
 
314
        return cases
 
315
 
 
316
    def addTest(self, test):
 
317
        self._tests.append(test)
 
318
 
 
319
    def addTests(self, tests):
 
320
        for test in tests:
 
321
            self.addTest(test)
 
322
 
 
323
    def run(self, result):
 
324
        return self(result)
 
325
 
 
326
    def __call__(self, result):
 
327
        for test in self._tests:
 
328
            if result.shouldStop:
 
329
                break
 
330
            test(result)
 
331
        return result
 
332
 
 
333
    def debug(self):
 
334
        """Run the tests without collecting errors in a TestResult"""
 
335
        for test in self._tests: test.debug()
 
336
 
 
337
 
 
338
class FunctionTestCase(TestCase):
 
339
    """A test case that wraps a test function.
 
340
 
 
341
    This is useful for slipping pre-existing test functions into the
 
342
    PyUnit framework. Optionally, set-up and tidy-up functions can be
 
343
    supplied. As with TestCase, the tidy-up ('tearDown') function will
 
344
    always be called if the set-up ('setUp') function ran successfully.
 
345
    """
 
346
 
 
347
    def __init__(self, testFunc, setUp=None, tearDown=None,
 
348
                 description=None):
 
349
        TestCase.__init__(self)
 
350
        self.__setUpFunc = setUp
 
351
        self.__tearDownFunc = tearDown
 
352
        self.__testFunc = testFunc
 
353
        self.__description = description
 
354
 
 
355
    def setUp(self):
 
356
        if self.__setUpFunc is not None:
 
357
            self.__setUpFunc()
 
358
 
 
359
    def tearDown(self):
 
360
        if self.__tearDownFunc is not None:
 
361
            self.__tearDownFunc()
 
362
 
 
363
    def runTest(self):
 
364
        self.__testFunc()
 
365
 
 
366
    def id(self):
 
367
        return self.__testFunc.__name__
 
368
 
 
369
    def __str__(self):
 
370
        return "%s (%s)" % (self.__class__, self.__testFunc.__name__)
 
371
 
 
372
    def __repr__(self):
 
373
        return "<%s testFunc=%s>" % (self.__class__, self.__testFunc)
 
374
 
 
375
    def shortDescription(self):
 
376
        if self.__description is not None: return self.__description
 
377
        doc = self.__testFunc.__doc__
 
378
        return doc and string.strip(string.split(doc, "\n")[0]) or None
 
379
 
 
380
 
 
381
 
 
382
##############################################################################
 
383
# Locating and loading tests
 
384
##############################################################################
 
385
 
 
386
class TestLoader:
 
387
    """This class is responsible for loading tests according to various
 
388
    criteria and returning them wrapped in a Test
 
389
    """
 
390
    testMethodPrefix = 'test'
 
391
    sortTestMethodsUsing = cmp
 
392
    suiteClass = TestSuite
 
393
 
 
394
    def loadTestsFromTestCase(self, testCaseClass):
 
395
        """Return a suite of all tests cases contained in testCaseClass"""
 
396
        return self.suiteClass(map(testCaseClass,
 
397
                                   self.getTestCaseNames(testCaseClass)))
 
398
 
 
399
    def loadTestsFromModule(self, module):
 
400
        """Return a suite of all tests cases contained in the given module"""
 
401
        tests = []
 
402
        for name in dir(module):
 
403
            obj = getattr(module, name)
 
404
            if type(obj) == types.ClassType and issubclass(obj, TestCase):
 
405
                tests.append(self.loadTestsFromTestCase(obj))
 
406
        return self.suiteClass(tests)
 
407
 
 
408
    def loadTestsFromName(self, name, module=None):
 
409
        """Return a suite of all tests cases given a string specifier.
 
410
 
 
411
        The name may resolve either to a module, a test case class, a
 
412
        test method within a test case class, or a callable object which
 
413
        returns a TestCase or TestSuite instance.
 
414
 
 
415
        The method optionally resolves the names relative to a given module.
 
416
        """
 
417
        parts = string.split(name, '.')
 
418
        if module is None:
 
419
            if not parts:
 
420
                raise ValueError, "incomplete test name: %s" % name
 
421
            else:
 
422
                parts_copy = parts[:]
 
423
                while parts_copy:
 
424
                    try:
 
425
                        module = __import__(string.join(parts_copy,'.'))
 
426
                        break
 
427
                    except ImportError:
 
428
                        del parts_copy[-1]
 
429
                        if not parts_copy: raise
 
430
                parts = parts[1:]
 
431
        obj = module
 
432
        for part in parts:
 
433
            obj = getattr(obj, part)
 
434
 
 
435
        import unittest
 
436
        if type(obj) == types.ModuleType:
 
437
            return self.loadTestsFromModule(obj)
 
438
        elif type(obj) == types.ClassType and issubclass(obj, unittest.TestCase):
 
439
            return self.loadTestsFromTestCase(obj)
 
440
        elif type(obj) == types.UnboundMethodType:
 
441
            return obj.im_class(obj.__name__)
 
442
        elif callable(obj):
 
443
            test = obj()
 
444
            if not isinstance(test, unittest.TestCase) and \
 
445
               not isinstance(test, unittest.TestSuite):
 
446
                raise ValueError, \
 
447
                      "calling %s returned %s, not a test" % (obj,test)
 
448
            return test
 
449
        else:
 
450
            raise ValueError, "don't know how to make test from: %s" % obj
 
451
 
 
452
    def loadTestsFromNames(self, names, module=None):
 
453
        """Return a suite of all tests cases found using the given sequence
 
454
        of string specifiers. See 'loadTestsFromName()'.
 
455
        """
 
456
        suites = []
 
457
        for name in names:
 
458
            suites.append(self.loadTestsFromName(name, module))
 
459
        return self.suiteClass(suites)
 
460
 
 
461
    def getTestCaseNames(self, testCaseClass):
 
462
        """Return a sorted sequence of method names found within testCaseClass
 
463
        """
 
464
        testFnNames = filter(lambda n,p=self.testMethodPrefix: n[:len(p)] == p,
 
465
                             dir(testCaseClass))
 
466
        for baseclass in testCaseClass.__bases__:
 
467
            for testFnName in self.getTestCaseNames(baseclass):
 
468
                if testFnName not in testFnNames:  # handle overridden methods
 
469
                    testFnNames.append(testFnName)
 
470
        if self.sortTestMethodsUsing:
 
471
            testFnNames.sort(self.sortTestMethodsUsing)
 
472
        return testFnNames
 
473
 
 
474
 
 
475
 
 
476
defaultTestLoader = TestLoader()
 
477
 
 
478
 
 
479
##############################################################################
 
480
# Patches for old functions: these functions should be considered obsolete
 
481
##############################################################################
 
482
 
 
483
def _makeLoader(prefix, sortUsing, suiteClass=None):
 
484
    loader = TestLoader()
 
485
    loader.sortTestMethodsUsing = sortUsing
 
486
    loader.testMethodPrefix = prefix
 
487
    if suiteClass: loader.suiteClass = suiteClass
 
488
    return loader
 
489
 
 
490
def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp):
 
491
    return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass)
 
492
 
 
493
def makeSuite(testCaseClass, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
 
494
    return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass)
 
495
 
 
496
def findTestCases(module, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
 
497
    return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(module)
 
498
 
 
499
 
 
500
##############################################################################
 
501
# Text UI
 
502
##############################################################################
 
503
 
 
504
class _WritelnDecorator:
 
505
    """Used to decorate file-like objects with a handy 'writeln' method"""
 
506
    def __init__(self,stream):
 
507
        self.stream = stream
 
508
 
 
509
    def __getattr__(self, attr):
 
510
        return getattr(self.stream,attr)
 
511
 
 
512
    def writeln(self, *args):
 
513
        if args: apply(self.write, args)
 
514
        self.write('\n') # text-mode streams translate to \r\n if needed
 
515
 
 
516
 
 
517
class _TextTestResult(TestResult):
 
518
    """A test result class that can print formatted text results to a stream.
 
519
 
 
520
    Used by TextTestRunner.
 
521
    """
 
522
    separator1 = '=' * 70
 
523
    separator2 = '-' * 70
 
524
 
 
525
    def __init__(self, stream, descriptions, verbosity):
 
526
        TestResult.__init__(self)
 
527
        self.stream = stream
 
528
        self.showAll = verbosity > 1
 
529
        self.dots = verbosity == 1
 
530
        self.descriptions = descriptions
 
531
 
 
532
    def getDescription(self, test):
 
533
        if self.descriptions:
 
534
            return test.shortDescription() or str(test)
 
535
        else:
 
536
            return str(test)
 
537
 
 
538
    def startTest(self, test):
 
539
        TestResult.startTest(self, test)
 
540
        if self.showAll:
 
541
            self.stream.write(self.getDescription(test))
 
542
            self.stream.write(" ... ")
 
543
 
 
544
    def addSuccess(self, test):
 
545
        TestResult.addSuccess(self, test)
 
546
        if self.showAll:
 
547
            self.stream.writeln("ok")
 
548
        elif self.dots:
 
549
            self.stream.write('.')
 
550
 
 
551
    def addError(self, test, err):
 
552
        TestResult.addError(self, test, err)
 
553
        if self.showAll:
 
554
            self.stream.writeln("ERROR")
 
555
        elif self.dots:
 
556
            self.stream.write('E')
 
557
        if err[0] is KeyboardInterrupt:
 
558
            self.shouldStop = 1
 
559
 
 
560
    def addFailure(self, test, err):
 
561
        TestResult.addFailure(self, test, err)
 
562
        if self.showAll:
 
563
            self.stream.writeln("FAIL")
 
564
        elif self.dots:
 
565
            self.stream.write('F')
 
566
 
 
567
    def printErrors(self):
 
568
        if self.dots or self.showAll:
 
569
            self.stream.writeln()
 
570
        self.printErrorList('ERROR', self.errors)
 
571
        self.printErrorList('FAIL', self.failures)
 
572
 
 
573
    def printErrorList(self, flavour, errors):
 
574
        for test, err in errors:
 
575
            self.stream.writeln(self.separator1)
 
576
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
 
577
            self.stream.writeln(self.separator2)
 
578
            for line in apply(traceback.format_exception, err):
 
579
                for l in string.split(line,"\n")[:-1]:
 
580
                    self.stream.writeln("%s" % l)
 
581
 
 
582
 
 
583
class TextTestRunner:
 
584
    """A test runner class that displays results in textual form.
 
585
 
 
586
    It prints out the names of tests as they are run, errors as they
 
587
    occur, and a summary of the results at the end of the test run.
 
588
    """
 
589
    def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1):
 
590
        self.stream = _WritelnDecorator(stream)
 
591
        self.descriptions = descriptions
 
592
        self.verbosity = verbosity
 
593
 
 
594
    def _makeResult(self):
 
595
        return _TextTestResult(self.stream, self.descriptions, self.verbosity)
 
596
 
 
597
    def run(self, test):
 
598
        "Run the given test case or test suite."
 
599
        result = self._makeResult()
 
600
        startTime = time.time()
 
601
        test(result)
 
602
        stopTime = time.time()
 
603
        timeTaken = float(stopTime - startTime)
 
604
        result.printErrors()
 
605
        self.stream.writeln(result.separator2)
 
606
        run = result.testsRun
 
607
        self.stream.writeln("Ran %d test%s in %.3fs" %
 
608
                            (run, run == 1 and "" or "s", timeTaken))
 
609
        self.stream.writeln()
 
610
        if not result.wasSuccessful():
 
611
            self.stream.write("FAILED (")
 
612
            failed, errored = map(len, (result.failures, result.errors))
 
613
            if failed:
 
614
                self.stream.write("failures=%d" % failed)
 
615
            if errored:
 
616
                if failed: self.stream.write(", ")
 
617
                self.stream.write("errors=%d" % errored)
 
618
            self.stream.writeln(")")
 
619
        else:
 
620
            self.stream.writeln("OK")
 
621
        return result
 
622
 
 
623
 
 
624
 
 
625
##############################################################################
 
626
# Facilities for running tests from the command line
 
627
##############################################################################
 
628
 
 
629
class TestProgram:
 
630
    """A command-line program that runs a set of tests; this is primarily
 
631
       for making test modules conveniently executable.
 
632
    """
 
633
    USAGE = """\
 
634
Usage: %(progName)s [options] [test] [...]
 
635
 
 
636
Options:
 
637
  -h, --help       Show this message
 
638
  -v, --verbose    Verbose output
 
639
  -q, --quiet      Minimal output
 
640
 
 
641
Examples:
 
642
  %(progName)s                               - run default set of tests
 
643
  %(progName)s MyTestSuite                   - run suite 'MyTestSuite'
 
644
  %(progName)s MyTestCase.testSomething      - run MyTestCase.testSomething
 
645
  %(progName)s MyTestCase                    - run all 'test*' test methods
 
646
                                               in MyTestCase
 
647
"""
 
648
    def __init__(self, module='__main__', defaultTest=None,
 
649
                 argv=None, testRunner=None, testLoader=defaultTestLoader):
 
650
        if type(module) == type(''):
 
651
            self.module = __import__(module)
 
652
            for part in string.split(module,'.')[1:]:
 
653
                self.module = getattr(self.module, part)
 
654
        else:
 
655
            self.module = module
 
656
        if argv is None:
 
657
            argv = sys.argv
 
658
        self.verbosity = 1
 
659
        self.defaultTest = defaultTest
 
660
        self.testRunner = testRunner
 
661
        self.testLoader = testLoader
 
662
        self.progName = os.path.basename(argv[0])
 
663
        self.parseArgs(argv)
 
664
        self.runTests()
 
665
 
 
666
    def usageExit(self, msg=None):
 
667
        if msg: print msg
 
668
        print self.USAGE % self.__dict__
 
669
        sys.exit(2)
 
670
 
 
671
    def parseArgs(self, argv):
 
672
        import getopt
 
673
        try:
 
674
            options, args = getopt.getopt(argv[1:], 'hHvq',
 
675
                                          ['help','verbose','quiet'])
 
676
            for opt, value in options:
 
677
                if opt in ('-h','-H','--help'):
 
678
                    self.usageExit()
 
679
                if opt in ('-q','--quiet'):
 
680
                    self.verbosity = 0
 
681
                if opt in ('-v','--verbose'):
 
682
                    self.verbosity = 2
 
683
            if len(args) == 0 and self.defaultTest is None:
 
684
                self.test = self.testLoader.loadTestsFromModule(self.module)
 
685
                return
 
686
            if len(args) > 0:
 
687
                self.testNames = args
 
688
            else:
 
689
                self.testNames = (self.defaultTest,)
 
690
            self.createTests()
 
691
        except getopt.error, msg:
 
692
            self.usageExit(msg)
 
693
 
 
694
    def createTests(self):
 
695
        self.test = self.testLoader.loadTestsFromNames(self.testNames,
 
696
                                                       self.module)
 
697
 
 
698
    def runTests(self):
 
699
        if self.testRunner is None:
 
700
            self.testRunner = TextTestRunner(verbosity=self.verbosity)
 
701
        result = self.testRunner.run(self.test)
 
702
        sys.exit(not result.wasSuccessful())
 
703
 
 
704
main = TestProgram
 
705
 
 
706
 
 
707
##############################################################################
 
708
# Executing this module from the command line
 
709
##############################################################################
 
710
 
 
711
if __name__ == "__main__":
 
712
    main(module=None)