1*cda5da8dSAndroid Build Coastguard Worker"""Unittest main program""" 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard Workerimport sys 4*cda5da8dSAndroid Build Coastguard Workerimport argparse 5*cda5da8dSAndroid Build Coastguard Workerimport os 6*cda5da8dSAndroid Build Coastguard Workerimport warnings 7*cda5da8dSAndroid Build Coastguard Worker 8*cda5da8dSAndroid Build Coastguard Workerfrom . import loader, runner 9*cda5da8dSAndroid Build Coastguard Workerfrom .signals import installHandler 10*cda5da8dSAndroid Build Coastguard Worker 11*cda5da8dSAndroid Build Coastguard Worker__unittest = True 12*cda5da8dSAndroid Build Coastguard Worker 13*cda5da8dSAndroid Build Coastguard WorkerMAIN_EXAMPLES = """\ 14*cda5da8dSAndroid Build Coastguard WorkerExamples: 15*cda5da8dSAndroid Build Coastguard Worker %(prog)s test_module - run tests from test_module 16*cda5da8dSAndroid Build Coastguard Worker %(prog)s module.TestClass - run tests from module.TestClass 17*cda5da8dSAndroid Build Coastguard Worker %(prog)s module.Class.test_method - run specified test method 18*cda5da8dSAndroid Build Coastguard Worker %(prog)s path/to/test_file.py - run tests from test_file.py 19*cda5da8dSAndroid Build Coastguard Worker""" 20*cda5da8dSAndroid Build Coastguard Worker 21*cda5da8dSAndroid Build Coastguard WorkerMODULE_EXAMPLES = """\ 22*cda5da8dSAndroid Build Coastguard WorkerExamples: 23*cda5da8dSAndroid Build Coastguard Worker %(prog)s - run default set of tests 24*cda5da8dSAndroid Build Coastguard Worker %(prog)s MyTestSuite - run suite 'MyTestSuite' 25*cda5da8dSAndroid Build Coastguard Worker %(prog)s MyTestCase.testSomething - run MyTestCase.testSomething 26*cda5da8dSAndroid Build Coastguard Worker %(prog)s MyTestCase - run all 'test*' test methods 27*cda5da8dSAndroid Build Coastguard Worker in MyTestCase 28*cda5da8dSAndroid Build Coastguard Worker""" 29*cda5da8dSAndroid Build Coastguard Worker 30*cda5da8dSAndroid Build Coastguard Workerdef _convert_name(name): 31*cda5da8dSAndroid Build Coastguard Worker # on Linux / Mac OS X 'foo.PY' is not importable, but on 32*cda5da8dSAndroid Build Coastguard Worker # Windows it is. Simpler to do a case insensitive match 33*cda5da8dSAndroid Build Coastguard Worker # a better check would be to check that the name is a 34*cda5da8dSAndroid Build Coastguard Worker # valid Python module name. 35*cda5da8dSAndroid Build Coastguard Worker if os.path.isfile(name) and name.lower().endswith('.py'): 36*cda5da8dSAndroid Build Coastguard Worker if os.path.isabs(name): 37*cda5da8dSAndroid Build Coastguard Worker rel_path = os.path.relpath(name, os.getcwd()) 38*cda5da8dSAndroid Build Coastguard Worker if os.path.isabs(rel_path) or rel_path.startswith(os.pardir): 39*cda5da8dSAndroid Build Coastguard Worker return name 40*cda5da8dSAndroid Build Coastguard Worker name = rel_path 41*cda5da8dSAndroid Build Coastguard Worker # on Windows both '\' and '/' are used as path 42*cda5da8dSAndroid Build Coastguard Worker # separators. Better to replace both than rely on os.path.sep 43*cda5da8dSAndroid Build Coastguard Worker return os.path.normpath(name)[:-3].replace('\\', '.').replace('/', '.') 44*cda5da8dSAndroid Build Coastguard Worker return name 45*cda5da8dSAndroid Build Coastguard Worker 46*cda5da8dSAndroid Build Coastguard Workerdef _convert_names(names): 47*cda5da8dSAndroid Build Coastguard Worker return [_convert_name(name) for name in names] 48*cda5da8dSAndroid Build Coastguard Worker 49*cda5da8dSAndroid Build Coastguard Worker 50*cda5da8dSAndroid Build Coastguard Workerdef _convert_select_pattern(pattern): 51*cda5da8dSAndroid Build Coastguard Worker if not '*' in pattern: 52*cda5da8dSAndroid Build Coastguard Worker pattern = '*%s*' % pattern 53*cda5da8dSAndroid Build Coastguard Worker return pattern 54*cda5da8dSAndroid Build Coastguard Worker 55*cda5da8dSAndroid Build Coastguard Worker 56*cda5da8dSAndroid Build Coastguard Workerclass TestProgram(object): 57*cda5da8dSAndroid Build Coastguard Worker """A command-line program that runs a set of tests; this is primarily 58*cda5da8dSAndroid Build Coastguard Worker for making test modules conveniently executable. 59*cda5da8dSAndroid Build Coastguard Worker """ 60*cda5da8dSAndroid Build Coastguard Worker # defaults for testing 61*cda5da8dSAndroid Build Coastguard Worker module=None 62*cda5da8dSAndroid Build Coastguard Worker verbosity = 1 63*cda5da8dSAndroid Build Coastguard Worker failfast = catchbreak = buffer = progName = warnings = testNamePatterns = None 64*cda5da8dSAndroid Build Coastguard Worker _discovery_parser = None 65*cda5da8dSAndroid Build Coastguard Worker 66*cda5da8dSAndroid Build Coastguard Worker def __init__(self, module='__main__', defaultTest=None, argv=None, 67*cda5da8dSAndroid Build Coastguard Worker testRunner=None, testLoader=loader.defaultTestLoader, 68*cda5da8dSAndroid Build Coastguard Worker exit=True, verbosity=1, failfast=None, catchbreak=None, 69*cda5da8dSAndroid Build Coastguard Worker buffer=None, warnings=None, *, tb_locals=False): 70*cda5da8dSAndroid Build Coastguard Worker if isinstance(module, str): 71*cda5da8dSAndroid Build Coastguard Worker self.module = __import__(module) 72*cda5da8dSAndroid Build Coastguard Worker for part in module.split('.')[1:]: 73*cda5da8dSAndroid Build Coastguard Worker self.module = getattr(self.module, part) 74*cda5da8dSAndroid Build Coastguard Worker else: 75*cda5da8dSAndroid Build Coastguard Worker self.module = module 76*cda5da8dSAndroid Build Coastguard Worker if argv is None: 77*cda5da8dSAndroid Build Coastguard Worker argv = sys.argv 78*cda5da8dSAndroid Build Coastguard Worker 79*cda5da8dSAndroid Build Coastguard Worker self.exit = exit 80*cda5da8dSAndroid Build Coastguard Worker self.failfast = failfast 81*cda5da8dSAndroid Build Coastguard Worker self.catchbreak = catchbreak 82*cda5da8dSAndroid Build Coastguard Worker self.verbosity = verbosity 83*cda5da8dSAndroid Build Coastguard Worker self.buffer = buffer 84*cda5da8dSAndroid Build Coastguard Worker self.tb_locals = tb_locals 85*cda5da8dSAndroid Build Coastguard Worker if warnings is None and not sys.warnoptions: 86*cda5da8dSAndroid Build Coastguard Worker # even if DeprecationWarnings are ignored by default 87*cda5da8dSAndroid Build Coastguard Worker # print them anyway unless other warnings settings are 88*cda5da8dSAndroid Build Coastguard Worker # specified by the warnings arg or the -W python flag 89*cda5da8dSAndroid Build Coastguard Worker self.warnings = 'default' 90*cda5da8dSAndroid Build Coastguard Worker else: 91*cda5da8dSAndroid Build Coastguard Worker # here self.warnings is set either to the value passed 92*cda5da8dSAndroid Build Coastguard Worker # to the warnings args or to None. 93*cda5da8dSAndroid Build Coastguard Worker # If the user didn't pass a value self.warnings will 94*cda5da8dSAndroid Build Coastguard Worker # be None. This means that the behavior is unchanged 95*cda5da8dSAndroid Build Coastguard Worker # and depends on the values passed to -W. 96*cda5da8dSAndroid Build Coastguard Worker self.warnings = warnings 97*cda5da8dSAndroid Build Coastguard Worker self.defaultTest = defaultTest 98*cda5da8dSAndroid Build Coastguard Worker self.testRunner = testRunner 99*cda5da8dSAndroid Build Coastguard Worker self.testLoader = testLoader 100*cda5da8dSAndroid Build Coastguard Worker self.progName = os.path.basename(argv[0]) 101*cda5da8dSAndroid Build Coastguard Worker self.parseArgs(argv) 102*cda5da8dSAndroid Build Coastguard Worker self.runTests() 103*cda5da8dSAndroid Build Coastguard Worker 104*cda5da8dSAndroid Build Coastguard Worker def usageExit(self, msg=None): 105*cda5da8dSAndroid Build Coastguard Worker warnings.warn("TestProgram.usageExit() is deprecated and will be" 106*cda5da8dSAndroid Build Coastguard Worker " removed in Python 3.13", DeprecationWarning) 107*cda5da8dSAndroid Build Coastguard Worker if msg: 108*cda5da8dSAndroid Build Coastguard Worker print(msg) 109*cda5da8dSAndroid Build Coastguard Worker if self._discovery_parser is None: 110*cda5da8dSAndroid Build Coastguard Worker self._initArgParsers() 111*cda5da8dSAndroid Build Coastguard Worker self._print_help() 112*cda5da8dSAndroid Build Coastguard Worker sys.exit(2) 113*cda5da8dSAndroid Build Coastguard Worker 114*cda5da8dSAndroid Build Coastguard Worker def _print_help(self, *args, **kwargs): 115*cda5da8dSAndroid Build Coastguard Worker if self.module is None: 116*cda5da8dSAndroid Build Coastguard Worker print(self._main_parser.format_help()) 117*cda5da8dSAndroid Build Coastguard Worker print(MAIN_EXAMPLES % {'prog': self.progName}) 118*cda5da8dSAndroid Build Coastguard Worker self._discovery_parser.print_help() 119*cda5da8dSAndroid Build Coastguard Worker else: 120*cda5da8dSAndroid Build Coastguard Worker print(self._main_parser.format_help()) 121*cda5da8dSAndroid Build Coastguard Worker print(MODULE_EXAMPLES % {'prog': self.progName}) 122*cda5da8dSAndroid Build Coastguard Worker 123*cda5da8dSAndroid Build Coastguard Worker def parseArgs(self, argv): 124*cda5da8dSAndroid Build Coastguard Worker self._initArgParsers() 125*cda5da8dSAndroid Build Coastguard Worker if self.module is None: 126*cda5da8dSAndroid Build Coastguard Worker if len(argv) > 1 and argv[1].lower() == 'discover': 127*cda5da8dSAndroid Build Coastguard Worker self._do_discovery(argv[2:]) 128*cda5da8dSAndroid Build Coastguard Worker return 129*cda5da8dSAndroid Build Coastguard Worker self._main_parser.parse_args(argv[1:], self) 130*cda5da8dSAndroid Build Coastguard Worker if not self.tests: 131*cda5da8dSAndroid Build Coastguard Worker # this allows "python -m unittest -v" to still work for 132*cda5da8dSAndroid Build Coastguard Worker # test discovery. 133*cda5da8dSAndroid Build Coastguard Worker self._do_discovery([]) 134*cda5da8dSAndroid Build Coastguard Worker return 135*cda5da8dSAndroid Build Coastguard Worker else: 136*cda5da8dSAndroid Build Coastguard Worker self._main_parser.parse_args(argv[1:], self) 137*cda5da8dSAndroid Build Coastguard Worker 138*cda5da8dSAndroid Build Coastguard Worker if self.tests: 139*cda5da8dSAndroid Build Coastguard Worker self.testNames = _convert_names(self.tests) 140*cda5da8dSAndroid Build Coastguard Worker if __name__ == '__main__': 141*cda5da8dSAndroid Build Coastguard Worker # to support python -m unittest ... 142*cda5da8dSAndroid Build Coastguard Worker self.module = None 143*cda5da8dSAndroid Build Coastguard Worker elif self.defaultTest is None: 144*cda5da8dSAndroid Build Coastguard Worker # createTests will load tests from self.module 145*cda5da8dSAndroid Build Coastguard Worker self.testNames = None 146*cda5da8dSAndroid Build Coastguard Worker elif isinstance(self.defaultTest, str): 147*cda5da8dSAndroid Build Coastguard Worker self.testNames = (self.defaultTest,) 148*cda5da8dSAndroid Build Coastguard Worker else: 149*cda5da8dSAndroid Build Coastguard Worker self.testNames = list(self.defaultTest) 150*cda5da8dSAndroid Build Coastguard Worker self.createTests() 151*cda5da8dSAndroid Build Coastguard Worker 152*cda5da8dSAndroid Build Coastguard Worker def createTests(self, from_discovery=False, Loader=None): 153*cda5da8dSAndroid Build Coastguard Worker if self.testNamePatterns: 154*cda5da8dSAndroid Build Coastguard Worker self.testLoader.testNamePatterns = self.testNamePatterns 155*cda5da8dSAndroid Build Coastguard Worker if from_discovery: 156*cda5da8dSAndroid Build Coastguard Worker loader = self.testLoader if Loader is None else Loader() 157*cda5da8dSAndroid Build Coastguard Worker self.test = loader.discover(self.start, self.pattern, self.top) 158*cda5da8dSAndroid Build Coastguard Worker elif self.testNames is None: 159*cda5da8dSAndroid Build Coastguard Worker self.test = self.testLoader.loadTestsFromModule(self.module) 160*cda5da8dSAndroid Build Coastguard Worker else: 161*cda5da8dSAndroid Build Coastguard Worker self.test = self.testLoader.loadTestsFromNames(self.testNames, 162*cda5da8dSAndroid Build Coastguard Worker self.module) 163*cda5da8dSAndroid Build Coastguard Worker 164*cda5da8dSAndroid Build Coastguard Worker def _initArgParsers(self): 165*cda5da8dSAndroid Build Coastguard Worker parent_parser = self._getParentArgParser() 166*cda5da8dSAndroid Build Coastguard Worker self._main_parser = self._getMainArgParser(parent_parser) 167*cda5da8dSAndroid Build Coastguard Worker self._discovery_parser = self._getDiscoveryArgParser(parent_parser) 168*cda5da8dSAndroid Build Coastguard Worker 169*cda5da8dSAndroid Build Coastguard Worker def _getParentArgParser(self): 170*cda5da8dSAndroid Build Coastguard Worker parser = argparse.ArgumentParser(add_help=False) 171*cda5da8dSAndroid Build Coastguard Worker 172*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-v', '--verbose', dest='verbosity', 173*cda5da8dSAndroid Build Coastguard Worker action='store_const', const=2, 174*cda5da8dSAndroid Build Coastguard Worker help='Verbose output') 175*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-q', '--quiet', dest='verbosity', 176*cda5da8dSAndroid Build Coastguard Worker action='store_const', const=0, 177*cda5da8dSAndroid Build Coastguard Worker help='Quiet output') 178*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('--locals', dest='tb_locals', 179*cda5da8dSAndroid Build Coastguard Worker action='store_true', 180*cda5da8dSAndroid Build Coastguard Worker help='Show local variables in tracebacks') 181*cda5da8dSAndroid Build Coastguard Worker if self.failfast is None: 182*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-f', '--failfast', dest='failfast', 183*cda5da8dSAndroid Build Coastguard Worker action='store_true', 184*cda5da8dSAndroid Build Coastguard Worker help='Stop on first fail or error') 185*cda5da8dSAndroid Build Coastguard Worker self.failfast = False 186*cda5da8dSAndroid Build Coastguard Worker if self.catchbreak is None: 187*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-c', '--catch', dest='catchbreak', 188*cda5da8dSAndroid Build Coastguard Worker action='store_true', 189*cda5da8dSAndroid Build Coastguard Worker help='Catch Ctrl-C and display results so far') 190*cda5da8dSAndroid Build Coastguard Worker self.catchbreak = False 191*cda5da8dSAndroid Build Coastguard Worker if self.buffer is None: 192*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-b', '--buffer', dest='buffer', 193*cda5da8dSAndroid Build Coastguard Worker action='store_true', 194*cda5da8dSAndroid Build Coastguard Worker help='Buffer stdout and stderr during tests') 195*cda5da8dSAndroid Build Coastguard Worker self.buffer = False 196*cda5da8dSAndroid Build Coastguard Worker if self.testNamePatterns is None: 197*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-k', dest='testNamePatterns', 198*cda5da8dSAndroid Build Coastguard Worker action='append', type=_convert_select_pattern, 199*cda5da8dSAndroid Build Coastguard Worker help='Only run tests which match the given substring') 200*cda5da8dSAndroid Build Coastguard Worker self.testNamePatterns = [] 201*cda5da8dSAndroid Build Coastguard Worker 202*cda5da8dSAndroid Build Coastguard Worker return parser 203*cda5da8dSAndroid Build Coastguard Worker 204*cda5da8dSAndroid Build Coastguard Worker def _getMainArgParser(self, parent): 205*cda5da8dSAndroid Build Coastguard Worker parser = argparse.ArgumentParser(parents=[parent]) 206*cda5da8dSAndroid Build Coastguard Worker parser.prog = self.progName 207*cda5da8dSAndroid Build Coastguard Worker parser.print_help = self._print_help 208*cda5da8dSAndroid Build Coastguard Worker 209*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('tests', nargs='*', 210*cda5da8dSAndroid Build Coastguard Worker help='a list of any number of test modules, ' 211*cda5da8dSAndroid Build Coastguard Worker 'classes and test methods.') 212*cda5da8dSAndroid Build Coastguard Worker 213*cda5da8dSAndroid Build Coastguard Worker return parser 214*cda5da8dSAndroid Build Coastguard Worker 215*cda5da8dSAndroid Build Coastguard Worker def _getDiscoveryArgParser(self, parent): 216*cda5da8dSAndroid Build Coastguard Worker parser = argparse.ArgumentParser(parents=[parent]) 217*cda5da8dSAndroid Build Coastguard Worker parser.prog = '%s discover' % self.progName 218*cda5da8dSAndroid Build Coastguard Worker parser.epilog = ('For test discovery all test modules must be ' 219*cda5da8dSAndroid Build Coastguard Worker 'importable from the top level directory of the ' 220*cda5da8dSAndroid Build Coastguard Worker 'project.') 221*cda5da8dSAndroid Build Coastguard Worker 222*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-s', '--start-directory', dest='start', 223*cda5da8dSAndroid Build Coastguard Worker help="Directory to start discovery ('.' default)") 224*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-p', '--pattern', dest='pattern', 225*cda5da8dSAndroid Build Coastguard Worker help="Pattern to match tests ('test*.py' default)") 226*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-t', '--top-level-directory', dest='top', 227*cda5da8dSAndroid Build Coastguard Worker help='Top level directory of project (defaults to ' 228*cda5da8dSAndroid Build Coastguard Worker 'start directory)') 229*cda5da8dSAndroid Build Coastguard Worker for arg in ('start', 'pattern', 'top'): 230*cda5da8dSAndroid Build Coastguard Worker parser.add_argument(arg, nargs='?', 231*cda5da8dSAndroid Build Coastguard Worker default=argparse.SUPPRESS, 232*cda5da8dSAndroid Build Coastguard Worker help=argparse.SUPPRESS) 233*cda5da8dSAndroid Build Coastguard Worker 234*cda5da8dSAndroid Build Coastguard Worker return parser 235*cda5da8dSAndroid Build Coastguard Worker 236*cda5da8dSAndroid Build Coastguard Worker def _do_discovery(self, argv, Loader=None): 237*cda5da8dSAndroid Build Coastguard Worker self.start = '.' 238*cda5da8dSAndroid Build Coastguard Worker self.pattern = 'test*.py' 239*cda5da8dSAndroid Build Coastguard Worker self.top = None 240*cda5da8dSAndroid Build Coastguard Worker if argv is not None: 241*cda5da8dSAndroid Build Coastguard Worker # handle command line args for test discovery 242*cda5da8dSAndroid Build Coastguard Worker if self._discovery_parser is None: 243*cda5da8dSAndroid Build Coastguard Worker # for testing 244*cda5da8dSAndroid Build Coastguard Worker self._initArgParsers() 245*cda5da8dSAndroid Build Coastguard Worker self._discovery_parser.parse_args(argv, self) 246*cda5da8dSAndroid Build Coastguard Worker 247*cda5da8dSAndroid Build Coastguard Worker self.createTests(from_discovery=True, Loader=Loader) 248*cda5da8dSAndroid Build Coastguard Worker 249*cda5da8dSAndroid Build Coastguard Worker def runTests(self): 250*cda5da8dSAndroid Build Coastguard Worker if self.catchbreak: 251*cda5da8dSAndroid Build Coastguard Worker installHandler() 252*cda5da8dSAndroid Build Coastguard Worker if self.testRunner is None: 253*cda5da8dSAndroid Build Coastguard Worker self.testRunner = runner.TextTestRunner 254*cda5da8dSAndroid Build Coastguard Worker if isinstance(self.testRunner, type): 255*cda5da8dSAndroid Build Coastguard Worker try: 256*cda5da8dSAndroid Build Coastguard Worker try: 257*cda5da8dSAndroid Build Coastguard Worker testRunner = self.testRunner(verbosity=self.verbosity, 258*cda5da8dSAndroid Build Coastguard Worker failfast=self.failfast, 259*cda5da8dSAndroid Build Coastguard Worker buffer=self.buffer, 260*cda5da8dSAndroid Build Coastguard Worker warnings=self.warnings, 261*cda5da8dSAndroid Build Coastguard Worker tb_locals=self.tb_locals) 262*cda5da8dSAndroid Build Coastguard Worker except TypeError: 263*cda5da8dSAndroid Build Coastguard Worker # didn't accept the tb_locals argument 264*cda5da8dSAndroid Build Coastguard Worker testRunner = self.testRunner(verbosity=self.verbosity, 265*cda5da8dSAndroid Build Coastguard Worker failfast=self.failfast, 266*cda5da8dSAndroid Build Coastguard Worker buffer=self.buffer, 267*cda5da8dSAndroid Build Coastguard Worker warnings=self.warnings) 268*cda5da8dSAndroid Build Coastguard Worker except TypeError: 269*cda5da8dSAndroid Build Coastguard Worker # didn't accept the verbosity, buffer or failfast arguments 270*cda5da8dSAndroid Build Coastguard Worker testRunner = self.testRunner() 271*cda5da8dSAndroid Build Coastguard Worker else: 272*cda5da8dSAndroid Build Coastguard Worker # it is assumed to be a TestRunner instance 273*cda5da8dSAndroid Build Coastguard Worker testRunner = self.testRunner 274*cda5da8dSAndroid Build Coastguard Worker self.result = testRunner.run(self.test) 275*cda5da8dSAndroid Build Coastguard Worker if self.exit: 276*cda5da8dSAndroid Build Coastguard Worker sys.exit(not self.result.wasSuccessful()) 277*cda5da8dSAndroid Build Coastguard Worker 278*cda5da8dSAndroid Build Coastguard Workermain = TestProgram 279