Index: testing/scripts/common.py |
diff --git a/testing/scripts/common.py b/testing/scripts/common.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..92ad6882ec956d6d0342091f76d22c57fb7c005e |
--- /dev/null |
+++ b/testing/scripts/common.py |
@@ -0,0 +1,117 @@ |
+# Copyright 2014 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+import argparse |
+import contextlib |
+import json |
+import os |
+import subprocess |
+import tempfile |
+ |
+ |
+SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__)) |
+SRC_DIR = os.path.abspath( |
+ os.path.join(SCRIPT_DIR, os.path.pardir, os.path.pardir)) |
+ |
+ |
+# run-webkit-tests returns the number of failures as the return |
+# code, but caps the return code at 101 to avoid overflow or colliding |
+# with reserved values from the shell. |
+MAX_FAILURES_EXIT_STATUS = 101 |
+ |
+ |
+def run_script(argv, funcs): |
+ def parse_json(path): |
+ with open(path) as f: |
+ return json.load(f) |
+ parser = argparse.ArgumentParser() |
+ # TODO(phajdan.jr): Make build-config-fs required after passing it in recipe. |
+ parser.add_argument('--build-config-fs') |
+ parser.add_argument('--paths', type=parse_json, default={}) |
+ parser.add_argument('--properties', type=parse_json, default={}) |
+ |
+ subparsers = parser.add_subparsers() |
+ |
+ run_parser = subparsers.add_parser('run') |
+ run_parser.add_argument( |
+ '--output', type=argparse.FileType('w'), required=True) |
+ run_parser.add_argument('--filter-file', type=argparse.FileType('r')) |
+ run_parser.set_defaults(func=funcs['run']) |
+ |
+ run_parser = subparsers.add_parser('compile_targets') |
+ run_parser.add_argument( |
+ '--output', type=argparse.FileType('w'), required=True) |
+ run_parser.set_defaults(func=funcs['compile_targets']) |
+ |
+ args = parser.parse_args(argv) |
+ return args.func(args) |
+ |
+ |
+def run_command(argv): |
+ print 'Running %r' % argv |
+ rc = subprocess.call(argv) |
+ print 'Command %r returned exit code %d' % (argv, rc) |
+ return rc |
+ |
+ |
+@contextlib.contextmanager |
+def temporary_file(): |
+ fd, path = tempfile.mkstemp() |
+ os.close(fd) |
+ try: |
+ yield path |
+ finally: |
+ os.remove(path) |
+ |
+ |
+def parse_common_test_results(json_results): |
+ def convert_trie_to_flat_paths(trie, prefix=None): |
+ # Also see webkitpy.layout_tests.layout_package.json_results_generator |
+ result = {} |
+ for name, data in trie.iteritems(): |
+ if prefix: |
+ name = prefix + '/' + name |
+ if len(data) and not 'actual' in data and not 'expected' in data: |
+ result.update(convert_trie_to_flat_paths(data, name)) |
+ else: |
+ result[name] = data |
+ return result |
+ |
+ results = { |
+ 'passes': {}, |
+ 'unexpected_passes': {}, |
+ 'failures': {}, |
+ 'unexpected_failures': {}, |
+ 'flakes': {}, |
+ 'unexpected_flakes': {}, |
+ } |
+ |
+ # TODO(dpranke): crbug.com/357866 - we should simplify the handling of |
+ # both the return code and parsing the actual results, below. |
+ |
+ passing_statuses = ('PASS', 'SLOW', 'NEEDSREBASELINE', |
+ 'NEEDSMANUALREBASELINE') |
+ |
+ for test, result in convert_trie_to_flat_paths( |
+ json_results['tests']).iteritems(): |
+ key = 'unexpected_' if result.get('is_unexpected') else '' |
+ data = result['actual'] |
+ actual_results = data.split() |
+ last_result = actual_results[-1] |
+ expected_results = result['expected'].split() |
+ |
+ if (len(actual_results) > 1 and |
+ (last_result in expected_results or last_result in passing_statuses)): |
+ key += 'flakes' |
+ elif last_result in passing_statuses: |
+ key += 'passes' |
+ # TODO(dpranke): crbug.com/357867 ... Why are we assigning result |
+ # instead of actual_result here. Do we even need these things to be |
+ # hashes, or just lists? |
+ data = result |
+ else: |
+ key += 'failures' |
+ results[key][test] = data |
+ |
+ return results |