1"""Unittest main program""" 2 3import sys 4import os 5import types 6 7from . import loader, runner 8from .signals import installHandler 9 10__unittest = True 11 12FAILFAST = " -f, --failfast Stop on first failure\n" 13CATCHBREAK = " -c, --catch Catch control-C and display results\n" 14BUFFEROUTPUT = " -b, --buffer Buffer stdout and stderr during test runs\n" 15 16USAGE_AS_MAIN = """\ 17Usage: %(progName)s [options] [tests] 18 19Options: 20 -h, --help Show this message 21 -v, --verbose Verbose output 22 -q, --quiet Minimal output 23%(failfast)s%(catchbreak)s%(buffer)s 24Examples: 25 %(progName)s test_module - run tests from test_module 26 %(progName)s module.TestClass - run tests from module.TestClass 27 %(progName)s module.Class.test_method - run specified test method 28 29[tests] can be a list of any number of test modules, classes and test 30methods. 31 32Alternative Usage: %(progName)s discover [options] 33 34Options: 35 -v, --verbose Verbose output 36%(failfast)s%(catchbreak)s%(buffer)s -s directory Directory to start discovery ('.' default) 37 -p pattern Pattern to match test files ('test*.py' default) 38 -t directory Top level directory of project (default to 39 start directory) 40 41For test discovery all test modules must be importable from the top 42level directory of the project. 43""" 44 45USAGE_FROM_MODULE = """\ 46Usage: %(progName)s [options] [test] [...] 47 48Options: 49 -h, --help Show this message 50 -v, --verbose Verbose output 51 -q, --quiet Minimal output 52%(failfast)s%(catchbreak)s%(buffer)s 53Examples: 54 %(progName)s - run default set of tests 55 %(progName)s MyTestSuite - run suite 'MyTestSuite' 56 %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething 57 %(progName)s MyTestCase - run all 'test*' test methods 58 in MyTestCase 59""" 60 61 62 63class TestProgram(object): 64 """A command-line program that runs a set of tests; this is primarily 65 for making test modules conveniently executable. 66 """ 67 USAGE = USAGE_FROM_MODULE 68 69 # defaults for testing 70 failfast = catchbreak = buffer = progName = None 71 72 def __init__(self, module='__main__', defaultTest=None, argv=None, 73 testRunner=None, testLoader=loader.defaultTestLoader, 74 exit=True, verbosity=1, failfast=None, catchbreak=None, 75 buffer=None): 76 if isinstance(module, basestring): 77 self.module = __import__(module) 78 for part in module.split('.')[1:]: 79 self.module = getattr(self.module, part) 80 else: 81 self.module = module 82 if argv is None: 83 argv = sys.argv 84 85 self.exit = exit 86 self.failfast = failfast 87 self.catchbreak = catchbreak 88 self.verbosity = verbosity 89 self.buffer = buffer 90 self.defaultTest = defaultTest 91 self.testRunner = testRunner 92 self.testLoader = testLoader 93 self.progName = os.path.basename(argv[0]) 94 self.parseArgs(argv) 95 self.runTests() 96 97 def usageExit(self, msg=None): 98 if msg: 99 print msg 100 usage = {'progName': self.progName, 'catchbreak': '', 'failfast': '', 101 'buffer': ''} 102 if self.failfast != False: 103 usage['failfast'] = FAILFAST 104 if self.catchbreak != False: 105 usage['catchbreak'] = CATCHBREAK 106 if self.buffer != False: 107 usage['buffer'] = BUFFEROUTPUT 108 print self.USAGE % usage 109 sys.exit(2) 110 111 def parseArgs(self, argv): 112 if len(argv) > 1 and argv[1].lower() == 'discover': 113 self._do_discovery(argv[2:]) 114 return 115 116 import getopt 117 long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer'] 118 try: 119 options, args = getopt.getopt(argv[1:], 'hHvqfcb', long_opts) 120 for opt, value in options: 121 if opt in ('-h','-H','--help'): 122 self.usageExit() 123 if opt in ('-q','--quiet'): 124 self.verbosity = 0 125 if opt in ('-v','--verbose'): 126 self.verbosity = 2 127 if opt in ('-f','--failfast'): 128 if self.failfast is None: 129 self.failfast = True 130 # Should this raise an exception if -f is not valid? 131 if opt in ('-c','--catch'): 132 if self.catchbreak is None: 133 self.catchbreak = True 134 # Should this raise an exception if -c is not valid? 135 if opt in ('-b','--buffer'): 136 if self.buffer is None: 137 self.buffer = True 138 # Should this raise an exception if -b is not valid? 139 if len(args) == 0 and self.defaultTest is None: 140 # createTests will load tests from self.module 141 self.testNames = None 142 elif len(args) > 0: 143 self.testNames = args 144 if __name__ == '__main__': 145 # to support python -m unittest ... 146 self.module = None 147 else: 148 self.testNames = (self.defaultTest,) 149 self.createTests() 150 except getopt.error, msg: 151 self.usageExit(msg) 152 153 def createTests(self): 154 if self.testNames is None: 155 self.test = self.testLoader.loadTestsFromModule(self.module) 156 else: 157 self.test = self.testLoader.loadTestsFromNames(self.testNames, 158 self.module) 159 160 def _do_discovery(self, argv, Loader=None): 161 if Loader is None: 162 Loader = lambda: self.testLoader 163 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: 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: 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