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
def _reactorAction(): return usage.CompleteList([r.shortName for r in reactors.getReactorTypes()])
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")
def _reporterAction(): return usage.CompleteList( [p.longOpt for p in plugin.getPlugins(itrial.IReporter)])