blob: a2eea86d232f4c572ac31390f61029705941d436 [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
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, basestring):
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:
112 self.test = self.testLoader.loadTestsFromModule(self.module)
113 return
114 if len(args) > 0:
115 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, msg:
123 self.usageExit(msg)
124
125 def createTests(self):
126 self.test = self.testLoader.loadTestsFromNames(self.testNames,
127 self.module)
128
129 def _do_discovery(self, argv, Loader=loader.TestLoader):
130 # handle command line args for test discovery
131 import optparse
132 parser = optparse.OptionParser()
133 parser.add_option('-v', '--verbose', dest='verbose', default=False,
134 help='Verbose output', action='store_true')
135 parser.add_option('-s', '--start-directory', dest='start', default='.',
136 help="Directory to start discovery ('.' default)")
137 parser.add_option('-p', '--pattern', dest='pattern', default='test*.py',
138 help="Pattern to match tests ('test*.py' default)")
139 parser.add_option('-t', '--top-level-directory', dest='top', default=None,
140 help='Top level directory of project (defaults to start directory)')
141
142 options, args = parser.parse_args(argv)
143 if len(args) > 3:
144 self.usageExit()
145
146 for name, value in zip(('start', 'pattern', 'top'), args):
147 setattr(options, name, value)
148
149 if options.verbose:
150 self.verbosity = 2
151
152 start_dir = options.start
153 pattern = options.pattern
154 top_level_dir = options.top
155
156 loader = Loader()
157 self.test = loader.discover(start_dir, pattern, top_level_dir)
158
159 def runTests(self):
160 if self.testRunner is None:
161 self.testRunner = runner.TextTestRunner
162 if isinstance(self.testRunner, (type, types.ClassType)):
163 try:
164 testRunner = self.testRunner(verbosity=self.verbosity)
165 except TypeError:
166 # didn't accept the verbosity argument
167 testRunner = self.testRunner()
168 else:
169 # it is assumed to be a TestRunner instance
170 testRunner = self.testRunner
171 self.result = testRunner.run(self.test)
172 if self.exit:
173 sys.exit(not self.result.wasSuccessful())
174
175main = TestProgram