main.py 8.81 KB
Newer Older
1 2 3 4 5 6 7
"""Unittest main program"""

import sys
import os
import types

from . import loader, runner
8
from .signals import installHandler
9

10 11
__unittest = True

12 13 14
FAILFAST     = "  -f, --failfast   Stop on first failure\n"
CATCHBREAK   = "  -c, --catch      Catch control-C and display results\n"
BUFFEROUTPUT = "  -b, --buffer     Buffer stdout and stderr during test runs\n"
15

16 17 18 19 20 21 22
USAGE_AS_MAIN = """\
Usage: %(progName)s [options] [tests]

Options:
  -h, --help       Show this message
  -v, --verbose    Verbose output
  -q, --quiet      Minimal output
23
%(failfast)s%(catchbreak)s%(buffer)s
24
Examples:
25 26 27
  %(progName)s test_module               - run tests from test_module
  %(progName)s module.TestClass          - run tests from module.TestClass
  %(progName)s module.Class.test_method  - run specified test method
28 29 30 31 32 33 34 35

[tests] can be a list of any number of test modules, classes and test
methods.

Alternative Usage: %(progName)s discover [options]

Options:
  -v, --verbose    Verbose output
36
%(failfast)s%(catchbreak)s%(buffer)s  -s directory     Directory to start discovery ('.' default)
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
  -p pattern       Pattern to match test files ('test*.py' default)
  -t directory     Top level directory of project (default to
                   start directory)

For test discovery all test modules must be importable from the top
level directory of the project.
"""

USAGE_FROM_MODULE = """\
Usage: %(progName)s [options] [test] [...]

Options:
  -h, --help       Show this message
  -v, --verbose    Verbose output
  -q, --quiet      Minimal output
52
%(failfast)s%(catchbreak)s%(buffer)s
53 54 55 56 57 58 59 60
Examples:
  %(progName)s                               - run default set of tests
  %(progName)s MyTestSuite                   - run suite 'MyTestSuite'
  %(progName)s MyTestCase.testSomething      - run MyTestCase.testSomething
  %(progName)s MyTestCase                    - run all 'test*' test methods
                                               in MyTestCase
"""

61 62


63 64 65 66
class TestProgram(object):
    """A command-line program that runs a set of tests; this is primarily
       for making test modules conveniently executable.
    """
67
    USAGE = USAGE_FROM_MODULE
68 69

    # defaults for testing
70
    failfast = catchbreak = buffer = progName = None
71

72 73 74 75
    def __init__(self, module='__main__', defaultTest=None, argv=None,
                    testRunner=None, testLoader=loader.defaultTestLoader,
                    exit=True, verbosity=1, failfast=None, catchbreak=None,
                    buffer=None):
76 77 78 79 80 81 82 83 84 85
        if isinstance(module, basestring):
            self.module = __import__(module)
            for part in module.split('.')[1:]:
                self.module = getattr(self.module, part)
        else:
            self.module = module
        if argv is None:
            argv = sys.argv

        self.exit = exit
86
        self.failfast = failfast
87
        self.catchbreak = catchbreak
88
        self.verbosity = verbosity
89
        self.buffer = buffer
90 91 92 93 94 95 96 97 98 99
        self.defaultTest = defaultTest
        self.testRunner = testRunner
        self.testLoader = testLoader
        self.progName = os.path.basename(argv[0])
        self.parseArgs(argv)
        self.runTests()

    def usageExit(self, msg=None):
        if msg:
            print msg
100 101
        usage = {'progName': self.progName, 'catchbreak': '', 'failfast': '',
                 'buffer': ''}
102 103 104 105
        if self.failfast != False:
            usage['failfast'] = FAILFAST
        if self.catchbreak != False:
            usage['catchbreak'] = CATCHBREAK
106 107
        if self.buffer != False:
            usage['buffer'] = BUFFEROUTPUT
108
        print self.USAGE % usage
109 110 111 112 113 114 115 116
        sys.exit(2)

    def parseArgs(self, argv):
        if len(argv) > 1 and argv[1].lower() == 'discover':
            self._do_discovery(argv[2:])
            return

        import getopt
117
        long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer']
118
        try:
119
            options, args = getopt.getopt(argv[1:], 'hHvqfcb', long_opts)
