blob: 9db1d307bf523435d08d3d1a96dbb7af80424ee7 [file] [log] [blame]
Johnny Chen75e28f92010-08-05 23:42:46 +00001"""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