| #!/usr/bin/env vpython |
| import json |
| import optparse |
| import os |
| import sys |
| |
| from webkitpy.common.host import Host |
| from webkitpy.layout_tests.port import platform_options, configuration_options |
| |
| |
| def main(argv): |
| parser = optparse.OptionParser(usage='%prog [path-to-results.json]') |
| parser.add_option('--failures', action='store_true', |
| help='show failing tests') |
| parser.add_option('--flakes', action='store_true', |
| help='show flaky tests') |
| parser.add_option('--expected', action='store_true', |
| help='include expected results along with unexpected') |
| parser.add_option('--passes', action='store_true', |
| help='show passing tests') |
| parser.add_option('--ignored-failures-path', action='store', |
| help='ignore failures seen in a previous run') |
| parser.add_options(platform_options()) |
| parser.add_options(configuration_options()) |
| options, args = parser.parse_args(argv) |
| |
| host = Host() |
| if args: |
| if args[0] == '-': |
| txt = sys.stdin.read() |
| elif os.path.exists(args[0]): |
| with open(args[0], 'r') as fp: |
| txt = fp.read() |
| else: |
| print >> sys.stderr, "file not found: %s" % args[0] |
| sys.exit(1) |
| else: |
| txt = host.filesystem.read_text_file( |
| host.filesystem.join( |
| host.port_factory.get( |
| options=options).results_directory(), |
| 'full_results.json')) |
| |
| if txt.startswith('ADD_RESULTS(') and txt.endswith(');'): |
| txt = txt[12:-2] # ignore optional JSONP wrapper |
| results = json.loads(txt) |
| |
| passes, failures, flakes = decode_results(results, options.expected) |
| |
| tests_to_print = [] |
| if options.passes: |
| tests_to_print += passes.keys() |
| if options.failures: |
| tests_to_print += failures.keys() |
| if options.flakes: |
| tests_to_print += flakes.keys() |
| print "\n".join(sorted(tests_to_print)) |
| |
| if options.ignored_failures_path: |
| with open(options.ignored_failures_path, 'r') as fp: |
| txt = fp.read() |
| if txt.startswith('ADD_RESULTS(') and txt.endswith(');'): |
| txt = txt[12:-2] # ignore optional JSONP wrapper |
| results = json.loads(txt) |
| _, ignored_failures, _ = decode_results(results, options.expected) |
| new_failures = set(failures.keys()) - set(ignored_failures.keys()) |
| if new_failures: |
| print "New failures:" |
| print "\n".join(sorted(new_failures)) |
| print |
| if ignored_failures: |
| print "Ignored failures:" |
| print "\n".join(sorted(ignored_failures.keys())) |
| if new_failures: |
| return 1 |
| return 0 |
| |
| |
| def decode_results(results, include_expected=False): |
| tests = convert_trie_to_flat_paths(results['tests']) |
| failures = {} |
| flakes = {} |
| passes = {} |
| for (test, result) in tests.iteritems(): |
| if include_expected or result.get('is_unexpected'): |
| actual_results = result['actual'].split() |
| expected_results = result['expected'].split() |
| if len(actual_results) > 1: |
| if actual_results[1] in expected_results: |
| flakes[test] = actual_results[0] |
| else: |
| # We report the first failure type back, even if the second |
| # was more severe. |
| failures[test] = actual_results[0] |
| elif actual_results[0] == 'PASS': |
| passes[test] = result |
| else: |
| failures[test] = actual_results[0] |
| |
| return (passes, failures, flakes) |
| |
| |
| def convert_trie_to_flat_paths(trie, prefix=None): |
| # Cloned from webkitpy.layout_tests.layout_package.json_results_generator |
| # so that this code can stand alone. |
| 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 |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:])) |