Home | History | Annotate | Download | only in unittest
      1 """Unittest main program"""
      2 
      3 import sys
      4 import os
      5 import types
      6 
      7 from . import loader, runner
      8 from .signals import installHandler
      9 
     10 __unittest = True
     11 
     12 FAILFAST     = "  -f, --failfast   Stop on first failure\n"
     13 CATCHBREAK   = "  -c, --catch      Catch control-C and display results\n"
     14 BUFFEROUTPUT = "  -b, --buffer     Buffer stdout and stderr during test runs\n"
     15 
     16 USAGE_AS_MAIN = """\
     17 Usage: %(progName)s [options] [tests]
     18 
     19 Options:
     20   -h, --help       Show this message
     21   -v, --verbose    Verbose output
     22   -q, --quiet      Minimal output
     23 %(failfast)s%(catchbreak)s%(buffer)s
     24 Examples:
     25   %(progName)s test_module               - run tests from test_module
     26   %(progName)s module.TestClass          - run tests from module.TestClass
     27   %(progName)s module.Class.test_method  - run specified test method
     28 
     29 [tests] can be a list of any number of test modules, classes and test
     30 methods.
     31 
     32 Alternative Usage: %(progName)s discover [options]
     33 
     34 Options:
     35   -v, --verbose    Verbose output
     36 %(failfast)s%(catchbreak)s%(buffer)s  -s directory     Directory to start discovery ('.' default)
     37   -p pattern       Pattern to match test files ('test*.py' default)
     38   -t directory     Top level directory of project (default to
     39                    start directory)
     40 
     41 For test discovery all test modules must be importable from the top
     42 level directory of the project.
     43 """
     44 
     45 USAGE_FROM_MODULE = """\
     46 Usage: %(progName)s [options] [test] [...]
     47 
     48 Options:
     49   -h, --help       Show this message
     50   -v, --verbose    Verbose output
     51   -q, --quiet      Minimal output
     52 %(failfast)s%(catchbreak)s%(buffer)s
     53 Examples:
     54   %(progName)s                               - run default set of tests
     55   %(progName)s MyTestSuite                   - run suite 'MyTestSuite'
     56   %(progName)s MyTestCase.testSomething      - run MyTestCase.testSomething
     57   %(progName)s MyTestCase                    - run all 'test*' test methods
     58                                                in MyTestCase
     59 """
     60 
     61 
     62 
     63 class TestProgram(object):
     64     """A command-line program that runs a set of tests; this is primarily
     65        for making test modules conveniently executable.
     66     """
     67     USAGE = USAGE_FROM_MODULE
     68 
     69     # defaults for testing
     70     failfast = catchbreak = buffer = progName = None
     71 
     72     def __init__(self, module='__main__', defaultTest=None, argv=None,
     73                     testRunner=None, testLoader=loader.defaultTestLoader,
     74                     exit=True, verbosity=1, failfast=None, catchbreak=None,
     75                     buffer=None):
     76         if isinstance(module, basestring):
     77             self.module = __import__(module)
     78             for part in module.split('.')[1:]:
     79                 self.module = getattr(self.module, part)
     80         else:
     81             self.module = module
     82         if argv is None:
     83             argv = sys.argv
     84 
     85         self.exit = exit
     86         self.failfast = failfast
     87         self.catchbreak = catchbreak
     88         self.verbosity = verbosity
     89         self.buffer = buffer
     90         self.defaultTest = defaultTest
     91         self.testRunner = testRunner
     92         self.testLoader = testLoader
     93         self.progName = os.path.basename(argv[0])
     94         self.parseArgs(argv)
     95         self.runTests()
     96 
     97     def usageExit(self, msg=None):
     98         if msg:
     99             print msg
    100         usage = {'progName': self.progName, 'catchbreak': '', 'failfast': '',
    101                  'buffer': ''}
    102         if self.failfast != False:
    103             usage['failfast'] = FAILFAST
    104         if self.catchbreak != False:
    105             usage['catchbreak'] = CATCHBREAK
    106         if self.buffer != False:
    107             usage['buffer'] = BUFFEROUTPUT
    108         print self.USAGE % usage
    109         sys.exit(2)
    110 
    111     def parseArgs(self, argv):
    112         if len(argv) > 1 and argv[1].lower() == 'discover':
    113             self._do_discovery(argv[2:])
    114             return
    115 
    116         import getopt
    117         long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer']
    118         try:
    119             options, args = getopt.getopt(argv[1:], 'hHvqfcb', long_opts)
    120             for opt, value in options:
    121                 if opt in ('-h','-H','--help'):
    122                     self.usageExit()
    123                 if opt in ('-q','--quiet'):
    124                     self.verbosity = 0
    125                 if opt in ('-v','--verbose'):
    126                     self.verbosity = 2
    127                 if opt in ('-f','--failfast'):
    128                     if self.failfast is None:
    129                         self.failfast = True
    130                     # Should this raise an exception if -f is not valid?
    131                 if opt in ('-c','--catch'):
    132                     if self.catchbreak is None:
    133                         self.catchbreak = True
    134                     # Should this raise an exception if -c is not valid?
    135                 if opt in ('-b','--buffer'):
    136                     if self.buffer is None:
    137                         self.buffer = True
    138                     # Should this raise an exception if -b is not valid?
    139             if len(args) == 0 and self.defaultTest is None:
    140                 # createTests will load tests from self.module
    141                 self.testNames = None
    142             elif len(args) > 0:
    143                 self.testNames = args
    144                 if __name__ == '__main__':
    145                     # to support python -m unittest ...
    146                     self.module = None
    147             else:
    148                 self.testNames = (self.defaultTest,)
    149             self.createTests()
    150         except getopt.error, msg:
    151             self.usageExit(msg)
    152 
    153     def createTests(self):
    154         if self.testNames is None:
    155             self.test = self.testLoader.loadTestsFromModule(self.module)
    156         else:
    157             self.test = self.testLoader.loadTestsFromNames(self.testNames,
    158                                                            self.module)
    159 
    160     def _do_discovery(self, argv, Loader=None):
    161         if Loader is None:
    162             Loader = lambda: self.testLoader
    163 
    164         # handle command line args for test discovery
    165         self.progName = '%s discover' % self.progName
    166         import optparse
    167         parser = optparse.OptionParser()
    168         parser.prog = self.progName
    169         parser.add_option('-v', '--verbose', dest='verbose', default=False,
    170                           help='Verbose output', action='store_true')
    171         if self.failfast != False:
    172             parser.add_option('-f', '--failfast', dest='failfast', default=False,
    173                               help='Stop on first fail or error',
    174                               action='store_true')
    175         if self.catchbreak != False:
    176             parser.add_option('-c', '--catch', dest='catchbreak', default=False,
    177                               help='Catch ctrl-C and display results so far',
    178                               action='store_true')
    179         if self.buffer != False:
    180             parser.add_option('-b', '--buffer', dest='buffer', default=False,
    181                               help='Buffer stdout and stderr during tests',
    182                               action='store_true')
    183         parser.add_option('-s', '--start-directory', dest='start', default='.',
    184                           help="Directory to start discovery ('.' default)")
    185         parser.add_option('-p', '--pattern', dest='pattern', default='test*.py',
    186                           help="Pattern to match tests ('test*.py' default)")
    187         parser.add_option('-t', '--top-level-directory', dest='top', default=None,
    188                           help='Top level directory of project (defaults to start directory)')
    189 
    190         options, args = parser.parse_args(argv)
    191         if len(args) > 3:
    192             self.usageExit()
    193 
    194         for name, value in zip(('start', 'pattern', 'top'), args):
    195             setattr(options, name, value)
    196 
    197         # only set options from the parsing here
    198         # if they weren't set explicitly in the constructor
    199         if self.failfast is None:
    200             self.failfast = options.failfast
    201         if self.catchbreak is None:
    202             self.catchbreak = options.catchbreak
    203         if self.buffer is None:
    204             self.buffer = options.buffer
    205 
    206         if options.verbose:
    207             self.verbosity = 2
    208 
    209         start_dir = options.start
    210         pattern = options.pattern
    211         top_level_dir = options.top
    212 
    213         loader = Loader()
    214         self.test = loader.discover(start_dir, pattern, top_level_dir)
    215 
    216     def runTests(self):
    217         if self.catchbreak:
    218             installHandler()
    219         if self.testRunner is None:
    220             self.testRunner = runner.TextTestRunner
    221         if isinstance(self.testRunner, (type, types.ClassType)):
    222             try:
    223                 testRunner = self.testRunner(verbosity=self.verbosity,
    224                                              failfast=self.failfast,
    225                                              buffer=self.buffer)
    226             except TypeError:
    227                 # didn't accept the verbosity, buffer or failfast arguments
    228                 testRunner = self.testRunner()
    229         else:
    230             # it is assumed to be a TestRunner instance
    231             testRunner = self.testRunner
    232         self.result = testRunner.run(self.test)
    233         if self.exit:
    234             sys.exit(not self.result.wasSuccessful())
    235 
    236 main = TestProgram
    237