120 121 122 123 124 125 126
            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
127
                if opt in ('-f','--failfast'):
128 129 130 131 132 133 134
                    if self.failfast is None:
                        self.failfast = True
                    # Should this raise an exception if -f is not valid?
                if opt in ('-c','--catch'):
                    if self.catchbreak is None:
                        self.catchbreak = True
                    # Should this raise an exception if -c is not valid?
135 136 137 138
                if opt in ('-b','--buffer'):
                    if self.buffer is None:
                        self.buffer = True
                    # Should this raise an exception if -b is not valid?
139
            if len(args) == 0 and self.defaultTest is None:
140 141 142
                # createTests will load tests from self.module
                self.testNames = None
            elif len(args) > 0:
143 144 145 146 147 148 149 150 151 152 153
                self.testNames = args
                if __name__ == '__main__':
                    # to support python -m unittest ...
                    self.module = None
            else:
                self.testNames = (self.defaultTest,)
            self.createTests()
        except getopt.error, msg:
            self.usageExit(msg)

    def createTests(self):
154 155 156 157 158
        if self.testNames is None:
            self.test = self.testLoader.loadTestsFromModule(self.module)
        else:
            self.test = self.testLoader.loadTestsFromNames(self.testNames,
                                                           self.module)
159 160 161

    def _do_discovery(self, argv, Loader=loader.TestLoader):
        # handle command line args for test discovery
162
        self.progName = '%s discover' % self.progName
163 164
        import optparse
        parser = optparse.OptionParser()
165
        parser.prog = self.progName
166 167
        parser.add_option('-v', '--verbose', dest='verbose', default=False,
                          help='Verbose output', action='store_true')
168 169 170 171 172 173 174 175
        if self.failfast != False:
            parser.add_option('-f', '--failfast', dest='failfast', default=False,
                              help='Stop on first fail or error',
                              action='store_true')
        if self.catchbreak != False:
            parser.add_option('-c', '--catch', dest='catchbreak', default=False,
                              help='Catch ctrl-C and display results so far',
                              action='store_true')
176 177 178 179
        if self.buffer != False:
            parser.add_option('-b', '--buffer', dest='buffer', default=False,
                              help='Buffer stdout and stderr during tests',
                              action='store_true')
180 181 182 183 184 185 186 187 188 189 190 191 192 193
        parser.add_option('-s', '--start-directory', dest='start', default='.',
                          help="Directory to start discovery ('.' default)")
        parser.add_option('-p', '--pattern', dest='pattern', default='test*.py',
                          help="Pattern to match tests ('test*.py' default)")
        parser.add_option('-t', '--top-level-directory', dest='top', default=None,
                          help='Top level directory of project (defaults to start directory)')

        options, args = parser.parse_args(argv)
        if len(args) > 3:
            self.usageExit()

        for name, value in zip(('start', 'pattern', 'top'), args):
            setattr(options, name, value)

194 195 196 197 198 199
        # only set options from the parsing here
        # if they weren't set explicitly in the constructor
        if self.failfast is None:
            self.failfast = options.failfast
        if self.catchbreak is None:
            self.catchbreak = options.catchbreak
200 201
        if self.buffer is None:
            self.buffer = options.buffer
202

203 204 205 206 207 208 209 210 211 212 213
        if options.verbose:
            self.verbosity = 2

        start_dir = options.start
        pattern = options.pattern
        top_level_dir = options.top

        loader = Loader()
        self.test = loader.discover(start_dir, pattern, top_level_dir)

    def runTests(self):
214 215
        if self.catchbreak:
            installHandler()
216 217 218 219
        if self.testRunner is None:
            self.testRunner = runner.TextTestRunner
        if isinstance(self.testRunner, (type, types.ClassType)):
            try:
220
                testRunner = self.testRunner(verbosity=self.verbosity,
221 222
                                             failfast=self.failfast,
                                             buffer=self.buffer)
223
            except TypeError:
224
                # didn't accept the verbosity, buffer or failfast arguments
225 226 227 228 229 230 231 232 233
                testRunner = self.testRunner()
        else:
            # it is assumed to be a TestRunner instance
            testRunner = self.testRunner
        self.result = testRunner.run(self.test)
        if self.exit:
            sys.exit(not self.result.wasSuccessful())

main = TestProgram