diff options
Diffstat (limited to 'vendor/Twisted-10.0.0/twisted/scripts/trial.py')
-rw-r--r-- | vendor/Twisted-10.0.0/twisted/scripts/trial.py | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/vendor/Twisted-10.0.0/twisted/scripts/trial.py b/vendor/Twisted-10.0.0/twisted/scripts/trial.py new file mode 100644 index 0000000000..a454d170e1 --- /dev/null +++ b/vendor/Twisted-10.0.0/twisted/scripts/trial.py @@ -0,0 +1,370 @@ +# -*- test-case-name: twisted.trial.test.test_script -*- + +# Copyright (c) 2001-2007 Twisted Matrix Laboratories. +# See LICENSE for details. + + +import sys, os, random, gc, time, warnings + +from twisted.internet import defer +from twisted.application import app +from twisted.python import usage, reflect, failure +from twisted import plugin +from twisted.python.util import spewer +from twisted.python.compat import set +from twisted.trial import runner, itrial, reporter + + +# Yea, this is stupid. Leave it for for command-line compatibility for a +# while, though. +TBFORMAT_MAP = { + 'plain': 'default', + 'default': 'default', + 'emacs': 'brief', + 'brief': 'brief', + 'cgitb': 'verbose', + 'verbose': 'verbose' + } + + +def _parseLocalVariables(line): + """Accepts a single line in Emacs local variable declaration format and + returns a dict of all the variables {name: value}. + Raises ValueError if 'line' is in the wrong format. + + See http://www.gnu.org/software/emacs/manual/html_node/File-Variables.html + """ + paren = '-*-' + start = line.find(paren) + len(paren) + end = line.rfind(paren) + if start == -1 or end == -1: + raise ValueError("%r not a valid local variable declaration" % (line,)) + items = line[start:end].split(';') + localVars = {} + for item in items: + if len(item.strip()) == 0: + continue + split = item.split(':') + if len(split) != 2: + raise ValueError("%r contains invalid declaration %r" + % (line, item)) + localVars[split[0].strip()] = split[1].strip() + return localVars + + +def loadLocalVariables(filename): + """Accepts a filename and attempts to load the Emacs variable declarations + from that file, simulating what Emacs does. + + See http://www.gnu.org/software/emacs/manual/html_node/File-Variables.html + """ + f = file(filename, "r") + lines = [f.readline(), f.readline()] + f.close() + for line in lines: + try: + return _parseLocalVariables(line) + except ValueError: + pass + return {} + + +def getTestModules(filename): + testCaseVar = loadLocalVariables(filename).get('test-case-name', None) + if testCaseVar is None: + return [] + return testCaseVar.split(',') + + +def isTestFile(filename): + """Returns true if 'filename' looks like a file containing unit tests. + False otherwise. Doesn't care whether filename exists. + """ + basename = os.path.basename(filename) + return (basename.startswith('test_') + and os.path.splitext(basename)[1] == ('.py')) + + +def _zshReporterAction(): + return "(%s)" % (" ".join([p.longOpt for p in plugin.getPlugins(itrial.IReporter)]),) + +class Options(usage.Options, app.ReactorSelectionMixin): + synopsis = """%s [options] [[file|package|module|TestCase|testmethod]...] + """ % (os.path.basename(sys.argv[0]),) + longdesc = ("trial loads and executes a suite of unit tests, obtained " + "from modules, packages and files listed on the command line.") + + optFlags = [["help", "h"], + ["rterrors", "e", "realtime errors, print out tracebacks as " + "soon as they occur"], + ["debug", "b", "Run tests in the Python debugger. 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"], + ["force-gc", None, "Have Trial run gc.collect() before and " + "after each test case."], + ["profile", None, "Run tests under the Python profiler"], + ["unclean-warnings", None, + "Turn dirty reactor errors into warnings"], + ["until-failure", "u", "Repeat test until it fails"], + ["no-recurse", "N", "Don't recurse into packages"], + ['help-reporters', None, + "Help on available output plugins (reporters)"] + ] + + optParameters = [ + ["logfile", "l", "test.log", "log file name"], + ["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.']] + + zsh_actions = {"tbformat":"(plain emacs cgitb)", + "reporter":_zshReporterAction} + zsh_actionDescr = {"logfile":"log file name", + "random":"random seed"} + zsh_extras = ["*:file|module|package|TestCase|testMethod:_files -g '*.py'"] + + fallbackReporter = reporter.TreeReporter + extra = None + tracer = None + + def __init__(self): + self['tests'] = set() + usage.Options.__init__(self) + + def opt_coverage(self): + """ + Generate coverage information in the _trial_temp/coverage. Requires + Python 2.3.3. + """ + coverdir = 'coverage' + print "Setting coverage directory to %s." % (coverdir,) + import trace + + # begin monkey patch --------------------------- + # Before Python 2.4, this function asserted that 'filename' had + # to end with '.py' This is wrong for at least two reasons: + # 1. We might be wanting to find executable line nos in a script + # 2. The implementation should use os.splitext + # This monkey patch is the same function as in the stdlib (v2.3) + # but with the assertion removed. + def find_executable_linenos(filename): + """Return dict where keys are line numbers in the line number + table. + """ + #assert filename.endswith('.py') # YOU BASTARDS + try: + prog = open(filename).read() + prog = '\n'.join(prog.splitlines()) + '\n' + except IOError, err: + sys.stderr.write("Not printing coverage data for %r: %s\n" + % (filename, err)) + sys.stderr.flush() + return {} + code = compile(prog, filename, "exec") + strs = trace.find_strings(filename) + return trace.find_lines(code, strs) + + trace.find_executable_linenos = find_executable_linenos + # end monkey patch ------------------------------ + + self.coverdir = os.path.abspath(os.path.join(self['temp-directory'], coverdir)) + self.tracer = trace.Trace(count=1, trace=0) + sys.settrace(self.tracer.globaltrace) + + 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'].add(filename) + else: + self['tests'].update(getTestModules(filename)) + + def opt_spew(self): + """Print an insanely verbose log of everything that happens. Useful + when debugging freezes or locks in complex code.""" + sys.settrace(spewer) + + + 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 + print + sys.exit(0) + + def opt_disablegc(self): + """Disable the garbage collector""" + 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_extra(self, arg): + """ + Add an extra argument. (This is a hack necessary for interfacing with + emacs's `gud'.) + """ + if self.extra is None: + self.extra = [] + self.extra.append(arg) + opt_x = opt_extra + + 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") + + def opt_random(self, option): + try: + self['random'] = long(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'] = long(time.time() * 100) + + def opt_without_module(self, option): + """ + Fake the lack of the specified modules, separated with commas. + """ + 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'].update(args) + if self.extra is not None: + self['tests'].update(self.extra) + + 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['nopm']: + if not self['debug']: + raise usage.UsageError("you must specify --debug when using " + "--nopm ") + failure.DO_POST_MORTEM = False + + +def _initialDebugSetup(config): + # do this part of debug setup first for easy debugging of import failures + if config['debug']: + failure.startDebugMode() + if config['debug'] or config['debug-stacktraces']: + defer.setDebugging(True) + + +def _getSuite(config): + loader = _getLoader(config) + recurse = not config['no-recurse'] + return loader.loadByNames(config['tests'], recurse) + + +def _getLoader(config): + loader = runner.TestLoader() + if config['random']: + randomer = random.Random() + randomer.seed(config['random']) + loader.sorter = lambda x : randomer.random() + print 'Running tests shuffled with seed %d\n' % config['random'] + if not config['until-failure']: + loader.suiteFactory = runner.DestructiveTestSuite + return loader + + +def _makeRunner(config): + mode = None + if config['debug']: + mode = runner.TrialRunner.DEBUG + if config['dry-run']: + mode = runner.TrialRunner.DRY_RUN + return runner.TrialRunner(config['reporter'], + mode=mode, + profile=config['profile'], + logfile=config['logfile'], + tracebackFormat=config['tbformat'], + realTimeErrors=config['rterrors'], + uncleanWarnings=config['unclean-warnings'], + workingDirectory=config['temp-directory'], + forceGarbageCollection=config['force-gc']) + + +def run(): + if len(sys.argv) == 1: + sys.argv.append("--help") + config = Options() + try: + config.parseOptions() + except usage.error, ue: + raise SystemExit, "%s: %s" % (sys.argv[0], ue) + _initialDebugSetup(config) + trialRunner = _makeRunner(config) + suite = _getSuite(config) + if config['until-failure']: + test_result = trialRunner.runUntilFailure(suite) + else: + test_result = trialRunner.run(suite) + if config.tracer: + sys.settrace(None) + results = config.tracer.results() + results.write_results(show_missing=1, summary=False, + coverdir=config.coverdir) + sys.exit(not test_result.wasSuccessful()) + |