Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(67)

Unified Diff: third_party/typ/typ/runner.py

Issue 664123005: Roll typ to v0.8.6 / re25b780b0b147580ef248c212b238446264d9d78 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/typ/typ/host.py ('k') | third_party/typ/typ/tests/host_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/typ/typ/runner.py
diff --git a/third_party/typ/typ/runner.py b/third_party/typ/typ/runner.py
index 3a8dc8212c172a7f9f70a9ba868f7e10aac30c59..61aeb9d9e39df6362d15e5766c408fc1137312d4 100644
--- a/third_party/typ/typ/runner.py
+++ b/third_party/typ/typ/runner.py
@@ -29,6 +29,8 @@ from collections import OrderedDict
# that typ/runner.py works when invoked via subprocess on windows in
# _spawn_main().
path_to_file = os.path.realpath(__file__)
+if path_to_file.endswith('.pyc'): # pragma: no cover
+ path_to_file = path_to_file[:-1]
dir_above_typ = os.path.dirname(os.path.dirname(path_to_file))
if dir_above_typ not in sys.path: # pragma: no cover
sys.path.append(dir_above_typ)
@@ -49,17 +51,12 @@ ResultSet = json_results.ResultSet
ResultType = json_results.ResultType
-def main(argv=None, host=None, stdout=None, stderr=None,
- win_multiprocessing=None, **defaults):
+def main(argv=None, host=None, win_multiprocessing=None, **defaults):
host = host or Host()
- if stdout:
- host.stdout = stdout
- if stderr:
- host.stderr = stderr
runner = Runner(host=host)
-
- return runner.main(argv, win_multiprocessing=win_multiprocessing,
- **defaults)
+ if win_multiprocessing is not None:
+ runner.win_multiprocessing = win_multiprocessing
+ return runner.main(argv, **defaults)
class TestInput(object):
@@ -74,8 +71,7 @@ class TestInput(object):
class TestSet(object):
def __init__(self, parallel_tests=None, isolated_tests=None,
- tests_to_skip=None, context=None, setup_fn=None,
- teardown_fn=None):
+ tests_to_skip=None):
def promote(tests):
tests = tests or []
@@ -85,18 +81,14 @@ class TestSet(object):
self.parallel_tests = promote(parallel_tests)
self.isolated_tests = promote(isolated_tests)
self.tests_to_skip = promote(tests_to_skip)
- self.context = context
- self.setup_fn = setup_fn
- self.teardown_fn = teardown_fn
class WinMultiprocessing(object):
- force = 'force'
ignore = 'ignore'
- run_serially = 'run_serially'
+ importable = 'importable'
spawn = 'spawn'
- values = [force, ignore, run_serially, spawn]
+ values = [ignore, importable, spawn]
class _AddTestsError(Exception):
@@ -106,29 +98,32 @@ class _AddTestsError(Exception):
class Runner(object):
def __init__(self, host=None):
+ self.args = None
+ self.classifier = None
+ self.cov = None
+ self.context = None
+ self.coverage_source = None
self.host = host or Host()
self.loader = unittest.loader.TestLoader()
self.printer = None
+ self.setup_fn = None
self.stats = None
- self.cov = None
- self.coverage_source = None
+ self.teardown_fn = None
self.top_level_dir = None
- self.args = None
+ self.win_multiprocessing = WinMultiprocessing.spawn
# initialize self.args to the defaults.
parser = ArgumentParser(self.host)
self.parse_args(parser, [])
- def main(self, argv=None, win_multiprocessing=None, **defaults):
+ def main(self, argv=None, **defaults):
parser = ArgumentParser(self.host)
self.parse_args(parser, argv, **defaults)
if parser.exit_status is not None:
return parser.exit_status
try:
- ret = self._handle_win_multiprocessing('main', win_multiprocessing)
- if ret is None:
- ret, _, _ = self.run(win_multiprocessing=win_multiprocessing)
+ ret, _, _ = self.run()
return ret
except KeyboardInterrupt:
self.print_("interrupted, exiting", stream=self.host.stderr)
@@ -145,65 +140,10 @@ class Runner(object):
if parser.exit_status is not None:
return
- def _handle_win_multiprocessing(self, entry_point, win_multiprocessing,
- allow_spawn=True):
- wmp = win_multiprocessing
- force, ignore, run_serially, spawn = WinMultiprocessing.values
-
- if (wmp is not None and wmp not in WinMultiprocessing.values):
- raise ValueError('illegal value %s for win_multiprocessing' %
- wmp)
-
- # First, check if __main__ is importable; if it is, we're fine.
- if (self._main_is_importable() and wmp != force):
- return None
-
- if wmp is None and self.args.jobs == 1:
- return None
-
- if wmp is None:
- raise ValueError(
- 'The __main__ module is not importable; The caller '
- 'must pass a valid WinMultiprocessing value (one of %s) '
- 'to %s to tell typ how to handle Windows.' %
- (WinMultiprocessing.values, entry_point))
-
- h = self.host
-
- if (h.platform != 'win32' and wmp != force):
- return
-
- if wmp == ignore: # pragma: win32
- raise ValueError('Cannot use WinMultiprocessing.ignore for '
- 'win_multiprocessing when actually running '
- 'on Windows.')
-
- if wmp == run_serially: # pragma: win32
- self.args.jobs = 1
- return None
-
- assert allow_spawn, ('Cannot use WinMultiprocessing.spawn '
- 'in %s' % entry_point)
- assert wmp in (force, spawn)
- argv = ArgumentParser(h).argv_from_args(self.args)
- return h.call_inline([h.python_interpreter, path_to_file] + argv)
-
- def _main_is_importable(self):
- path = self.host.realpath(sys.modules['__main__'].__file__)
- if not path or not path.endswith('.py'): # pragma: no cover
- return False
-
- for d in sys.path:
- if path.startswith(self.host.realpath(d)):
- return True
- return False # pragma: no cover
-
def print_(self, msg='', end='\n', stream=None):
self.host.print_(msg, end, stream=stream)
- def run(self, test_set=None, classifier=None,
- context=None, setup_fn=None, teardown_fn=None,
- win_multiprocessing=None):
+ def run(self, test_set=None):
ret = 0
h = self.host
@@ -212,8 +152,9 @@ class Runner(object):
self.print_(VERSION)
return ret, None, None
- self._handle_win_multiprocessing('Runner.run', win_multiprocessing,
- allow_spawn=False)
+ should_spawn = self._check_win_multiprocessing()
+ if should_spawn:
+ return self._spawn(test_set)
ret = self._set_up_runner()
if ret: # pragma: no cover
@@ -228,8 +169,7 @@ class Runner(object):
result_set = ResultSet()
if not test_set:
- ret, test_set = self.find_tests(self.args, classifier, context,
- setup_fn, teardown_fn)
+ ret, test_set = self.find_tests(self.args)
find_end = h.time()
if not ret:
@@ -243,8 +183,8 @@ class Runner(object):
trace = self._trace_from_results(result_set)
if full_results:
self._summarize(full_results)
- self.write_results(full_results)
- upload_ret = self.upload_results(full_results)
+ self._write(self.args.write_full_results_to, full_results)
+ upload_ret = self._upload(full_results)
if not ret:
ret = upload_ret
reporting_end = h.time()
@@ -252,13 +192,93 @@ class Runner(object):
self._add_trace_event(trace, 'discovery', find_start, find_end)
self._add_trace_event(trace, 'testing', find_end, test_end)
self._add_trace_event(trace, 'reporting', test_end, reporting_end)
- self.write_trace(trace)
+ self._write(self.args.write_trace_to, trace)
self.report_coverage()
else:
upload_ret = 0
return ret, full_results, trace
+ def _check_win_multiprocessing(self):
+ wmp = self.win_multiprocessing
+
+ ignore, importable, spawn = WinMultiprocessing.values
+
+ if wmp not in WinMultiprocessing.values:
+ raise ValueError('illegal value %s for win_multiprocessing' %
+ wmp)
+
+ h = self.host
+ if wmp == ignore and h.platform == 'win32': # pragma: win32
+ raise ValueError('Cannot use WinMultiprocessing.ignore for '
+ 'win_multiprocessing when actually running '
+ 'on Windows.')
+
+ if wmp == ignore or self.args.jobs == 1:
+ return False
+
+ if wmp == importable:
+ if self._main_is_importable():
+ return False
+ raise ValueError('The __main__ module (%s) ' # pragma: no cover
+ 'may not be importable' %
+ sys.modules['__main__'].__file__)
+
+ assert wmp == spawn
+ return True
+
+ def _main_is_importable(self): # pragma: untested
+ path = sys.modules['__main__'].__file__
+ if not path:
+ return False
+ if path.endswith('.pyc'):
+ path = path[:-1]
+ if not path.endswith('.py'):
+ return False
+ if path.endswith('__main__.py'):
+ # main modules are not directly importable.
+ return False
+
+ path = self.host.realpath(path)
+ for d in sys.path:
+ if path.startswith(self.host.realpath(d)):
+ return True
+ return False # pragma: no cover
+
+ def _spawn(self, test_set):
+ # TODO: Handle picklable hooks, rather than requiring them to be None.
+ assert self.classifier is None
+ assert self.context is None
+ assert self.setup_fn is None
+ assert self.teardown_fn is None
+ assert test_set is None
+ h = self.host
+
+ if self.args.write_trace_to: # pragma: untested
+ should_delete_trace = False
+ else:
+ should_delete_trace = True
+ fp = h.mktempfile(delete=False)
+ fp.close()
+ self.args.write_trace_to = fp.name
+
+ if self.args.write_full_results_to: # pragma: untested
+ should_delete_results = False
+ else:
+ should_delete_results = True
+ fp = h.mktempfile(delete=False)
+ fp.close()
+ self.args.write_full_results_to = fp.name
+
+ argv = ArgumentParser(h).argv_from_args(self.args)
+ ret = h.call_inline([h.python_interpreter, path_to_file] + argv)
+
+ trace = self._read_and_delete(self.args.write_trace_to,
+ should_delete_trace)
+ full_results = self._read_and_delete(self.args.write_full_results_to,
+ should_delete_results)
+ return ret, full_results, trace
+
def _set_up_runner(self):
h = self.host
args = self.args
@@ -303,11 +323,8 @@ class Runner(object):
self.cov.erase()
return 0
- def find_tests(self, args, classifier=None,
- context=None, setup_fn=None, teardown_fn=None):
- test_set = self._make_test_set(context=context,
- setup_fn=setup_fn,
- teardown_fn=teardown_fn)
+ def find_tests(self, args):
+ test_set = TestSet()
orig_skip = unittest.skip
orig_skip_if = unittest.skipIf
@@ -317,7 +334,7 @@ class Runner(object):
try:
names = self._name_list_from_args(args)
- classifier = classifier or _default_classifier(args)
+ classifier = self.classifier or _default_classifier(args)
for name in names:
try:
@@ -396,7 +413,7 @@ class Runner(object):
self._run_one_set(self.stats, result_set, test_set)
- failed_tests = json_results.failed_test_names(result_set)
+ failed_tests = sorted(json_results.failed_test_names(result_set))
retry_limit = self.args.retry_limit
while retry_limit and failed_tests:
@@ -414,11 +431,7 @@ class Runner(object):
stats = Stats(self.args.status_format, h.time, 1)
stats.total = len(failed_tests)
- tests_to_retry = self._make_test_set(
- isolated_tests=[TestInput(name) for name in failed_tests],
- context=test_set.context,
- setup_fn=test_set.setup_fn,
- teardown_fn=test_set.teardown_fn)
+ tests_to_retry = TestSet(isolated_tests=list(failed_tests))
retry_set = ResultSet()
self._run_one_set(stats, retry_set, tests_to_retry)
result_set.results.extend(retry_set.results)
@@ -435,26 +448,14 @@ class Runner(object):
return (json_results.exit_code_from_full_results(full_results),
full_results)
- def _make_test_set(self, parallel_tests=None, isolated_tests=None,
- tests_to_skip=None, context=None, setup_fn=None,
- teardown_fn=None):
- parallel_tests = parallel_tests or []
- isolated_tests = isolated_tests or []
- tests_to_skip = tests_to_skip or []
- return TestSet(_sort_inputs(parallel_tests),
- _sort_inputs(isolated_tests),
- _sort_inputs(tests_to_skip),
- context, setup_fn, teardown_fn)
-
def _run_one_set(self, stats, result_set, test_set):
stats.total = (len(test_set.parallel_tests) +
len(test_set.isolated_tests) +
len(test_set.tests_to_skip))
self._skip_tests(stats, result_set, test_set.tests_to_skip)
- self._run_list(stats, result_set, test_set,
- test_set.parallel_tests, self.args.jobs)
- self._run_list(stats, result_set, test_set,
- test_set.isolated_tests, 1)
+ self._run_list(stats, result_set, test_set.parallel_tests,
+ self.args.jobs)
+ self._run_list(stats, result_set, test_set.isolated_tests, 1)
def _skip_tests(self, stats, result_set, tests_to_skip):
for test_input in tests_to_skip:
@@ -470,7 +471,7 @@ class Runner(object):
stats.finished += 1
self._print_test_finished(stats, result)
- def _run_list(self, stats, result_set, test_set, test_inputs, jobs):
+ def _run_list(self, stats, result_set, test_inputs, jobs):
h = self.host
running_jobs = set()
@@ -478,7 +479,7 @@ class Runner(object):
if not jobs:
return
- child = _Child(self, self.loader, test_set)
+ child = _Child(self)
pool = make_pool(h, jobs, _run_one_test, child,
_setup_process, _teardown_process)
try:
@@ -572,19 +573,22 @@ class Runner(object):
'' if num_failures == 1 else 's'), elide=False)
self.print_()
- def write_trace(self, trace):
- if self.args.write_trace_to:
- self.host.write_text_file(
- self.args.write_trace_to,
- json.dumps(trace, indent=2) + '\n')
-
- def write_results(self, full_results):
- if self.args.write_full_results_to:
- self.host.write_text_file(
- self.args.write_full_results_to,
- json.dumps(full_results, indent=2) + '\n')
-
- def upload_results(self, full_results):
+ def _read_and_delete(self, path, delete):
+ h = self.host
+ obj = None
+ if h.exists(path):
+ contents = h.read_text_file(path)
+ if contents:
+ obj = json.loads(contents)
+ if delete:
+ h.remove(path)
+ return obj
+
+ def _write(self, path, obj):
+ if path:
+ self.host.write_text_file(path, json.dumps(obj, indent=2) + '\n')
+
+ def _upload(self, full_results):
h = self.host
if not self.args.test_results_server:
return 0
@@ -697,7 +701,7 @@ def _test_adder(test_set, classifier):
class _Child(object):
- def __init__(self, parent, loader, test_set):
+ def __init__(self, parent):
self.host = None
self.worker_num = None
self.all = parent.args.all
@@ -705,11 +709,11 @@ class _Child(object):
self.coverage = parent.args.coverage and parent.args.jobs > 1
self.coverage_source = parent.coverage_source
self.dry_run = parent.args.dry_run
- self.loader = loader
+ self.loader = parent.loader
self.passthrough = parent.args.passthrough
- self.context = test_set.context
- self.setup_fn = test_set.setup_fn
- self.teardown_fn = test_set.teardown_fn
+ self.context = parent.context
+ self.setup_fn = parent.setup_fn
+ self.teardown_fn = parent.teardown_fn
self.context_after_setup = None
self.top_level_dir = parent.top_level_dir
self.loaded_suites = {}
@@ -897,4 +901,5 @@ def _sort_inputs(inps):
if __name__ == '__main__': # pragma: no cover
- sys.exit(main(win_multiprocessing='spawn'))
+ sys.modules['__main__'].__file__ = path_to_file
+ sys.exit(main(win_multiprocessing=WinMultiprocessing.importable))
« no previous file with comments | « third_party/typ/typ/host.py ('k') | third_party/typ/typ/tests/host_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698