diff options
Diffstat (limited to 'Lib/unittest/main.py')
| -rw-r--r-- | Lib/unittest/main.py | 274 | 
1 files changed, 274 insertions, 0 deletions
| diff --git a/Lib/unittest/main.py b/Lib/unittest/main.py new file mode 100644 index 0000000..55d4e4b --- /dev/null +++ b/Lib/unittest/main.py @@ -0,0 +1,274 @@ +"""Unittest main program""" + +import sys +import os +import types + +from . import loader, runner +from .signals import installHandler + +__unittest = True + +FAILFAST     = "  -f, --failfast   Stop on first failure\n" +CATCHBREAK   = "  -c, --catch      Catch control-C and display results\n" +BUFFEROUTPUT = "  -b, --buffer     Buffer stdout and stderr during test runs\n" + +USAGE_AS_MAIN = """\ +Usage: %(progName)s [options] [tests] + +Options: +  -h, --help       Show this message +  -v, --verbose    Verbose output +  -q, --quiet      Minimal output +%(failfast)s%(catchbreak)s%(buffer)s +Examples: +  %(progName)s test_module               - run tests from test_module +  %(progName)s module.TestClass          - run tests from module.TestClass +  %(progName)s module.Class.test_method  - run specified test method + +[tests] can be a list of any number of test modules, classes and test +methods. + +Alternative Usage: %(progName)s discover [options] + +Options: +  -v, --verbose    Verbose output +%(failfast)s%(catchbreak)s%(buffer)s  -s directory     Directory to start discovery ('.' default) +  -p pattern       Pattern to match test files ('test*.py' default) +  -t directory     Top level directory of project (default to +                   start directory) + +For test discovery all test modules must be importable from the top +level directory of the project. +""" + +USAGE_FROM_MODULE = """\ +Usage: %(progName)s [options] [test] [...] + +Options: +  -h, --help       Show this message +  -v, --verbose    Verbose output +  -q, --quiet      Minimal output +%(failfast)s%(catchbreak)s%(buffer)s +Examples: +  %(progName)s                               - run default set of tests +  %(progName)s MyTestSuite                   - run suite 'MyTestSuite' +  %(progName)s MyTestCase.testSomething      - run MyTestCase.testSomething +  %(progName)s MyTestCase                    - run all 'test*' test methods +                                               in MyTestCase +""" + +def _convert_name(name): +    # on Linux / Mac OS X 'foo.PY' is not importable, but on +    # Windows it is. Simpler to do a case insensitive match +    # a better check would be to check that the name is a +    # valid Python module name. +    if os.path.isfile(name) and name.lower().endswith('.py'): +        if os.path.isabs(name): +            rel_path = os.path.relpath(name, os.getcwd()) +            if os.path.isabs(rel_path) or rel_path.startswith(os.pardir): +                return name +            name = rel_path +        # on Windows both '\' and '/' are used as path +        # separators. Better to replace both than rely on os.path.sep +        return name[:-3].replace('\\', '.').replace('/', '.') +    return name + +def _convert_names(names): +    return [_convert_name(name) for name in names] + +class TestProgram(object): +    """A command-line program that runs a set of tests; this is primarily +       for making test modules conveniently executable. +    """ +    USAGE = USAGE_FROM_MODULE + +    # defaults for testing +    failfast = catchbreak = buffer = progName = warnings = None + +    def __init__(self, module='__main__', defaultTest=None, argv=None, +                    testRunner=None, testLoader=loader.defaultTestLoader, +                    exit=True, verbosity=1, failfast=None, catchbreak=None, +                    buffer=None, warnings=None): +        if isinstance(module, str): +            self.module = __import__(module) +            for part in module.split('.')[1:]: +                self.module = getattr(self.module, part) +        else: +            self.module = module +        if argv is None: +            argv = sys.argv + +        self.exit = exit +        self.failfast = failfast +        self.catchbreak = catchbreak +        self.verbosity = verbosity +        self.buffer = buffer +        if warnings is None and not sys.warnoptions: +            # even if DreprecationWarnings are ignored by default +            # print them anyway unless other warnings settings are +            # specified by the warnings arg or the -W python flag +            self.warnings = 'default' +        else: +            # here self.warnings is set either to the value passed +            # to the warnings args or to None. +            # If the user didn't pass a value self.warnings will +            # be None. This means that the behavior is unchanged +            # and depends on the values passed to -W. +            self.warnings = warnings +        self.defaultTest = defaultTest +        self.testRunner = testRunner +        self.testLoader = testLoader +        self.progName = os.path.basename(argv[0]) +        self.parseArgs(argv) +        self.runTests() + +    def usageExit(self, msg=None): +        if msg: +            print(msg) +        usage = {'progName': self.progName, 'catchbreak': '', 'failfast': '', +                 'buffer': ''} +        if self.failfast != False: +            usage['failfast'] = FAILFAST +        if self.catchbreak != False: +            usage['catchbreak'] = CATCHBREAK +        if self.buffer != False: +            usage['buffer'] = BUFFEROUTPUT +        print(self.USAGE % usage) +        sys.exit(2) + +    def parseArgs(self, argv): +        if ((len(argv) > 1 and argv[1].lower() == 'discover') or +            (len(argv) == 1 and self.module is None)): +            self._do_discovery(argv[2:]) +            return + +        import getopt +        long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer'] +        try: +            options, args = getopt.getopt(argv[1:], 'hHvqfcb', long_opts) +        except getopt.error as msg: +            self.usageExit(msg) +            return + +        for opt, value in options: +            if opt in ('-h','-H','--help'): +                self.usageExit() +            if opt in ('-q','--quiet'): +                self.verbosity = 0 +            if opt in ('-v','--verbose'): +                self.verbosity = 2 +            if opt in ('-f','--failfast'): +                if self.failfast is None: +                    self.failfast = True +                # Should this raise an exception if -f is not valid? +            if opt in ('-c','--catch'): +                if self.catchbreak is None: +                    self.catchbreak = True +                # Should this raise an exception if -c is not valid? +            if opt in ('-b','--buffer'): +                if self.buffer is None: +                    self.buffer = True +                # Should this raise an exception if -b is not valid? + +        if len(args) == 0 and self.module is None: +            # this allows "python -m unittest -v" to still work for +            # test discovery. This means -c / -b / -v / -f options will +            # be handled twice, which is harmless but not ideal. +            self._do_discovery(argv[1:]) +            return + +        if len(args) == 0 and self.defaultTest is None: +            # createTests will load tests from self.module +            self.testNames = None +        elif len(args) > 0: +            self.testNames = _convert_names(args) +            if __name__ == '__main__': +                # to support python -m unittest ... +                self.module = None +        else: +            self.testNames = (self.defaultTest,) +        self.createTests() + +    def createTests(self): +        if self.testNames is None: +            self.test = self.testLoader.loadTestsFromModule(self.module) +        else: +            self.test = self.testLoader.loadTestsFromNames(self.testNames, +                                                           self.module) + +    def _do_discovery(self, argv, Loader=loader.TestLoader): +        # handle command line args for test discovery +        self.progName = '%s discover' % self.progName +        import optparse +        parser = optparse.OptionParser() +        parser.prog = self.progName +        parser.add_option('-v', '--verbose', dest='verbose', default=False, +                          help='Verbose output', action='store_true') +        if self.failfast != False: +            parser.add_option('-f', '--failfast', dest='failfast', default=False, +                              help='Stop on first fail or error', +                              action='store_true') +        if self.catchbreak != False: +            parser.add_option('-c', '--catch', dest='catchbreak', default=False, +                              help='Catch ctrl-C and display results so far', +                              action='store_true') +        if self.buffer != False: +            parser.add_option('-b', '--buffer', dest='buffer', default=False, +                              help='Buffer stdout and stderr during tests', +                              action='store_true') +        parser.add_option('-s', '--start-directory', dest='start', default='.', +                          help="Directory to start discovery ('.' default)") +        parser.add_option('-p', '--pattern', dest='pattern', default='test*.py', +                          help="Pattern to match tests ('test*.py' default)") +        parser.add_option('-t', '--top-level-directory', dest='top', default=None, +                          help='Top level directory of project (defaults to start directory)') + +        options, args = parser.parse_args(argv) +        if len(args) > 3: +            self.usageExit() + +        for name, value in zip(('start', 'pattern', 'top'), args): +            setattr(options, name, value) + +        # only set options from the parsing here +        # if they weren't set explicitly in the constructor +        if self.failfast is None: +            self.failfast = options.failfast +        if self.catchbreak is None: +            self.catchbreak = options.catchbreak +        if self.buffer is None: +            self.buffer = options.buffer + +        if options.verbose: +            self.verbosity = 2 + +        start_dir = options.start +        pattern = options.pattern +        top_level_dir = options.top + +        loader = Loader() +        self.test = loader.discover(start_dir, pattern, top_level_dir) + +    def runTests(self): +        if self.catchbreak: +            installHandler() +        if self.testRunner is None: +            self.testRunner = runner.TextTestRunner +        if isinstance(self.testRunner, type): +            try: +                testRunner = self.testRunner(verbosity=self.verbosity, +                                             failfast=self.failfast, +                                             buffer=self.buffer, +                                             warnings=self.warnings) +            except TypeError: +                # didn't accept the verbosity, buffer or failfast arguments +                testRunner = self.testRunner() +        else: +            # it is assumed to be a TestRunner instance +            testRunner = self.testRunner +        self.result = testRunner.run(self.test) +        if self.exit: +            sys.exit(not self.result.wasSuccessful()) + +main = TestProgram | 
