1 #!/usr/bin/python 2 import json 3 import optparse 4 import os 5 import sys 6 7 from webkitpy.common.host import Host 8 from webkitpy.layout_tests.port import platform_options, configuration_options 9 10 11 def main(argv): 12 parser = optparse.OptionParser(usage='%prog [path-to-results.json]') 13 parser.add_option('--failures', action='store_true', 14 help='show failing tests') 15 parser.add_option('--flakes', action='store_true', 16 help='show flaky tests') 17 parser.add_option('--expected', action='store_true', 18 help='include expected results along with unexpected') 19 parser.add_option('--passes', action='store_true', 20 help='show passing tests') 21 parser.add_option('--ignored-failures-path', action='store', 22 help='ignore failures seen in a previous run') 23 parser.add_options(platform_options()) 24 parser.add_options(configuration_options()) 25 options, args = parser.parse_args(argv) 26 27 host = Host() 28 if args: 29 if args[0] == '-': 30 txt = sys.stdin.read() 31 elif os.path.exists(args[0]): 32 with open(args[0], 'r') as fp: 33 txt = fp.read() 34 else: 35 print >> sys.stderr, "file not found: %s" % args[0] 36 sys.exit(1) 37 else: 38 txt = host.filesystem.read_text_file(host.filesystem.join(host.port_factory.get(options=options).results_directory(), 'full_results.json')) 39 40 if txt.startswith('ADD_RESULTS(') and txt.endswith(');'): 41 txt = txt[12:-2] # ignore optional JSONP wrapper 42 results = json.loads(txt) 43 44 passes, failures, flakes = decode_results(results, options.expected) 45 46 tests_to_print = [] 47 if options.passes: 48 tests_to_print += passes.keys() 49 if options.failures: 50 tests_to_print += failures.keys() 51 if options.flakes: 52 tests_to_print += flakes.keys() 53 print "\n".join(sorted(tests_to_print)) 54 55 if options.ignored_failures_path: 56 with open(options.ignored_failures_path, 'r') as fp: 57 txt = fp.read() 58 if txt.startswith('ADD_RESULTS(') and txt.endswith(');'): 59 txt = txt[12:-2] # ignore optional JSONP wrapper 60 results = json.loads(txt) 61 _, ignored_failures, _ = decode_results(results, options.expected) 62 new_failures = set(failures.keys()) - set(ignored_failures.keys()) 63 if new_failures: 64 print "New failures:" 65 print "\n".join(sorted(new_failures)) 66 print 67 if ignored_failures: 68 print "Ignored failures:" 69 print "\n".join(sorted(ignored_failures.keys())) 70 if new_failures: 71 return 1 72 return 0 73 74 75 def decode_results(results, include_expected=False): 76 tests = convert_trie_to_flat_paths(results['tests']) 77 failures = {} 78 flakes = {} 79 passes = {} 80 for (test, result) in tests.iteritems(): 81 if include_expected or result.get('is_unexpected'): 82 actual_result = result['actual'] 83 if ' PASS' in actual_result: 84 flakes[test] = actual_result 85 elif actual_result == 'PASS': 86 passes[test] = result 87 else: 88 failures[test] = actual_result 89 90 return (passes, failures, flakes) 91 92 93 def convert_trie_to_flat_paths(trie, prefix=None): 94 # Cloned from webkitpy.layout_tests.layout_package.json_results_generator 95 # so that this code can stand alone. 96 result = {} 97 for name, data in trie.iteritems(): 98 if prefix: 99 name = prefix + "/" + name 100 101 if len(data) and not "actual" in data and not "expected" in data: 102 result.update(convert_trie_to_flat_paths(data, name)) 103 else: 104 result[name] = data 105 106 return result 107 108 109 if __name__ == '__main__': 110 sys.exit(main(sys.argv[1:])) 111