Updated to latest PyUnit version (1.31 in PyUnit CVS); test_support.py
changed accordingly.
diff --git a/Lib/unittest.py b/Lib/unittest.py
index b3eec18..850c38e 100644
--- a/Lib/unittest.py
+++ b/Lib/unittest.py
@@ -1,17 +1,32 @@
 #!/usr/bin/env python
-"""
+'''
 Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's
 Smalltalk testing framework.
 
-Further information is available in the bundled documentation, and from
-
-  http://pyunit.sourceforge.net/
-
 This module contains the core framework classes that form the basis of
 specific test cases and suites (TestCase, TestSuite etc.), and also a
 text-based utility class for running the tests and reporting the results
 (TextTestRunner).
 
+Simple usage:
+
+    import unittest
+
+    class IntegerArithmenticTestCase(unittest.TestCase):
+        def testAdd(self):  ## test method names begin 'test*'
+            self.assertEquals((1 + 2), 3)
+            self.assertEquals(0 + 1, 1)
+        def testMultiply(self);
+            self.assertEquals((0 * 10), 0)
+            self.assertEquals((5 * 8), 40)
+
+    if __name__ == '__main__':
+        unittest.main()
+
+Further information is available in the bundled documentation, and from
+
+  http://pyunit.sourceforge.net/
+
 Copyright (c) 1999, 2000, 2001 Steve Purcell
 This module is free software, and you may redistribute it and/or modify
 it under the same terms as Python itself, so long as this copyright message
@@ -27,9 +42,10 @@
 PARTICULAR PURPOSE.  THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
 AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
-"""
+'''
 
-__author__ = "Steve Purcell (stephen_purcell@yahoo.com)"
+__author__ = "Steve Purcell"
+__email__ = "stephen_purcell at yahoo dot com"
 __version__ = "$Revision$"[11:-2]
 
 import time
@@ -37,15 +53,7 @@
 import traceback
 import string
 import os
-
-##############################################################################
-# A platform-specific concession to help the code work for JPython users
-##############################################################################
-
-plat = string.lower(sys.platform)
-_isJPython = string.find(plat, 'java') >= 0 or string.find(plat, 'jdk') >= 0
-del plat
-
+import types
 
 ##############################################################################
 # Test framework core
@@ -84,6 +92,10 @@
         "Called when a failure has occurred"
         self.failures.append((test, err))
 
+    def addSuccess(self, test):
+        "Called when a test has completed successfully"
+        pass
+
     def wasSuccessful(self):
         "Tells whether or not this result was a success"
         return len(self.failures) == len(self.errors) == 0
@@ -101,17 +113,23 @@
 class TestCase:
     """A class whose instances are single test cases.
 
-    Test authors should subclass TestCase for their own tests. Construction 
-    and deconstruction of the test's environment ('fixture') can be
-    implemented by overriding the 'setUp' and 'tearDown' methods respectively.
-
     By default, the test code itself should be placed in a method named
     'runTest'.
-    
+
     If the fixture may be used for many test cases, create as 
     many test methods as are needed. When instantiating such a TestCase
     subclass, specify in the constructor arguments the name of the test method
     that the instance is to execute.
+
+    Test authors should subclass TestCase for their own tests. Construction 
+    and deconstruction of the test's environment ('fixture') can be
+    implemented by overriding the 'setUp' and 'tearDown' methods respectively.
+
+    If it is necessary to override the __init__ method, the base class
+    __init__ method must always be called. It is important that subclasses
+    should not change the signature of their __init__ method, since instances
+    of the classes are instantiated automatically by parts of the framework
+    in order to be run.
     """
     def __init__(self, methodName='runTest'):
         """Create an instance of the class that will use the named test
@@ -119,7 +137,9 @@
            not have a method with the specified name.
         """
         try:
-            self.__testMethod = getattr(self,methodName)
+            self.__testMethodName = methodName
+            testMethod = getattr(self, methodName)
+            self.__testMethodDoc = testMethod.__doc__
         except AttributeError:
             raise ValueError, "no such test method in %s: %s" % \
                   (self.__class__, methodName)
@@ -145,18 +165,18 @@
         The default implementation of this method returns the first line of
         the specified test method's docstring.
         """
-        doc = self.__testMethod.__doc__
+        doc = self.__testMethodDoc
         return doc and string.strip(string.split(doc, "\n")[0]) or None
 
     def id(self):
-        return "%s.%s" % (self.__class__, self.__testMethod.__name__)
+        return "%s.%s" % (self.__class__, self.__testMethodName)
 
     def __str__(self):
-        return "%s (%s)" % (self.__testMethod.__name__, self.__class__)
+        return "%s (%s)" % (self.__testMethodName, self.__class__)
 
     def __repr__(self):
         return "<%s testMethod=%s>" % \
-               (self.__class__, self.__testMethod.__name__)
+               (self.__class__, self.__testMethodName)
 
     def run(self, result=None):
         return self(result)
@@ -164,6 +184,7 @@
     def __call__(self, result=None):
         if result is None: result = self.defaultTestResult()
         result.startTest(self)
+        testMethod = getattr(self, self.__testMethodName)
         try:
             try:
                 self.setUp()
@@ -171,8 +192,10 @@
                 result.addError(self,self.__exc_info())
                 return
 
+            ok = 0
             try:
-                self.__testMethod()
+                testMethod()
+                ok = 1
             except AssertionError, e:
                 result.addFailure(self,self.__exc_info())
             except:
@@ -182,12 +205,15 @@
                 self.tearDown()
             except:
                 result.addError(self,self.__exc_info())
+                ok = 0
+            if ok: result.addSuccess(self)
         finally:
             result.stopTest(self)
 
     def debug(self):
+        """Run the test without collecting errors in a TestResult"""
         self.setUp()
-        self.__testMethod()
+        getattr(self, self.__testMethodName)()
         self.tearDown()
 
     def assert_(self, expr, msg=None):
@@ -220,10 +246,26 @@
             else: excName = str(excClass)
             raise AssertionError, excName
 
+    def assertEquals(self, first, second, msg=None):
+        """Assert that the two objects are equal as determined by the '=='
+           operator.
+        """
+        self.assert_((first == second), msg or '%s != %s' % (first, second))
+
+    def assertNotEquals(self, first, second, msg=None):
+        """Assert that the two objects are unequal as determined by the '!='
+           operator.
+        """
+        self.assert_((first != second), msg or '%s == %s' % (first, second))
+
+    assertEqual = assertEquals
+
+    assertNotEqual = assertNotEquals
+
     def fail(self, msg=None):
         """Fail immediately, with the given message."""
         raise AssertionError, msg
-                                   
+
     def __exc_info(self):
         """Return a version of sys.exc_info() with the traceback frame
            minimised; usually the top level of the traceback frame is not
@@ -278,8 +320,8 @@
         return result
 
     def debug(self):
+        """Run the tests without collecting errors in a TestResult"""
         for test in self._tests: test.debug()
-        
 
 
 class FunctionTestCase(TestCase):
@@ -327,84 +369,100 @@
 
 
 ##############################################################################
-# Convenience functions
+# Locating and loading tests
 ##############################################################################
 
-def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp):
-    """Extracts all the names of functions in the given test case class
-       and its base classes that start with the given prefix. This is used
-       by makeSuite().
+class TestLoader:
+    """This class is responsible for loading tests according to various
+    criteria and returning them wrapped in a Test
+
+    It can load all tests within a given, module
     """
-    testFnNames = filter(lambda n,p=prefix: n[:len(p)] == p,
-                         dir(testCaseClass))
-    for baseclass in testCaseClass.__bases__:
-        testFnNames = testFnNames + \
-                      getTestCaseNames(baseclass, prefix, sortUsing=None)
-    if sortUsing:
-        testFnNames.sort(sortUsing)
-    return testFnNames
+    testMethodPrefix = 'test'
+    sortTestMethodsUsing = cmp
+    suiteClass = TestSuite
 
+    def loadTestsFromTestCase(self, testCaseClass):
+        return self.suiteClass(map(testCaseClass,
+                                   self.getTestCaseNames(testCaseClass)))
 
-def makeSuite(testCaseClass, prefix='test', sortUsing=cmp):
-    """Returns a TestSuite instance built from all of the test functions
-       in the given test case class whose names begin with the given
-       prefix. The cases are sorted by their function names
-       using the supplied comparison function, which defaults to 'cmp'.
-    """
-    cases = map(testCaseClass,
-                getTestCaseNames(testCaseClass, prefix, sortUsing))
-    return TestSuite(cases)
+    def loadTestsFromModule(self, module):
+        tests = []
+        for name in dir(module):
+            obj = getattr(module, name)
+            if type(obj) == types.ClassType and issubclass(obj, TestCase):
+                tests.append(self.loadTestsFromTestCase(obj))
+        return self.suiteClass(tests)
 
+    def loadTestsFromName(self, name, module=None):
+        parts = string.split(name, '.')
+        if module is None:
+            if not parts:
+                raise ValueError, "incomplete test name: %s" % name
+            else:
+                module = __import__(parts)
+                parts = parts[1:]
+        obj = module
+        for part in parts:
+            obj = getattr(obj, part)
 
-def createTestInstance(name, module=None):
-    """Finds tests by their name, optionally only within the given module.
-
-    Return the newly-constructed test, ready to run. If the name contains a ':'
-    then the portion of the name after the colon is used to find a specific
-    test case within the test case class named before the colon.
-
-    Examples:
-     findTest('examples.listtests.suite')
-        -- returns result of calling 'suite'
-     findTest('examples.listtests.ListTestCase:checkAppend')
-        -- returns result of calling ListTestCase('checkAppend')
-     findTest('examples.listtests.ListTestCase:check-')
-        -- returns result of calling makeSuite(ListTestCase, prefix="check")
-    """
-          
-    spec = string.split(name, ':')
-    if len(spec) > 2: raise ValueError, "illegal test name: %s" % name
-    if len(spec) == 1:
-        testName = spec[0]
-        caseName = None
-    else:
-        testName, caseName = spec
-    parts = string.split(testName, '.')
-    if module is None:
-        if len(parts) < 2:
-            raise ValueError, "incomplete test name: %s" % name
-        constructor = __import__(string.join(parts[:-1],'.'))
-        parts = parts[1:]
-    else:
-        constructor = module
-    for part in parts:
-        constructor = getattr(constructor, part)
-    if not callable(constructor):
-        raise ValueError, "%s is not a callable object" % constructor
-    if caseName:
-        if caseName[-1] == '-':
-            prefix = caseName[:-1]
-            if not prefix:
-                raise ValueError, "prefix too short: %s" % name
-            test = makeSuite(constructor, prefix=prefix)
+        if type(obj) == types.ModuleType:
+            return self.loadTestsFromModule(obj)
+        elif type(obj) == types.ClassType and issubclass(obj, TestCase):
+            return self.loadTestsFromTestCase(obj)
+        elif type(obj) == types.UnboundMethodType:
+            return obj.im_class(obj.__name__)
+        elif callable(obj):
+            test = obj()
+            if not isinstance(test, TestCase) and \
+               not isinstance(test, TestSuite):
+                raise ValueError, \
+                      "calling %s returned %s, not a test" % obj,test
+            return test
         else:
-            test = constructor(caseName)
-    else:
-        test = constructor()
-    if not hasattr(test,"countTestCases"):
-        raise TypeError, \
-              "object %s found with spec %s is not a test" % (test, name)
-    return test
+            raise ValueError, "don't know how to make test from: %s" % obj
+
+    def loadTestsFromNames(self, names, module=None):
+        suites = []
+        for name in names:
+            suites.append(self.loadTestsFromName(name, module))
+        return self.suiteClass(suites)
+
+    def getTestCaseNames(self, testCaseClass):
+        testFnNames = filter(lambda n,p=self.testMethodPrefix: n[:len(p)] == p,
+                             dir(testCaseClass))
+        for baseclass in testCaseClass.__bases__:
+            for testFnName in self.getTestCaseNames(baseclass):
+                if testFnName not in testFnNames:  # handle overridden methods
+                    testFnNames.append(testFnName)
+        if self.sortTestMethodsUsing:
+            testFnNames.sort(self.sortTestMethodsUsing)
+        return testFnNames
+
+
+
+defaultTestLoader = TestLoader()
+
+
+##############################################################################
+# Patches for old functions: these functions should be considered obsolete
+##############################################################################
+
+def _makeLoader(prefix, sortUsing, suiteClass=None):
+    loader = TestLoader()
+    loader.sortTestMethodsUsing = sortUsing
+    loader.testMethodPrefix = prefix
+    if suiteClass: loader.suiteClass = suiteClass
+    return loader
+
+def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp):
+    return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass)
+
+def makeSuite(testCaseClass, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
+    return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass)
+
+def findTestCases(module, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
+    return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(module)
 
 
 ##############################################################################
@@ -415,181 +473,107 @@
     """Used to decorate file-like objects with a handy 'writeln' method"""
     def __init__(self,stream):
         self.stream = stream
-        if _isJPython:
-            import java.lang.System
-            self.linesep = java.lang.System.getProperty("line.separator")
-        else:
-            self.linesep = os.linesep
 
     def __getattr__(self, attr):
         return getattr(self.stream,attr)
 
     def writeln(self, *args):
         if args: apply(self.write, args)
-        self.write(self.linesep)
+        self.write('\n') # text-mode streams translate to \r\n if needed
+ 
 
-        
-class _JUnitTextTestResult(TestResult):
+class _TextTestResult(TestResult):
     """A test result class that can print formatted text results to a stream.
 
-    Used by JUnitTextTestRunner.
+    Used by TextTestRunner.
     """
-    def __init__(self, stream):
-        self.stream = stream
-        TestResult.__init__(self)
+    separator1 = '=' * 70
+    separator2 = '-' * 70
 
-    def addError(self, test, error):
-        TestResult.addError(self,test,error)
-        self.stream.write('E')
-        self.stream.flush()
-        if error[0] is KeyboardInterrupt:
-            self.shouldStop = 1
- 
-    def addFailure(self, test, error):
-        TestResult.addFailure(self,test,error)
-        self.stream.write('F')
-        self.stream.flush()
- 
-    def startTest(self, test):
-        TestResult.startTest(self,test)
-        self.stream.write('.')
-        self.stream.flush()
-
-    def printNumberedErrors(self,errFlavour,errors):
-        if not errors: return
-        if len(errors) == 1:
-            self.stream.writeln("There was 1 %s:" % errFlavour)
-        else:
-            self.stream.writeln("There were %i %ss:" %
-                                (len(errors), errFlavour))
-        i = 1
-        for test,error in errors:
-            errString = string.join(apply(traceback.format_exception,error),"")
-            self.stream.writeln("%i) %s" % (i, test))
-            self.stream.writeln(errString)
-            i = i + 1
- 
-    def printErrors(self):
-        self.printNumberedErrors("error",self.errors)
-
-    def printFailures(self):
-        self.printNumberedErrors("failure",self.failures)
-
-    def printHeader(self):
-        self.stream.writeln()
-        if self.wasSuccessful():
-            self.stream.writeln("OK (%i tests)" % self.testsRun)
-        else:
-            self.stream.writeln("!!!FAILURES!!!")
-            self.stream.writeln("Test Results")
-            self.stream.writeln()
-            self.stream.writeln("Run: %i ; Failures: %i ; Errors: %i" %
-                                (self.testsRun, len(self.failures),
-                                 len(self.errors)))
-            
-    def printResult(self):
-        self.printHeader()
-        self.printErrors()
-        self.printFailures()
-
-
-class JUnitTextTestRunner:
-    """A test runner class that displays results in textual form.
-    
-    The display format approximates that of JUnit's 'textui' test runner.
-    This test runner may be removed in a future version of PyUnit.
-    """
-    def __init__(self, stream=sys.stderr):
-        self.stream = _WritelnDecorator(stream)
-
-    def run(self, test):
-        "Run the given test case or test suite."
-        result = _JUnitTextTestResult(self.stream)
-        startTime = time.time()
-        test(result)
-        stopTime = time.time()
-        self.stream.writeln()
-        self.stream.writeln("Time: %.3fs" % float(stopTime - startTime))
-        result.printResult()
-        return result
-
-
-##############################################################################
-# Verbose text UI
-##############################################################################
-
-class _VerboseTextTestResult(TestResult):
-    """A test result class that can print formatted text results to a stream.
-
-    Used by VerboseTextTestRunner.
-    """
-    def __init__(self, stream, descriptions):
+    def __init__(self, stream, descriptions, verbosity):
         TestResult.__init__(self)
         self.stream = stream
-        self.lastFailure = None
+        self.showAll = verbosity > 1
+        self.dots = verbosity == 1
         self.descriptions = descriptions
-        
+
+    def getDescription(self, test):
+        if self.descriptions:
+            return test.shortDescription() or str(test)
+        else:
+            return str(test)
+
     def startTest(self, test):
         TestResult.startTest(self, test)
-        if self.descriptions:
-            self.stream.write(test.shortDescription() or str(test))
-        else:
-            self.stream.write(str(test))
-        self.stream.write(" ... ")
+        if self.showAll:
+            self.stream.write(self.getDescription(test))
+            self.stream.write(" ... ")
 
-    def stopTest(self, test):
-        TestResult.stopTest(self, test)
-        if self.lastFailure is not test:
+    def addSuccess(self, test):
+        TestResult.addSuccess(self, test)
+        if self.showAll:
             self.stream.writeln("ok")
+        elif self.dots:
+            self.stream.write('.')
 
     def addError(self, test, err):
         TestResult.addError(self, test, err)
-        self._printError("ERROR", test, err)
-        self.lastFailure = test
+        if self.showAll:
+            self.stream.writeln("ERROR")
+        elif self.dots:
+            self.stream.write('E')
         if err[0] is KeyboardInterrupt:
             self.shouldStop = 1
 
     def addFailure(self, test, err):
         TestResult.addFailure(self, test, err)
-        self._printError("FAIL", test, err)
-        self.lastFailure = test
+        if self.showAll:
+            self.stream.writeln("FAIL")
+        elif self.dots:
+            self.stream.write('F')
 
-    def _printError(self, flavour, test, err):
-        errLines = []
-        separator1 = "\t" + '=' * 70
-        separator2 = "\t" + '-' * 70
-        if not self.lastFailure is test:
+    def printErrors(self):
+        if self.dots or self.showAll:
             self.stream.writeln()
-            self.stream.writeln(separator1)
-        self.stream.writeln("\t%s" % flavour)
-        self.stream.writeln(separator2)
-        for line in apply(traceback.format_exception, err):
-            for l in string.split(line,"\n")[:-1]:
-                self.stream.writeln("\t%s" % l)
-        self.stream.writeln(separator1)
+        self.printErrorList('ERROR', self.errors)
+        self.printErrorList('FAIL', self.failures)
+
+    def printErrorList(self, flavour, errors):
+        for test, err in errors:
+            self.stream.writeln(self.separator1)
+            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
+            self.stream.writeln(self.separator2)
+            for line in apply(traceback.format_exception, err):
+                for l in string.split(line,"\n")[:-1]:
+                    self.stream.writeln("%s" % l)
 
 
-class VerboseTextTestRunner:
+class TextTestRunner:
     """A test runner class that displays results in textual form.
     
     It prints out the names of tests as they are run, errors as they
     occur, and a summary of the results at the end of the test run.
     """
-    def __init__(self, stream=sys.stderr, descriptions=1):
+    def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1):
         self.stream = _WritelnDecorator(stream)
         self.descriptions = descriptions
+        self.verbosity = verbosity
+
+    def _makeResult(self):
+        return _TextTestResult(self.stream, self.descriptions, self.verbosity)
 
     def run(self, test):
         "Run the given test case or test suite."
-        result = _VerboseTextTestResult(self.stream, self.descriptions)
+        result = self._makeResult()
         startTime = time.time()
         test(result)
         stopTime = time.time()
         timeTaken = float(stopTime - startTime)
-        self.stream.writeln("-" * 78)
+        result.printErrors()
+        self.stream.writeln(result.separator2)
         run = result.testsRun
         self.stream.writeln("Ran %d test%s in %.3fs" %
-                            (run, run > 1 and "s" or "", timeTaken))
+                            (run, run == 1 and "" or "s", timeTaken))
         self.stream.writeln()
         if not result.wasSuccessful():
             self.stream.write("FAILED (")
@@ -605,9 +589,6 @@
         return result
         
 
-# Which flavour of TextTestRunner is the default?
-TextTestRunner = VerboseTextTestRunner
-
 
 ##############################################################################
 # Facilities for running tests from the command line
@@ -618,17 +599,22 @@
        for making test modules conveniently executable.
     """
     USAGE = """\
-Usage: %(progName)s [-h|--help] [test[:(casename|prefix-)]] [...]
+Usage: %(progName)s [options] [test[:(casename|prefix-)]] [...]
+
+Options:
+  -h, --help       Show this message
+  -v, --verbose    Verbose output
+  -q, --quiet      Minimal output
 
 Examples:
   %(progName)s                               - run default set of tests
   %(progName)s MyTestSuite                   - run suite 'MyTestSuite'
-  %(progName)s MyTestCase:checkSomething     - run MyTestCase.checkSomething
-  %(progName)s MyTestCase:check-             - run all 'check*' test methods
+  %(progName)s MyTestCase.testSomething      - run MyTestCase.testSomething
+  %(progName)s MyTestCase                    - run all 'test*' test methods
                                                in MyTestCase
 """
     def __init__(self, module='__main__', defaultTest=None,
-                 argv=None, testRunner=None):
+                 argv=None, testRunner=None, testLoader=defaultTestLoader):
         if type(module) == type(''):
             self.module = __import__(module)
             for part in string.split(module,'.')[1:]:
@@ -637,11 +623,12 @@
             self.module = module
         if argv is None:
             argv = sys.argv
+        self.verbosity = 1
         self.defaultTest = defaultTest
         self.testRunner = testRunner
+        self.testLoader = testLoader
         self.progName = os.path.basename(argv[0])
         self.parseArgs(argv)
-        self.createTests()
         self.runTests()
 
     def usageExit(self, msg=None):
@@ -652,29 +639,34 @@
     def parseArgs(self, argv):
         import getopt
         try:
-            options, args = getopt.getopt(argv[1:], 'hH', ['help'])
+            options, args = getopt.getopt(argv[1:], 'hHvq',
+                                          ['help','verbose','quiet'])
             opts = {}
             for opt, value in options:
                 if opt in ('-h','-H','--help'):
                     self.usageExit()
+                if opt in ('-q','--quiet'):
+                    self.verbosity = 0
+                if opt in ('-v','--verbose'):
+                    self.verbosity = 2
             if len(args) == 0 and self.defaultTest is None:
-                raise getopt.error, "No default test is defined."
+                self.test = self.testLoader.loadTestsFromModule(self.module)
+                return
             if len(args) > 0:
                 self.testNames = args
             else:
                 self.testNames = (self.defaultTest,)
+            self.createTests()
         except getopt.error, msg:
             self.usageExit(msg)
 
     def createTests(self):
-        tests = []
-        for testName in self.testNames:
-            tests.append(createTestInstance(testName, self.module))
-        self.test = TestSuite(tests)
+        self.test = self.testLoader.loadTestsFromNames(self.testNames,
+                                                       self.module)
 
     def runTests(self):
         if self.testRunner is None:
-            self.testRunner = TextTestRunner()
+            self.testRunner = TextTestRunner(verbosity=self.verbosity)
         result = self.testRunner.run(self.test)
         sys.exit(not result.wasSuccessful())