Example #1
0
class Options(_BasicOptions, usage.Options, app.ReactorSelectionMixin):
    """
    Options to the trial command line tool.

    @ivar _workerFlags: List of flags which are accepted by trial distributed
        workers. This is used by C{_getWorkerArguments} to build the command
        line arguments.
    @type _workerFlags: C{list}

    @ivar _workerParameters: List of parameter which are accepted by trial
        distributed workers. This is used by C{_getWorkerArguments} to build
        the command line arguments.
    @type _workerParameters: C{list}
    """

    optFlags = [
        [
            "debug",
            "b",
            "Run tests in a debugger. If that debugger is "
            "pdb, will load '.pdbrc' from current directory if it exists.",
        ],
        [
            "debug-stacktraces",
            "B",
            "Report Deferred creation and "
            "callback stack traces",
        ],
        [
            "nopm",
            None,
            "don't automatically jump into debugger for "
            "postmorteming of exceptions",
        ],
        ["dry-run", "n", "do everything but run the tests"],
        ["profile", None, "Run tests under the Python profiler"],
        ["until-failure", "u", "Repeat test until it fails"],
    ]

    optParameters = [
        [
            "debugger",
            None,
            "pdb",
            "the fully qualified name of a debugger to "
            "use if --debug is passed",
        ],
        ["logfile", "l", "test.log", "log file name"],
        ["jobs", "j", None, "Number of local workers to run"],
    ]

    compData = usage.Completions(optActions={
        "tbformat":
        usage.CompleteList(["plain", "emacs", "cgitb"]),
        "reporter":
        _reporterAction,
    }, )

    _workerFlags = ["disablegc", "force-gc", "coverage"]
    _workerParameters = ["recursionlimit", "reactor", "without-module"]

    fallbackReporter = reporter.TreeReporter
    extra = None
    tracer = None

    def opt_jobs(self, number):
        """
        Number of local workers to run, a strictly positive integer.
        """
        try:
            number = int(number)
        except ValueError:
            raise usage.UsageError(
                "Expecting integer argument to jobs, got '%s'" % number)
        if number <= 0:
            raise usage.UsageError(
                "Argument to jobs must be a strictly positive integer")
        self["jobs"] = number

    def _getWorkerArguments(self):
        """
        Return a list of options to pass to distributed workers.
        """
        args = []
        for option in self._workerFlags:
            if self.get(option) is not None:
                if self[option]:
                    args.append("--%s" % (option, ))
        for option in self._workerParameters:
            if self.get(option) is not None:
                args.extend(["--%s" % (option, ), str(self[option])])
        return args

    def postOptions(self):
        _BasicOptions.postOptions(self)
        if self["jobs"]:
            conflicts = ["debug", "profile", "debug-stacktraces", "exitfirst"]
            for option in conflicts:
                if self[option]:
                    raise usage.UsageError(
                        "You can't specify --%s when using --jobs" % option)
        if self["nopm"]:
            if not self["debug"]:
                raise usage.UsageError("You must specify --debug when using "
                                       "--nopm ")
            failure.DO_POST_MORTEM = False
Example #2
0
def _reactorAction():
    return usage.CompleteList([r.shortName for r in reactors.getReactorTypes()])
