1"""Unittest main program"""
2
3import sys
4import os
5import types
6
7from unittest2 import loader, runner
8try:
9    from unittest2.signals import installHandler
10except ImportError:
11    installHandler = None
12
13__unittest = True
14
15FAILFAST     = "  -f, --failfast   Stop on first failure\n"
16CATCHBREAK   = "  -c, --catch      Catch control-C and display results\n"
17BUFFEROUTPUT = "  -b, --buffer     Buffer stdout and stderr during test runs\n"
18
19USAGE_AS_MAIN = """\
20Usage: %(progName)s [options] [tests]
21
22Options:
23  -h, --help       Show this message
24  -v, --verbose    Verbose output
25  -q, --quiet      Minimal output
26%(failfast)s%(catchbreak)s%(buffer)s
27Examples:
28  %(progName)s test_module                       - run tests from test_module
29  %(progName)s test_module.TestClass             - run tests from
30                                                   test_module.TestClass
31  %(progName)s test_module.TestClass.test_method - run specified test method
32
33[tests] can be a list of any number of test modules, classes and test
34methods.
35
36Alternative Usage: %(progName)s discover [options]
37
38Options:
39  -v, --verbose    Verbose output
40%(failfast)s%(catchbreak)s%(buffer)s  -s directory     Directory to start discovery ('.' default)
41  -p pattern       Pattern to match test files ('test*.py' default)
42  -t directory     Top level directory of project (default to
43                   start directory)
44
45For test discovery all test modules must be importable from the top
46level directory of the project.
47"""
48
49USAGE_FROM_MODULE = """\
50Usage: %(progName)s [options] [test] [...]
51
52Options:
53  -h, --help       Show this message
54  -v, --verbose    Verbose output
55  -q, --quiet      Minimal output
56%(failfast)s%(catchbreak)s%(buffer)s
57Examples:
58  %(progName)s                               - run default set of tests
59  %(progName)s MyTestSuite                   - run suite 'MyTestSuite'
60  %(progName)s MyTestCase.testSomething      - run MyTestCase.testSomething
61  %(progName)s MyTestCase                    - run all 'test*' test methods
62                                               in MyTestCase
63"""
64
65
66class TestProgram(object):
67    """A command-line program that runs a set of tests; this is primarily
68       for making test modules conveniently executable.
69    """
70    USAGE = USAGE_FROM_MODULE
71
72    # defaults for testing
73    failfast = catchbreak = buffer = progName = None
74
75    def __init__(self, module='__main__', defaultTest=None,
76                 argv=None, testRunner=None,
77                 testLoader=loader.defaultTestLoader, exit=True,
78                 verbosity=1, failfast=None, catchbreak=None, buffer=None):
79        if isinstance(module, basestring):
80            self.module = __import__(module)
81            for part in module.split('.')[1:]:
82                self.module = getattr(self.module, part)
83        else:
84            self.module = module
85        if argv is None:
86            argv = sys.argv
87
88        self.exit = exit
89        self.verbosity = verbosity
90        self.failfast = failfast
91        self.catchbreak = catchbreak
92        self.buffer = buffer
93        self.defaultTest = defaultTest
94        self.testRunner = testRunner
95        self.testLoader = testLoader
96        self.progName = os.path.basename(argv[0])
97        self.parseArgs(argv)
98        self.runTests()
99
100    def usageExit(self, msg=None):
101        if msg:
102            print msg
103        usage = {'progName': self.progName, 'catchbreak': '', 'failfast': '',
104                 'buffer': ''}
105        if self.failfast != False:
106            usage['failfast'] = FAILFAST
107        if self.catchbreak != False and installHandler is not None:
108            usage['catchbreak'] = CATCHBREAK
109        if self.buffer != False:
110            usage['buffer'] = BUFFEROUTPUT
111        print self.USAGE % usage
112        sys.exit(2)
113
114    def parseArgs(self, argv):
115        if len(argv) > 1 and argv[1].lower() == 'discover':
116            self._do_discovery(argv[2:])
117            return
118
119        import getopt
120        long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer']
121        try:
122            options, args = getopt.getopt(argv[1:], 'hHvqfcb', long_opts)
123            for opt, value in options:
124                if opt in ('-h','-H','--help'):
125                    self.usageExit()
126                if opt in ('-q','--quiet'):
127                    self.verbosity = 0
128                if opt in ('-v','--verbose'):
129                    self.verbosity = 2
130                if opt in ('-f','--failfast'):
131                    if self.failfast is None:
132                        self.failfast = True
133                    # Should this raise an exception if -f is not valid?
134                if opt in ('-c','--catch'):
135                    if self.catchbreak is None and installHandler is not None:
136                        self.catchbreak = True
137                    # Should this raise an exception if -c is not valid?
138                if opt in ('-b','--buffer'):
139                    if self.buffer is None:
140                        self.buffer = True
141                    # Should this raise an exception if -b is not valid?
142            if len(args) == 0 and self.defaultTest is None:
143                # createTests will load tests from self.module
144                self.testNames = None
145            elif len(args) > 0:
146                self.testNames = args
147                if __name__ == '__main__':
148                    # to support python -m unittest ...
149                    self.module = None
150            else:
151                self.testNames = (self.defaultTest,)
152            self.createTests()
153        except getopt.error, msg:
154            self.usageExit(msg)
155
156    def createTests(self):
157        if self.testNames is None:
158            self.test = self.testLoader.loadTestsFromModule(self.module)
159        else:
160            self.test = self.testLoader.loadTestsFromNames(self.testNames,
161                                                           self.module)
162
163    def _do_discovery(self, argv, Loader=loader.TestLoader):
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 and installHandler is not None:
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 and installHandler is not 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
236main = TestProgram
237
238def main_():
239    TestProgram.USAGE = USAGE_AS_MAIN
240    main(module=None)
241
242