epoger@google.com | a55e48d | 2013-05-21 16:06:40 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
| 6 | """Utility to display a summary of JSON-format GM results, and exit with |
| 7 | a nonzero errorcode if there were non-ignored failures in the GM results. |
| 8 | |
| 9 | Usage: |
| 10 | python display_json_results.py <filename> |
| 11 | |
| 12 | TODO(epoger): We may want to add flags to set the following: |
| 13 | - which error types cause a nonzero return code |
| 14 | - maximum number of tests to list for any one ResultAccumulator |
| 15 | (to keep the output reasonably short) |
| 16 | """ |
| 17 | |
| 18 | __author__ = 'Elliot Poger' |
| 19 | |
| 20 | |
epoger@google.com | 9d33154 | 2013-05-28 15:25:38 +0000 | [diff] [blame] | 21 | # system-level imports |
epoger@google.com | a55e48d | 2013-05-21 16:06:40 +0000 | [diff] [blame] | 22 | import sys |
| 23 | |
epoger@google.com | 9d33154 | 2013-05-28 15:25:38 +0000 | [diff] [blame] | 24 | # local imports |
| 25 | import gm_json |
epoger@google.com | a55e48d | 2013-05-21 16:06:40 +0000 | [diff] [blame] | 26 | |
| 27 | |
| 28 | class ResultAccumulator(object): |
| 29 | """Object that accumulates results of a given type, and can generate a |
| 30 | summary upon request.""" |
| 31 | |
| 32 | def __init__(self, name, do_list, do_fail): |
| 33 | """name: name of the category this result type falls into |
| 34 | do_list: whether to list all of the tests with this results type |
| 35 | do_fail: whether to return with nonzero exit code if there are any |
| 36 | results of this type |
| 37 | """ |
| 38 | self._name = name |
| 39 | self._do_list = do_list |
| 40 | self._do_fail = do_fail |
| 41 | self._testnames = [] |
| 42 | |
| 43 | def AddResult(self, testname): |
| 44 | """Adds a result of this particular type. |
| 45 | testname: (string) name of the test""" |
| 46 | self._testnames.append(testname) |
| 47 | |
| 48 | def ShouldSignalFailure(self): |
| 49 | """Returns true if this result type is serious (self._do_fail is True) |
| 50 | and there were any results of this type.""" |
| 51 | if self._do_fail and self._testnames: |
| 52 | return True |
| 53 | else: |
| 54 | return False |
| 55 | |
| 56 | def GetSummaryLine(self): |
| 57 | """Returns a single-line string summary of all results added to this |
| 58 | accumulator so far.""" |
| 59 | summary = '' |
| 60 | if self._do_fail: |
| 61 | summary += '[*] ' |
| 62 | else: |
| 63 | summary += '[ ] ' |
| 64 | summary += str(len(self._testnames)) |
| 65 | summary += ' ' |
| 66 | summary += self._name |
| 67 | if self._do_list: |
| 68 | summary += ': ' |
| 69 | for testname in self._testnames: |
| 70 | summary += testname |
| 71 | summary += ' ' |
| 72 | return summary |
| 73 | |
| 74 | |
| 75 | def Display(filepath): |
| 76 | """Displays a summary of the results in a JSON file. |
| 77 | Returns True if the results are free of any significant failures. |
| 78 | filepath: (string) path to JSON file""" |
| 79 | |
| 80 | # Map labels within the JSON file to the ResultAccumulator for each label. |
| 81 | results_map = { |
epoger@google.com | 9d33154 | 2013-05-28 15:25:38 +0000 | [diff] [blame] | 82 | gm_json.JSONKEY_ACTUALRESULTS_FAILED: |
epoger@google.com | a55e48d | 2013-05-21 16:06:40 +0000 | [diff] [blame] | 83 | ResultAccumulator(name='ExpectationsMismatch', |
| 84 | do_list=True, do_fail=True), |
epoger@google.com | 9d33154 | 2013-05-28 15:25:38 +0000 | [diff] [blame] | 85 | gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED: |
epoger@google.com | a55e48d | 2013-05-21 16:06:40 +0000 | [diff] [blame] | 86 | ResultAccumulator(name='IgnoredExpectationsMismatch', |
| 87 | do_list=True, do_fail=False), |
epoger@google.com | 9d33154 | 2013-05-28 15:25:38 +0000 | [diff] [blame] | 88 | gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON: |
epoger@google.com | a55e48d | 2013-05-21 16:06:40 +0000 | [diff] [blame] | 89 | ResultAccumulator(name='MissingExpectations', |
| 90 | do_list=False, do_fail=False), |
epoger@google.com | 9d33154 | 2013-05-28 15:25:38 +0000 | [diff] [blame] | 91 | gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED: |
epoger@google.com | a55e48d | 2013-05-21 16:06:40 +0000 | [diff] [blame] | 92 | ResultAccumulator(name='Passed', |
| 93 | do_list=False, do_fail=False), |
| 94 | } |
| 95 | |
| 96 | success = True |
epoger@google.com | 4075fd4 | 2013-06-04 17:50:36 +0000 | [diff] [blame] | 97 | json_dict = gm_json.LoadFromFile(filepath) |
epoger@google.com | 9d33154 | 2013-05-28 15:25:38 +0000 | [diff] [blame] | 98 | actual_results = json_dict[gm_json.JSONKEY_ACTUALRESULTS] |
epoger@google.com | a55e48d | 2013-05-21 16:06:40 +0000 | [diff] [blame] | 99 | for label, accumulator in results_map.iteritems(): |
| 100 | results = actual_results[label] |
| 101 | if results: |
| 102 | for result in results: |
| 103 | accumulator.AddResult(result) |
| 104 | print accumulator.GetSummaryLine() |
| 105 | if accumulator.ShouldSignalFailure(): |
| 106 | success = False |
| 107 | print '(results marked with [*] will cause nonzero return value)' |
| 108 | return success |
| 109 | |
| 110 | |
| 111 | if '__main__' == __name__: |
| 112 | if len(sys.argv) != 2: |
| 113 | raise Exception('usage: %s <input-json-filepath>' % sys.argv[0]) |
| 114 | sys.exit(0 if Display(sys.argv[1]) else 1) |