blob: c50066f5897fa2af976b0f180f4da8b860902a75 [file] [log] [blame]
Benjamin Petersonbed7d042009-07-19 21:01:52 +00001"""Unittest main program"""
2
3import sys
4import os
Benjamin Petersonbed7d042009-07-19 21:01:52 +00005
6from . import loader, runner
Michael Foord65b69a12010-03-27 13:25:41 +00007from .signals import installHandler
Benjamin Petersonbed7d042009-07-19 21:01:52 +00008
Benjamin Petersondccc1fc2010-03-22 00:15:53 +00009__unittest = True
10
Benjamin Petersonb48af542010-04-11 20:43:16 +000011FAILFAST = " -f, --failfast Stop on first failure\n"
12CATCHBREAK = " -c, --catch Catch control-C and display results\n"
13BUFFEROUTPUT = " -b, --buffer Buffer stdout and stderr during test runs\n"
Michael Foord65b69a12010-03-27 13:25:41 +000014
Benjamin Petersonbed7d042009-07-19 21:01:52 +000015USAGE_AS_MAIN = """\
16Usage: %(progName)s [options] [tests]
17
18Options:
19 -h, --help Show this message
20 -v, --verbose Verbose output
21 -q, --quiet Minimal output
Benjamin Petersonb48af542010-04-11 20:43:16 +000022%(failfast)s%(catchbreak)s%(buffer)s
Benjamin Petersonbed7d042009-07-19 21:01:52 +000023Examples:
Michael Foordf707aa72010-05-10 20:23:58 +000024 %(progName)s test_module - run tests from test_module
25 %(progName)s module.TestClass - run tests from module.TestClass
26 %(progName)s module.Class.test_method - run specified test method
Benjamin Petersonbed7d042009-07-19 21:01:52 +000027
28[tests] can be a list of any number of test modules, classes and test
29methods.
30
31Alternative Usage: %(progName)s discover [options]
32
33Options:
34 -v, --verbose Verbose output
Benjamin Petersonb48af542010-04-11 20:43:16 +000035%(failfast)s%(catchbreak)s%(buffer)s -s directory Directory to start discovery ('.' default)
Benjamin Petersonbed7d042009-07-19 21:01:52 +000036 -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
Benjamin Petersonb48af542010-04-11 20:43:16 +000051%(failfast)s%(catchbreak)s%(buffer)s
Benjamin Petersonbed7d042009-07-19 21:01:52 +000052Examples:
53 %(progName)s - run default set of tests
54 %(progName)s MyTestSuite - run suite 'MyTestSuite'
55 %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething
56 %(progName)s MyTestCase - run all 'test*' test methods
57 in MyTestCase
58"""
59
Michael Foord37d120a2010-12-04 01:11:21 +000060def _convert_name(name):
61 # on Linux / Mac OS X 'foo.PY' is not importable, but on
62 # Windows it is. Simpler to do a case insensitive match
63 # a better check would be to check that the name is a
64 # valid Python module name.
65 if os.path.isfile(name) and name.lower().endswith('.py'):
66 if os.path.isabs(name):
67 rel_path = os.path.relpath(name, os.getcwd())
68 if os.path.isabs(rel_path) or rel_path.startswith(os.pardir):
69 return name
70 name = rel_path
71 # on Windows both '\' and '/' are used as path
72 # separators. Better to replace both than rely on os.path.sep
73 return name[:-3].replace('\\', '.').replace('/', '.')
74 return name
Michael Foord65b69a12010-03-27 13:25:41 +000075
Michael Foord37d120a2010-12-04 01:11:21 +000076def _convert_names(names):
77 return [_convert_name(name) for name in names]
Michael Foord65b69a12010-03-27 13:25:41 +000078
Benjamin Petersonbed7d042009-07-19 21:01:52 +000079class TestProgram(object):
80 """A command-line program that runs a set of tests; this is primarily
81 for making test modules conveniently executable.
82 """
Benjamin Petersoneab4b4c2010-03-22 02:53:52 +000083 USAGE = USAGE_FROM_MODULE
Michael Foord65b69a12010-03-27 13:25:41 +000084
85 # defaults for testing
Ezio Melotti60901872010-12-01 00:56:10 +000086 failfast = catchbreak = buffer = progName = warnings = None
Michael Foord65b69a12010-03-27 13:25:41 +000087
Michael Foord4a8cf3c2010-05-07 15:35:24 +000088 def __init__(self, module='__main__', defaultTest=None, argv=None,
89 testRunner=None, testLoader=loader.defaultTestLoader,
90 exit=True, verbosity=1, failfast=None, catchbreak=None,
Ezio Melotti60901872010-12-01 00:56:10 +000091 buffer=None, warnings=None):
Benjamin Petersonbed7d042009-07-19 21:01:52 +000092 if isinstance(module, str):
93 self.module = __import__(module)
94 for part in module.split('.')[1:]:
95 self.module = getattr(self.module, part)
96 else:
97 self.module = module
98 if argv is None:
99 argv = sys.argv
100
101 self.exit = exit
Benjamin Peterson8769fd82010-03-22 01:13:48 +0000102 self.failfast = failfast
Michael Foord65b69a12010-03-27 13:25:41 +0000103 self.catchbreak = catchbreak
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000104 self.verbosity = verbosity
Benjamin Petersonb48af542010-04-11 20:43:16 +0000105 self.buffer = buffer
Ezio Melotti60901872010-12-01 00:56:10 +0000106 if warnings is None and not sys.warnoptions:
107 # even if DreprecationWarnings are ignored by default
108 # print them anyway unless other warnings settings are
109 # specified by the warnings arg or the -W python flag
110 self.warnings = 'default'
111 else:
112 # here self.warnings is set either to the value passed
113 # to the warnings args or to None.
114 # If the user didn't pass a value self.warnings will
115 # be None. This means that the behavior is unchanged
116 # and depends on the values passed to -W.
117 self.warnings = warnings
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000118 self.defaultTest = defaultTest
119 self.testRunner = testRunner
120 self.testLoader = testLoader
121 self.progName = os.path.basename(argv[0])
122 self.parseArgs(argv)
123 self.runTests()
124
125 def usageExit(self, msg=None):
126 if msg:
127 print(msg)
Benjamin Petersonb48af542010-04-11 20:43:16 +0000128 usage = {'progName': self.progName, 'catchbreak': '', 'failfast': '',
129 'buffer': ''}
Michael Foord65b69a12010-03-27 13:25:41 +0000130 if self.failfast != False:
131 usage['failfast'] = FAILFAST
132 if self.catchbreak != False:
133 usage['catchbreak'] = CATCHBREAK
Benjamin Petersonb48af542010-04-11 20:43:16 +0000134 if self.buffer != False:
135 usage['buffer'] = BUFFEROUTPUT
Michael Foord65b69a12010-03-27 13:25:41 +0000136 print(self.USAGE % usage)
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000137 sys.exit(2)
138
139 def parseArgs(self, argv):
Michael Foord086f3082010-11-21 21:28:01 +0000140 if ((len(argv) > 1 and argv[1].lower() == 'discover') or
141 (len(argv) == 1 and self.module is None)):
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000142 self._do_discovery(argv[2:])
143 return
144
145 import getopt
Benjamin Petersonb48af542010-04-11 20:43:16 +0000146 long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer']
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000147 try:
Benjamin Petersonb48af542010-04-11 20:43:16 +0000148 options, args = getopt.getopt(argv[1:], 'hHvqfcb', long_opts)
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000149 except getopt.error as msg:
150 self.usageExit(msg)
Michael Foordf100dbd2010-12-19 03:59:10 +0000151 return
152
153 for opt, value in options:
154 if opt in ('-h','-H','--help'):
155 self.usageExit()
156 if opt in ('-q','--quiet'):
157 self.verbosity = 0
158 if opt in ('-v','--verbose'):
159 self.verbosity = 2
160 if opt in ('-f','--failfast'):
161 if self.failfast is None:
162 self.failfast = True
163 # Should this raise an exception if -f is not valid?
164 if opt in ('-c','--catch'):
165 if self.catchbreak is None:
166 self.catchbreak = True
167 # Should this raise an exception if -c is not valid?
168 if opt in ('-b','--buffer'):
169 if self.buffer is None:
170 self.buffer = True
171 # Should this raise an exception if -b is not valid?
172
173 if len(args) == 0 and self.module is None:
174 # this allows "python -m unittest -v" to still work for
175 # test discovery. This means -c / -b / -v / -f options will
176 # be handled twice, which is harmless but not ideal.
177 self._do_discovery(argv[1:])
178 return
179
180 if len(args) == 0 and self.defaultTest is None:
181 # createTests will load tests from self.module
182 self.testNames = None
183 elif len(args) > 0:
184 self.testNames = _convert_names(args)
185 if __name__ == '__main__':
186 # to support python -m unittest ...
187 self.module = None
188 else:
189 self.testNames = (self.defaultTest,)
190 self.createTests()
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000191
192 def createTests(self):
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000193 if self.testNames is None:
194 self.test = self.testLoader.loadTestsFromModule(self.module)
195 else:
196 self.test = self.testLoader.loadTestsFromNames(self.testNames,
197 self.module)
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000198
199 def _do_discovery(self, argv, Loader=loader.TestLoader):
200 # handle command line args for test discovery
Michael Foordf707aa72010-05-10 20:23:58 +0000201 self.progName = '%s discover' % self.progName
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000202 import optparse
203 parser = optparse.OptionParser()
Michael Foordf707aa72010-05-10 20:23:58 +0000204 parser.prog = self.progName
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000205 parser.add_option('-v', '--verbose', dest='verbose', default=False,
206 help='Verbose output', action='store_true')
Michael Foord65b69a12010-03-27 13:25:41 +0000207 if self.failfast != False:
208 parser.add_option('-f', '--failfast', dest='failfast', default=False,
209 help='Stop on first fail or error',
210 action='store_true')
211 if self.catchbreak != False:
212 parser.add_option('-c', '--catch', dest='catchbreak', default=False,
213 help='Catch ctrl-C and display results so far',
214 action='store_true')
Benjamin Petersonb48af542010-04-11 20:43:16 +0000215 if self.buffer != False:
216 parser.add_option('-b', '--buffer', dest='buffer', default=False,
217 help='Buffer stdout and stderr during tests',
218 action='store_true')
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000219 parser.add_option('-s', '--start-directory', dest='start', default='.',
220 help="Directory to start discovery ('.' default)")
221 parser.add_option('-p', '--pattern', dest='pattern', default='test*.py',
222 help="Pattern to match tests ('test*.py' default)")
223 parser.add_option('-t', '--top-level-directory', dest='top', default=None,
224 help='Top level directory of project (defaults to start directory)')
225
226 options, args = parser.parse_args(argv)
227 if len(args) > 3:
228 self.usageExit()
229
230 for name, value in zip(('start', 'pattern', 'top'), args):
231 setattr(options, name, value)
232
Michael Foord65b69a12010-03-27 13:25:41 +0000233 # only set options from the parsing here
234 # if they weren't set explicitly in the constructor
235 if self.failfast is None:
236 self.failfast = options.failfast
237 if self.catchbreak is None:
238 self.catchbreak = options.catchbreak
Benjamin Petersonb48af542010-04-11 20:43:16 +0000239 if self.buffer is None:
240 self.buffer = options.buffer
Michael Foord65b69a12010-03-27 13:25:41 +0000241
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000242 if options.verbose:
243 self.verbosity = 2
244
245 start_dir = options.start
246 pattern = options.pattern
247 top_level_dir = options.top
248
249 loader = Loader()
250 self.test = loader.discover(start_dir, pattern, top_level_dir)
251
252 def runTests(self):
Michael Foord65b69a12010-03-27 13:25:41 +0000253 if self.catchbreak:
254 installHandler()
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000255 if self.testRunner is None:
256 self.testRunner = runner.TextTestRunner
257 if isinstance(self.testRunner, type):
258 try:
Benjamin Peterson8769fd82010-03-22 01:13:48 +0000259 testRunner = self.testRunner(verbosity=self.verbosity,
Benjamin Petersonb48af542010-04-11 20:43:16 +0000260 failfast=self.failfast,
Ezio Melotti60901872010-12-01 00:56:10 +0000261 buffer=self.buffer,
262 warnings=self.warnings)
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000263 except TypeError:
Benjamin Petersonb48af542010-04-11 20:43:16 +0000264 # didn't accept the verbosity, buffer or failfast arguments
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000265 testRunner = self.testRunner()
266 else:
267 # it is assumed to be a TestRunner instance
268 testRunner = self.testRunner
269 self.result = testRunner.run(self.test)
270 if self.exit:
271 sys.exit(not self.result.wasSuccessful())
272
273main = TestProgram