OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 | 2 |
3 ''' | 3 ''' |
4 Copyright 2013 Google Inc. | 4 Copyright 2013 Google Inc. |
5 | 5 |
6 Use of this source code is governed by a BSD-style license that can be | 6 Use of this source code is governed by a BSD-style license that can be |
7 found in the LICENSE file. | 7 found in the LICENSE file. |
8 ''' | 8 ''' |
9 | 9 |
10 ''' | 10 ''' |
(...skipping 30 matching lines...) Expand all Loading... |
41 | 41 |
42 | 42 |
43 # Object that generates diffs between two JSON gm result files. | 43 # Object that generates diffs between two JSON gm result files. |
44 class GMDiffer(object): | 44 class GMDiffer(object): |
45 | 45 |
46 def __init__(self): | 46 def __init__(self): |
47 pass | 47 pass |
48 | 48 |
49 def _GetFileContentsAsString(self, filepath): | 49 def _GetFileContentsAsString(self, filepath): |
50 """Returns the full contents of a file, as a single string. | 50 """Returns the full contents of a file, as a single string. |
51 If the filename looks like a URL, download its contents...""" | 51 If the filename looks like a URL, download its contents. |
52 if filepath.startswith('http:') or filepath.startswith('https:'): | 52 If the filename is None, return None.""" |
| 53 if filepath is None: |
| 54 return None |
| 55 elif filepath.startswith('http:') or filepath.startswith('https:'): |
53 return urllib2.urlopen(filepath).read() | 56 return urllib2.urlopen(filepath).read() |
54 else: | 57 else: |
55 return open(filepath, 'r').read() | 58 return open(filepath, 'r').read() |
56 | 59 |
57 def _GetExpectedResults(self, filepath): | 60 def _GetExpectedResults(self, contents): |
58 """Returns the dictionary of expected results from a JSON file, | 61 """Returns the dictionary of expected results from a JSON string, |
59 in this form: | 62 in this form: |
60 | 63 |
61 { | 64 { |
62 'test1' : 14760033689012826769, | 65 'test1' : 14760033689012826769, |
63 'test2' : 9151974350149210736, | 66 'test2' : 9151974350149210736, |
64 ... | 67 ... |
65 } | 68 } |
66 | 69 |
67 We make these simplifying assumptions: | 70 We make these simplifying assumptions: |
68 1. Each test has either 0 or 1 allowed results. | 71 1. Each test has either 0 or 1 allowed results. |
69 2. All expectations are of type JSONKEY_HASHTYPE_BITMAP_64BITMD5. | 72 2. All expectations are of type JSONKEY_HASHTYPE_BITMAP_64BITMD5. |
70 | 73 |
71 Any tests which violate those assumptions will cause an exception to | 74 Any tests which violate those assumptions will cause an exception to |
72 be raised. | 75 be raised. |
73 | 76 |
74 Any tests for which we have no expectations will be left out of the | 77 Any tests for which we have no expectations will be left out of the |
75 returned dictionary. | 78 returned dictionary. |
76 """ | 79 """ |
77 result_dict = {} | 80 result_dict = {} |
78 contents = self._GetFileContentsAsString(filepath) | |
79 json_dict = gm_json.LoadFromString(contents) | 81 json_dict = gm_json.LoadFromString(contents) |
80 all_expectations = json_dict[gm_json.JSONKEY_EXPECTEDRESULTS] | 82 all_expectations = json_dict[gm_json.JSONKEY_EXPECTEDRESULTS] |
81 for test_name in all_expectations.keys(): | 83 for test_name in all_expectations.keys(): |
82 test_expectations = all_expectations[test_name] | 84 test_expectations = all_expectations[test_name] |
83 allowed_digests = test_expectations[ | 85 allowed_digests = test_expectations[ |
84 gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS] | 86 gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS] |
85 if allowed_digests: | 87 if allowed_digests: |
86 num_allowed_digests = len(allowed_digests) | 88 num_allowed_digests = len(allowed_digests) |
87 if num_allowed_digests > 1: | 89 if num_allowed_digests > 1: |
88 raise ValueError( | 90 raise ValueError( |
89 'test %s in file %s has %d allowed digests' % ( | 91 'test %s has %d allowed digests' % ( |
90 test_name, filepath, num_allowed_digests)) | 92 test_name, num_allowed_digests)) |
91 digest_pair = allowed_digests[0] | 93 digest_pair = allowed_digests[0] |
92 if digest_pair[0] != gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD5: | 94 if digest_pair[0] != gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD5: |
93 raise ValueError( | 95 raise ValueError( |
94 'test %s in file %s has unsupported hashtype %s' % ( | 96 'test %s has unsupported hashtype %s' % ( |
95 test_name, filepath, digest_pair[0])) | 97 test_name, digest_pair[0])) |
96 result_dict[test_name] = digest_pair[1] | 98 result_dict[test_name] = digest_pair[1] |
97 return result_dict | 99 return result_dict |
98 | 100 |
99 def _GetActualResults(self, filepath): | 101 def _GetActualResults(self, contents): |
100 """Returns the dictionary of actual results from a JSON file, | 102 """Returns the dictionary of actual results from a JSON string, |
101 in this form: | 103 in this form: |
102 | 104 |
103 { | 105 { |
104 'test1' : 14760033689012826769, | 106 'test1' : 14760033689012826769, |
105 'test2' : 9151974350149210736, | 107 'test2' : 9151974350149210736, |
106 ... | 108 ... |
107 } | 109 } |
108 | 110 |
109 We make these simplifying assumptions: | 111 We make these simplifying assumptions: |
110 1. All results are of type JSONKEY_HASHTYPE_BITMAP_64BITMD5. | 112 1. All results are of type JSONKEY_HASHTYPE_BITMAP_64BITMD5. |
111 | 113 |
112 Any tests which violate those assumptions will cause an exception to | 114 Any tests which violate those assumptions will cause an exception to |
113 be raised. | 115 be raised. |
114 | 116 |
115 Any tests for which we have no actual results will be left out of the | 117 Any tests for which we have no actual results will be left out of the |
116 returned dictionary. | 118 returned dictionary. |
117 """ | 119 """ |
118 result_dict = {} | 120 result_dict = {} |
119 contents = self._GetFileContentsAsString(filepath) | |
120 json_dict = gm_json.LoadFromString(contents) | 121 json_dict = gm_json.LoadFromString(contents) |
121 all_result_types = json_dict[gm_json.JSONKEY_ACTUALRESULTS] | 122 all_result_types = json_dict[gm_json.JSONKEY_ACTUALRESULTS] |
122 for result_type in all_result_types.keys(): | 123 for result_type in all_result_types.keys(): |
123 results_of_this_type = all_result_types[result_type] | 124 results_of_this_type = all_result_types[result_type] |
124 if results_of_this_type: | 125 if results_of_this_type: |
125 for test_name in results_of_this_type.keys(): | 126 for test_name in results_of_this_type.keys(): |
126 digest_pair = results_of_this_type[test_name] | 127 digest_pair = results_of_this_type[test_name] |
127 if digest_pair[0] != gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD
5: | 128 if digest_pair[0] != gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD
5: |
128 raise ValueError( | 129 raise ValueError( |
129 'test %s in file %s has unsupported hashtype %s' % ( | 130 'test %s has unsupported hashtype %s' % ( |
130 test_name, filepath, digest_pair[0])) | 131 test_name, digest_pair[0])) |
131 result_dict[test_name] = digest_pair[1] | 132 result_dict[test_name] = digest_pair[1] |
132 return result_dict | 133 return result_dict |
133 | 134 |
134 def _DictionaryDiff(self, old_dict, new_dict): | 135 def _DictionaryDiff(self, old_dict, new_dict): |
135 """Generate a dictionary showing the diffs between old_dict and new_dict
. | 136 """Generate a dictionary showing the diffs between old_dict and new_dict
. |
136 Any entries which are identical across them will be left out.""" | 137 Any entries which are identical across them will be left out.""" |
137 diff_dict = {} | 138 diff_dict = {} |
138 all_keys = set(old_dict.keys() + new_dict.keys()) | 139 all_keys = set(old_dict.keys() + new_dict.keys()) |
139 for key in all_keys: | 140 for key in all_keys: |
140 if old_dict.get(key) != new_dict.get(key): | 141 if old_dict.get(key) != new_dict.get(key): |
141 new_entry = {} | 142 new_entry = {} |
142 new_entry['old'] = old_dict.get(key) | 143 new_entry['old'] = old_dict.get(key) |
143 new_entry['new'] = new_dict.get(key) | 144 new_entry['new'] = new_dict.get(key) |
144 diff_dict[key] = new_entry | 145 diff_dict[key] = new_entry |
145 return diff_dict | 146 return diff_dict |
146 | 147 |
147 def GenerateDiffDict(self, oldfile, newfile=None): | 148 def GenerateDiffDict(self, oldfile, newfile=None): |
148 """Generate a dictionary showing the diffs: | 149 """Generate a dictionary showing the diffs: |
149 old = expectations within oldfile | 150 old = expectations within oldfile |
150 new = expectations within newfile | 151 new = expectations within newfile |
151 | 152 |
152 If newfile is not specified, then 'new' is the actual results within | 153 If newfile is not specified, then 'new' is the actual results within |
153 oldfile. | 154 oldfile. |
154 """ | 155 """ |
155 old_results = self._GetExpectedResults(oldfile) | 156 return self.GenerateDiffDictFromStrings(self._GetFileContentsAsString(ol
dfile), |
156 if newfile: | 157 self._GetFileContentsAsString(ne
wfile)) |
157 new_results = self._GetExpectedResults(newfile) | 158 |
| 159 def GenerateDiffDictFromStrings(self, oldjson, newjson=None): |
| 160 """Generate a dictionary showing the diffs: |
| 161 old = expectations within oldjson |
| 162 new = expectations within newjson |
| 163 |
| 164 If newfile is not specified, then 'new' is the actual results within |
| 165 oldfile. |
| 166 """ |
| 167 old_results = self._GetExpectedResults(oldjson) |
| 168 if newjson: |
| 169 new_results = self._GetExpectedResults(newjson) |
158 else: | 170 else: |
159 new_results = self._GetActualResults(oldfile) | 171 new_results = self._GetActualResults(oldjson) |
160 return self._DictionaryDiff(old_results, new_results) | 172 return self._DictionaryDiff(old_results, new_results) |
161 | 173 |
162 | 174 |
163 def _Main(): | 175 def _Main(): |
164 parser = argparse.ArgumentParser() | 176 parser = argparse.ArgumentParser() |
165 parser.add_argument( | 177 parser.add_argument( |
166 'old', | 178 'old', |
167 help='Path to JSON file whose expectations to display on ' + | 179 help='Path to JSON file whose expectations to display on ' + |
168 'the "old" side of the diff. This can be a filepath on ' + | 180 'the "old" side of the diff. This can be a filepath on ' + |
169 'local storage, or a URL.') | 181 'local storage, or a URL.') |
170 parser.add_argument( | 182 parser.add_argument( |
171 'new', nargs='?', | 183 'new', nargs='?', |
172 help='Path to JSON file whose expectations to display on ' + | 184 help='Path to JSON file whose expectations to display on ' + |
173 'the "new" side of the diff; if not specified, uses the ' + | 185 'the "new" side of the diff; if not specified, uses the ' + |
174 'ACTUAL results from the "old" JSON file. This can be a ' + | 186 'ACTUAL results from the "old" JSON file. This can be a ' + |
175 'filepath on local storage, or a URL.') | 187 'filepath on local storage, or a URL.') |
176 args = parser.parse_args() | 188 args = parser.parse_args() |
177 differ = GMDiffer() | 189 differ = GMDiffer() |
178 diffs = differ.GenerateDiffDict(oldfile=args.old, newfile=args.new) | 190 diffs = differ.GenerateDiffDict(oldfile=args.old, newfile=args.new) |
179 json.dump(diffs, sys.stdout, sort_keys=True, indent=2) | 191 json.dump(diffs, sys.stdout, sort_keys=True, indent=2) |
180 | 192 |
181 | 193 |
182 if __name__ == '__main__': | 194 if __name__ == '__main__': |
183 _Main() | 195 _Main() |
OLD | NEW |