blob: e04ec16ffbec1df9acabc10ad49db72fd8edf950 [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
Benjamin Petersondccc1fc2010-03-22 00:15:53 +00009__unittest = True
10
Benjamin Petersonbed7d042009-07-19 21:01:52 +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, str):
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:
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000114 # createTests will load tests from self.module
115 self.testNames = None
116 elif len(args) > 0:
Benjamin Petersonbed7d042009-07-19 21:01:52 +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 as msg:
125 self.usageExit(msg)
126
127 def createTests(self):
Benjamin Peterson4ac9ce42009-10-04 14:49: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 Petersonbed7d042009-07-19 21:01:52 +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):
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