blob: d0da7c070a7db515de368cd2cc0a5d8ad6723cec [file] [log] [blame]
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +00001"""Unittest main program"""
2
3import sys
4import os
5import types
6
7from . import loader, runner
8
Michael Foordb1aa30f2010-03-22 00:06:30 +00009__unittest = True
10
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000011
12USAGE_AS_MAIN = """\
13Usage: %(progName)s [options] [tests]
14
15Options:
16 -h, --help Show this message
17 -v, --verbose Verbose output
18 -q, --quiet Minimal output
Michael Foord1b9e9532010-03-22 01:01:34 +000019 -f, --failfast Stop on first failure
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000020
21Examples:
22 %(progName)s test_module - run tests from test_module
23 %(progName)s test_module.TestClass - run tests from
24 test_module.TestClass
25 %(progName)s test_module.TestClass.test_method - run specified test method
26
27[tests] can be a list of any number of test modules, classes and test
28methods.
29
30Alternative Usage: %(progName)s discover [options]
31
32Options:
33 -v, --verbose Verbose output
Michael Foord1b9e9532010-03-22 01:01:34 +000034 -f, --failfast Stop on first failure
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000035 -s directory Directory to start discovery ('.' default)
36 -p pattern Pattern to match test files ('test*.py' default)
37 -t directory Top level directory of project (default to
38 start directory)
39
40For test discovery all test modules must be importable from the top
41level directory of the project.
42"""
43
44USAGE_FROM_MODULE = """\
45Usage: %(progName)s [options] [test] [...]
46
47Options:
48 -h, --help Show this message
49 -v, --verbose Verbose output
50 -q, --quiet Minimal output
Michael Foord1b9e9532010-03-22 01:01:34 +000051 -f, --failfast Stop on first failure
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000052
53Examples:
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
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000061class TestProgram(object):
62 """A command-line program that runs a set of tests; this is primarily
63 for making test modules conveniently executable.
64 """
Michael Foordeef159b2010-03-22 02:49:08 +000065 USAGE = USAGE_FROM_MODULE
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000066 def __init__(self, module='__main__', defaultTest=None,
67 argv=None, testRunner=None,
68 testLoader=loader.defaultTestLoader, exit=True,
Michael Foord1b9e9532010-03-22 01:01:34 +000069 verbosity=1, failfast=False):
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000070 if isinstance(module, basestring):
71 self.module = __import__(module)
72 for part in module.split('.')[1:]:
73 self.module = getattr(self.module, part)
74 else:
75 self.module = module
76 if argv is None:
77 argv = sys.argv
78
79 self.exit = exit
Michael Foord1b9e9532010-03-22 01:01:34 +000080 self.failfast = failfast
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000081 self.verbosity = verbosity
82 self.defaultTest = defaultTest
83 self.testRunner = testRunner
84 self.testLoader = testLoader
85 self.progName = os.path.basename(argv[0])
86 self.parseArgs(argv)
87 self.runTests()
88
89 def usageExit(self, msg=None):
90 if msg:
91 print msg
92 print self.USAGE % self.__dict__
93 sys.exit(2)
94
95 def parseArgs(self, argv):
96 if len(argv) > 1 and argv[1].lower() == 'discover':
97 self._do_discovery(argv[2:])
98 return
99
100 import getopt
Michael Foord1b9e9532010-03-22 01:01:34 +0000101 long_opts = ['help', 'verbose', 'quiet', 'failfast']
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000102 try:
Michael Foord1b9e9532010-03-22 01:01:34 +0000103 options, args = getopt.getopt(argv[1:], 'hHvqf', long_opts)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000104 for opt, value in options:
105 if opt in ('-h','-H','--help'):
106 self.usageExit()
107 if opt in ('-q','--quiet'):
108 self.verbosity = 0
109 if opt in ('-v','--verbose'):
110 self.verbosity = 2
Michael Foord1b9e9532010-03-22 01:01:34 +0000111 if opt in ('-f','--failfast'):
112 self.failfast = True
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000113 if len(args) == 0 and self.defaultTest is None:
Michael Foordd6aabcf2009-09-27 19:15:41 +0000114 # createTests will load tests from self.module
115 self.testNames = None
116 elif len(args) > 0:
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000117 self.testNames = args
118 if __name__ == '__main__':
119 # to support python -m unittest ...
120 self.module = None
121 else:
122 self.testNames = (self.defaultTest,)
123 self.createTests()
124 except getopt.error, msg:
125 self.usageExit(msg)
126
127 def createTests(self):
Michael Foordd6aabcf2009-09-27 19:15:41 +0000128 if self.testNames is None:
129 self.test = self.testLoader.loadTestsFromModule(self.module)
130 else:
131 self.test = self.testLoader.loadTestsFromNames(self.testNames,
132 self.module)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000133
134 def _do_discovery(self, argv, Loader=loader.TestLoader):
135 # handle command line args for test discovery
136 import optparse
137 parser = optparse.OptionParser()
138 parser.add_option('-v', '--verbose', dest='verbose', default=False,
139 help='Verbose output', action='store_true')
Michael Foord1b9e9532010-03-22 01:01:34 +0000140 parser.add_option('-f', '--failfast', dest='failfast', default=False,
141 help='Stop on first fail or error', action='store_true')
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000142 parser.add_option('-s', '--start-directory', dest='start', default='.',
143 help="Directory to start discovery ('.' default)")
144 parser.add_option('-p', '--pattern', dest='pattern', default='test*.py',
145 help="Pattern to match tests ('test*.py' default)")
146 parser.add_option('-t', '--top-level-directory', dest='top', default=None,
147 help='Top level directory of project (defaults to start directory)')
148
149 options, args = parser.parse_args(argv)
150 if len(args) > 3:
151 self.usageExit()
152
153 for name, value in zip(('start', 'pattern', 'top'), args):
154 setattr(options, name, value)
155
Michael Foord49899692010-03-22 01:41:11 +0000156 self.failfast = options.failfast
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000157 if options.verbose:
158 self.verbosity = 2
159
160 start_dir = options.start
161 pattern = options.pattern
162 top_level_dir = options.top
163
164 loader = Loader()
165 self.test = loader.discover(start_dir, pattern, top_level_dir)
166
167 def runTests(self):
168 if self.testRunner is None:
169 self.testRunner = runner.TextTestRunner
170 if isinstance(self.testRunner, (type, types.ClassType)):
171 try:
Michael Foord1b9e9532010-03-22 01:01:34 +0000172 testRunner = self.testRunner(verbosity=self.verbosity,
173 failfast=self.failfast)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000174 except TypeError:
Michael Foord0ce16722010-03-22 01:56:54 +0000175 # didn't accept the verbosity or failfast arguments
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000176 testRunner = self.testRunner()
177 else:
178 # it is assumed to be a TestRunner instance
179 testRunner = self.testRunner
180 self.result = testRunner.run(self.test)
181 if self.exit:
182 sys.exit(not self.result.wasSuccessful())
183
184main = TestProgram