blob: e6237b0f6ae82741c32954426767bbbea459c57e [file] [log] [blame]
Benjamin Petersonbed7d042009-07-19 21:01:52 +00001"""Unittest main program"""
2
3import sys
4import os
5import types
6
7from . import loader, runner
8
9
10USAGE_AS_MAIN = """\
11Usage: %(progName)s [options] [tests]
12
13Options:
14 -h, --help Show this message
15 -v, --verbose Verbose output
16 -q, --quiet Minimal output
17
18Examples:
19 %(progName)s test_module - run tests from test_module
20 %(progName)s test_module.TestClass - run tests from
21 test_module.TestClass
22 %(progName)s test_module.TestClass.test_method - run specified test method
23
24[tests] can be a list of any number of test modules, classes and test
25methods.
26
27Alternative Usage: %(progName)s discover [options]
28
29Options:
30 -v, --verbose Verbose output
31 -s directory Directory to start discovery ('.' default)
32 -p pattern Pattern to match test files ('test*.py' default)
33 -t directory Top level directory of project (default to
34 start directory)
35
36For test discovery all test modules must be importable from the top
37level directory of the project.
38"""
39
40USAGE_FROM_MODULE = """\
41Usage: %(progName)s [options] [test] [...]
42
43Options:
44 -h, --help Show this message
45 -v, --verbose Verbose output
46 -q, --quiet Minimal output
47
48Examples:
49 %(progName)s - run default set of tests
50 %(progName)s MyTestSuite - run suite 'MyTestSuite'
51 %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething
52 %(progName)s MyTestCase - run all 'test*' test methods
53 in MyTestCase
54"""
55
56if __name__ == '__main__':
57 USAGE = USAGE_AS_MAIN
58else:
59 USAGE = USAGE_FROM_MODULE
60
61
62class TestProgram(object):
63 """A command-line program that runs a set of tests; this is primarily
64 for making test modules conveniently executable.
65 """
66 USAGE = USAGE
67 def __init__(self, module='__main__', defaultTest=None,
68 argv=None, testRunner=None,
69 testLoader=loader.defaultTestLoader, exit=True,
70 verbosity=1):
71 if isinstance(module, str):
72 self.module = __import__(module)
73 for part in module.split('.')[1:]:
74 self.module = getattr(self.module, part)
75 else:
76 self.module = module
77 if argv is None:
78 argv = sys.argv
79
80 self.exit = exit
81 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
101 long_opts = ['help','verbose','quiet']
102 try:
103 options, args = getopt.getopt(argv[1:], 'hHvq', long_opts)
104 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
111 if len(args) == 0 and self.defaultTest is None:
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000112 # createTests will load tests from self.module
113 self.testNames = None
114 elif len(args) > 0:
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000115 self.testNames = args
116 if __name__ == '__main__':
117 # to support python -m unittest ...
118 self.module = None
119 else:
120 self.testNames = (self.defaultTest,)
121 self.createTests()
122 except getopt.error as msg:
123 self.usageExit(msg)
124
125 def createTests(self):
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000126 if self.testNames is None:
127 self.test = self.testLoader.loadTestsFromModule(self.module)
128 else:
129 self.test = self.testLoader.loadTestsFromNames(self.testNames,
130 self.module)
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000131
132 def _do_discovery(self, argv, Loader=loader.TestLoader):
133 # handle command line args for test discovery
134 import optparse
135 parser = optparse.OptionParser()
136 parser.add_option('-v', '--verbose', dest='verbose', default=False,
137 help='Verbose output', action='store_true')
138 parser.add_option('-s', '--start-directory', dest='start', default='.',
139 help="Directory to start discovery ('.' default)")
140 parser.add_option('-p', '--pattern', dest='pattern', default='test*.py',
141 help="Pattern to match tests ('test*.py' default)")
142 parser.add_option('-t', '--top-level-directory', dest='top', default=None,
143 help='Top level directory of project (defaults to start directory)')
144
145 options, args = parser.parse_args(argv)
146 if len(args) > 3:
147 self.usageExit()
148
149 for name, value in zip(('start', 'pattern', 'top'), args):
150 setattr(options, name, value)
151
152 if options.verbose:
153 self.verbosity = 2
154
155 start_dir = options.start
156 pattern = options.pattern
157 top_level_dir = options.top
158
159 loader = Loader()
160 self.test = loader.discover(start_dir, pattern, top_level_dir)
161
162 def runTests(self):
163 if self.testRunner is None:
164 self.testRunner = runner.TextTestRunner
165 if isinstance(self.testRunner, type):
166 try:
167 testRunner = self.testRunner(verbosity=self.verbosity)
168 except TypeError:
169 # didn't accept the verbosity argument
170 testRunner = self.testRunner()
171 else:
172 # it is assumed to be a TestRunner instance
173 testRunner = self.testRunner
174 self.result = testRunner.run(self.test)
175 if self.exit:
176 sys.exit(not self.result.wasSuccessful())
177
178main = TestProgram