blob: 3f0f43d0e24c22954785a57e68a4d919e00764e1 [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
19
20Examples:
21 %(progName)s test_module - run tests from test_module
22 %(progName)s test_module.TestClass - run tests from
23 test_module.TestClass
24 %(progName)s test_module.TestClass.test_method - run specified test method
25
26[tests] can be a list of any number of test modules, classes and test
27methods.
28
29Alternative Usage: %(progName)s discover [options]
30
31Options:
32 -v, --verbose Verbose output
33 -s directory Directory to start discovery ('.' default)
34 -p pattern Pattern to match test files ('test*.py' default)
35 -t directory Top level directory of project (default to
36 start directory)
37
38For test discovery all test modules must be importable from the top
39level directory of the project.
40"""
41
42USAGE_FROM_MODULE = """\
43Usage: %(progName)s [options] [test] [...]
44
45Options:
46 -h, --help Show this message
47 -v, --verbose Verbose output
48 -q, --quiet Minimal output
49
50Examples:
51 %(progName)s - run default set of tests
52 %(progName)s MyTestSuite - run suite 'MyTestSuite'
53 %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething
54 %(progName)s MyTestCase - run all 'test*' test methods
55 in MyTestCase
56"""
57
58if __name__ == '__main__':
59 USAGE = USAGE_AS_MAIN
60else:
61 USAGE = USAGE_FROM_MODULE
62
63
64class TestProgram(object):
65 """A command-line program that runs a set of tests; this is primarily
66 for making test modules conveniently executable.
67 """
68 USAGE = USAGE
69 def __init__(self, module='__main__', defaultTest=None,
70 argv=None, testRunner=None,
71 testLoader=loader.defaultTestLoader, exit=True,
72 verbosity=1):
73 if isinstance(module, basestring):
74 self.module = __import__(module)
75 for part in module.split('.')[1:]:
76 self.module = getattr(self.module, part)
77 else:
78 self.module = module
79 if argv is None:
80 argv = sys.argv
81
82 self.exit = exit
83 self.verbosity = verbosity
84 self.defaultTest = defaultTest
85 self.testRunner = testRunner
86 self.testLoader = testLoader
87 self.progName = os.path.basename(argv[0])
88 self.parseArgs(argv)
89 self.runTests()
90
91 def usageExit(self, msg=None):
92 if msg:
93 print msg
94 print self.USAGE % self.__dict__
95 sys.exit(2)
96
97 def parseArgs(self, argv):
98 if len(argv) > 1 and argv[1].lower() == 'discover':
99 self._do_discovery(argv[2:])
100 return
101
102 import getopt
103 long_opts = ['help','verbose','quiet']
104 try:
105 options, args = getopt.getopt(argv[1:], 'hHvq', long_opts)
106 for opt, value in options:
107 if opt in ('-h','-H','--help'):
108 self.usageExit()
109 if opt in ('-q','--quiet'):
110 self.verbosity = 0
111 if opt in ('-v','--verbose'):
112 self.verbosity = 2
113 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')
140 parser.add_option('-s', '--start-directory', dest='start', default='.',
141 help="Directory to start discovery ('.' default)")
142 parser.add_option('-p', '--pattern', dest='pattern', default='test*.py',
143 help="Pattern to match tests ('test*.py' default)")
144 parser.add_option('-t', '--top-level-directory', dest='top', default=None,
145 help='Top level directory of project (defaults to start directory)')
146
147 options, args = parser.parse_args(argv)
148 if len(args) > 3:
149 self.usageExit()
150
151 for name, value in zip(('start', 'pattern', 'top'), args):
152 setattr(options, name, value)
153
154 if options.verbose:
155 self.verbosity = 2
156
157 start_dir = options.start
158 pattern = options.pattern
159 top_level_dir = options.top
160
161 loader = Loader()
162 self.test = loader.discover(start_dir, pattern, top_level_dir)
163
164 def runTests(self):
165 if self.testRunner is None:
166 self.testRunner = runner.TextTestRunner
167 if isinstance(self.testRunner, (type, types.ClassType)):
168 try:
169 testRunner = self.testRunner(verbosity=self.verbosity)
170 except TypeError:
171 # didn't accept the verbosity argument
172 testRunner = self.testRunner()
173 else:
174 # it is assumed to be a TestRunner instance
175 testRunner = self.testRunner
176 self.result = testRunner.run(self.test)
177 if self.exit:
178 sys.exit(not self.result.wasSuccessful())
179
180main = TestProgram