Example #3
0
class _BasicOptions:
    """
    Basic options shared between trial and its local workers.
    """

    longdesc = ("trial loads and executes a suite of unit tests, obtained "
                "from modules, packages and files listed on the command line.")

    optFlags = [
        ["help", "h"],
        ["no-recurse", "N", "Don't recurse into packages"],
        ["help-orders", None, "Help on available test running orders"],
        [
            "help-reporters", None,
            "Help on available output plugins (reporters)"
        ],
        [
            "rterrors",
            "e",
            "realtime errors, print out tracebacks as "
            "soon as they occur",
        ],
        ["unclean-warnings", None, "Turn dirty reactor errors into warnings"],
        [
            "force-gc",
            None,
            "Have Trial run gc.collect() before and "
            "after each test case.",
        ],
        [
            "exitfirst",
            "x",
            "Exit after the first non-successful result (cannot be "
            "specified along with --jobs).",
        ],
    ]

    optParameters = [
        [
            "order",
            "o",
            None,
            "Specify what order to run test cases and methods. "
            "See --help-orders for more info.",
            _checkKnownRunOrder,
        ],
        [
            "random", "z", None,
            "Run tests in random order using the specified seed"
        ],
        [
            "temp-directory",
            None,
            "_trial_temp",
            "Path to use as working directory for tests.",
        ],
        [
            "reporter",
            None,
            "verbose",
            "The reporter to use for this test run.  See --help-reporters for "
            "more info.",
        ],
    ]

    compData = usage.Completions(
        optActions={
            "order": usage.CompleteList(_runOrders),
            "reporter": _reporterAction,
            "logfile": usage.CompleteFiles(descr="log file name"),
            "random": usage.Completer(descr="random seed"),
        },
        extraActions=[
            usage.CompleteFiles(
                "*.py",
                descr="file | module | package | TestCase | testMethod",
                repeat=True,
            )
        ],
    )

    fallbackReporter = reporter.TreeReporter
    tracer = None

    def __init__(self):
        self["tests"] = []
        usage.Options.__init__(self)

    def getSynopsis(self):
        executableName = reflect.filenameToModuleName(sys.argv[0])

        if executableName.endswith(".__main__"):
            executableName = "{} -m {}".format(
                os.path.basename(sys.executable),
                executableName.replace(".__main__", ""),
            )

        return """%s [options] [[file|package|module|TestCase|testmethod]...]
        """ % (executableName, )

    def coverdir(self):
        """
        Return a L{FilePath} representing the directory into which coverage
        results should be written.
        """
        coverdir = "coverage"
        result = FilePath(self["temp-directory"]).child(coverdir)
        print("Setting coverage directory to %s." % (result.path, ))
        return result

    # TODO: Some of the opt_* methods on this class have docstrings and some do
    #       not. This is mostly because usage.Options's currently will replace
    #       any intended output in optFlags and optParameters with the
    #       docstring. See #6427. When that is fixed, all methods should be
    #       given docstrings (and it should be verified that those with
    #       docstrings already have content suitable for printing as usage
    #       information).

    def opt_coverage(self):
        """
        Generate coverage information in the coverage file in the
        directory specified by the temp-directory option.
        """
        import trace

        self.tracer = trace.Trace(count=1, trace=0)
        sys.settrace(self.tracer.globaltrace)
        self["coverage"] = True

    def opt_testmodule(self, filename):
        """
        Filename to grep for test cases (-*- test-case-name).
        """
        # If the filename passed to this parameter looks like a test module
        # we just add that to the test suite.
        #
        # If not, we inspect it for an Emacs buffer local variable called
        # 'test-case-name'.  If that variable is declared, we try to add its
        # value to the test suite as a module.
        #
        # This parameter allows automated processes (like Buildbot) to pass
        # a list of files to Trial with the general expectation of "these files,
        # whatever they are, will get tested"
        if not os.path.isfile(filename):
            sys.stderr.write("File %r doesn't exist\n" % (filename, ))
            return
        filename = os.path.abspath(filename)
        if isTestFile(filename):
            self["tests"].append(filename)
        else:
            self["tests"].extend(getTestModules(filename))

    def opt_spew(self):
        """
        Print an insanely verbose log of everything that happens.  Useful
        when debugging freezes or locks in complex code.
        """
        from twisted.python.util import spewer

        sys.settrace(spewer)

    def opt_help_orders(self):
        synopsis = ("Trial can attempt to run test cases and their methods in "
                    "a few different orders. You can select any of the "
                    "following options using --order=<foo>.\n")

        print(synopsis)
        for name, (description, _) in sorted(_runOrders.items()):
            print("   ", name, "\t", description)
        sys.exit(0)

    def opt_help_reporters(self):
        synopsis = ("Trial's output can be customized using plugins called "
                    "Reporters. You can\nselect any of the following "
                    "reporters using --reporter=<foo>\n")
        print(synopsis)
        for p in plugin.getPlugins(itrial.IReporter):
            print("   ", p.longOpt, "\t", p.description)
        sys.exit(0)

    def opt_disablegc(self):
        """
        Disable the garbage collector
        """
        self["disablegc"] = True
        gc.disable()

    def opt_tbformat(self, opt):
        """
        Specify the format to display tracebacks with. Valid formats are
        'plain', 'emacs', and 'cgitb' which uses the nicely verbose stdlib
        cgitb.text function
        """
        try:
            self["tbformat"] = TBFORMAT_MAP[opt]
        except KeyError:
            raise usage.UsageError(
                "tbformat must be 'plain', 'emacs', or 'cgitb'.")

    def opt_recursionlimit(self, arg):
        """
        see sys.setrecursionlimit()
        """
        try:
            sys.setrecursionlimit(int(arg))
        except (TypeError, ValueError):
            raise usage.UsageError(
                "argument to recursionlimit must be an integer")
        else:
            self["recursionlimit"] = int(arg)

    def opt_random(self, option):
        try:
            self["random"] = int(option)
        except ValueError:
            raise usage.UsageError(
                "Argument to --random must be a positive integer")
        else:
            if self["random"] < 0:
                raise usage.UsageError(
                    "Argument to --random must be a positive integer")
            elif self["random"] == 0:
                self["random"] = int(time.time() * 100)

    def opt_without_module(self, option):
        """
        Fake the lack of the specified modules, separated with commas.
        """
        self["without-module"] = option
        for module in option.split(","):
            if module in sys.modules:
                warnings.warn(
                    "Module '%s' already imported, "
                    "disabling anyway." % (module, ),
                    category=RuntimeWarning,
                )
            sys.modules[module] = None

    def parseArgs(self, *args):
        self["tests"].extend(args)

    def _loadReporterByName(self, name):
        for p in plugin.getPlugins(itrial.IReporter):
            qual = "%s.%s" % (p.module, p.klass)
            if p.longOpt == name:
                return reflect.namedAny(qual)
        raise usage.UsageError("Only pass names of Reporter plugins to "
                               "--reporter. See --help-reporters for "
                               "more info.")

    def postOptions(self):
        # Only load reporters now, as opposed to any earlier, to avoid letting
        # application-defined plugins muck up reactor selecting by importing
        # t.i.reactor and causing the default to be installed.
        self["reporter"] = self._loadReporterByName(self["reporter"])
        if "tbformat" not in self:
            self["tbformat"] = "default"
        if self["order"] is not None and self["random"] is not None:
            raise usage.UsageError(
                "You can't specify --random when using --order")
Example #4
0
def _reporterAction():
    return usage.CompleteList(
        [p.longOpt for p in plugin.getPlugins(itrial.IReporter)])