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 Repackage expected/actual GM results as needed by our HTML rebaseline viewer. | 9 Repackage expected/actual GM results as needed by our HTML rebaseline viewer. |
10 """ | 10 """ |
(...skipping 29 matching lines...) Expand all Loading... |
40 import imagediffdb | 40 import imagediffdb |
41 import imagepair | 41 import imagepair |
42 import imagepairset | 42 import imagepairset |
43 import results | 43 import results |
44 | 44 |
45 EXPECTATION_FIELDS_PASSED_THRU_VERBATIM = [ | 45 EXPECTATION_FIELDS_PASSED_THRU_VERBATIM = [ |
46 results.KEY__EXPECTATIONS__BUGS, | 46 results.KEY__EXPECTATIONS__BUGS, |
47 results.KEY__EXPECTATIONS__IGNOREFAILURE, | 47 results.KEY__EXPECTATIONS__IGNOREFAILURE, |
48 results.KEY__EXPECTATIONS__REVIEWED, | 48 results.KEY__EXPECTATIONS__REVIEWED, |
49 ] | 49 ] |
| 50 DEFAULT_EXPECTATIONS_DIR = os.path.join(TRUNK_DIRECTORY, 'expectations', 'gm') |
50 | 51 |
51 IMAGEPAIR_SET_DESCRIPTIONS = ('expected image', 'actual image') | 52 IMAGEPAIR_SET_DESCRIPTIONS = ('expected image', 'actual image') |
52 | 53 |
53 DEFAULT_ACTUALS_DIR = '.gm-actuals' | |
54 DEFAULT_EXPECTATIONS_DIR = os.path.join(TRUNK_DIRECTORY, 'expectations', 'gm') | |
55 DEFAULT_GENERATED_IMAGES_ROOT = os.path.join( | |
56 PARENT_DIRECTORY, '.generated-images') | |
57 | 54 |
58 | 55 class ExpectationComparisons(results.BaseComparisons): |
59 class Results(object): | 56 """Loads actual and expected GM results into an ImagePairSet. |
60 """ Loads actual and expected GM results into an ImagePairSet. | |
61 | 57 |
62 Loads actual and expected results from all builders, except for those skipped | 58 Loads actual and expected results from all builders, except for those skipped |
63 by _ignore_builder(). | 59 by _ignore_builder(). |
64 | 60 |
65 Once this object has been constructed, the results (in self._results[]) | 61 Once this object has been constructed, the results (in self._results[]) |
66 are immutable. If you want to update the results based on updated JSON | 62 are immutable. If you want to update the results based on updated JSON |
67 file contents, you will need to create a new Results object.""" | 63 file contents, you will need to create a new ExpectationComparisons object.""" |
68 | 64 |
69 def __init__(self, actuals_root=DEFAULT_ACTUALS_DIR, | 65 def __init__(self, actuals_root=results.DEFAULT_ACTUALS_DIR, |
70 expected_root=DEFAULT_EXPECTATIONS_DIR, | 66 expected_root=DEFAULT_EXPECTATIONS_DIR, |
71 generated_images_root=DEFAULT_GENERATED_IMAGES_ROOT, | 67 generated_images_root=results.DEFAULT_GENERATED_IMAGES_ROOT, |
72 diff_base_url=None): | 68 diff_base_url=None): |
73 """ | 69 """ |
74 Args: | 70 Args: |
75 actuals_root: root directory containing all actual-results.json files | 71 actuals_root: root directory containing all actual-results.json files |
76 expected_root: root directory containing all expected-results.json files | 72 expected_root: root directory containing all expected-results.json files |
77 generated_images_root: directory within which to create all pixel diffs; | 73 generated_images_root: directory within which to create all pixel diffs; |
78 if this directory does not yet exist, it will be created | 74 if this directory does not yet exist, it will be created |
79 diff_base_url: base URL within which the client should look for diff | 75 diff_base_url: base URL within which the client should look for diff |
80 images; if not specified, defaults to a "file:///" URL representation | 76 images; if not specified, defaults to a "file:///" URL representation |
81 of generated_images_root | 77 of generated_images_root |
82 """ | 78 """ |
83 time_start = int(time.time()) | 79 time_start = int(time.time()) |
84 self._image_diff_db = imagediffdb.ImageDiffDB(generated_images_root) | 80 self._image_diff_db = imagediffdb.ImageDiffDB(generated_images_root) |
85 self._diff_base_url = ( | 81 self._diff_base_url = ( |
86 diff_base_url or | 82 diff_base_url or |
87 download_actuals.create_filepath_url(generated_images_root)) | 83 download_actuals.create_filepath_url(generated_images_root)) |
88 self._actuals_root = actuals_root | 84 self._actuals_root = actuals_root |
89 self._expected_root = expected_root | 85 self._expected_root = expected_root |
90 self._load_actual_and_expected() | 86 self._load_actual_and_expected() |
91 self._timestamp = int(time.time()) | 87 self._timestamp = int(time.time()) |
92 logging.info('Results complete; took %d seconds.' % | 88 logging.info('Results complete; took %d seconds.' % |
93 (self._timestamp - time_start)) | 89 (self._timestamp - time_start)) |
94 | 90 |
95 def get_timestamp(self): | |
96 """Return the time at which this object was created, in seconds past epoch | |
97 (UTC). | |
98 """ | |
99 return self._timestamp | |
100 | |
101 def edit_expectations(self, modifications): | 91 def edit_expectations(self, modifications): |
102 """Edit the expectations stored within this object and write them back | 92 """Edit the expectations stored within this object and write them back |
103 to disk. | 93 to disk. |
104 | 94 |
105 Note that this will NOT update the results stored in self._results[] ; | 95 Note that this will NOT update the results stored in self._results[] ; |
106 in order to see those updates, you must instantiate a new Results object | 96 in order to see those updates, you must instantiate a new |
107 based on the (now updated) files on disk. | 97 ExpectationComparisons object based on the (now updated) files on disk. |
108 | 98 |
109 Args: | 99 Args: |
110 modifications: a list of dictionaries, one for each expectation to update: | 100 modifications: a list of dictionaries, one for each expectation to update: |
111 | 101 |
112 [ | 102 [ |
113 { | 103 { |
114 imagepair.KEY__EXPECTATIONS_DATA: { | 104 imagepair.KEY__EXPECTATIONS_DATA: { |
115 results.KEY__EXPECTATIONS__BUGS: [123, 456], | 105 results.KEY__EXPECTATIONS__BUGS: [123, 456], |
116 results.KEY__EXPECTATIONS__IGNOREFAILURE: false, | 106 results.KEY__EXPECTATIONS__IGNOREFAILURE: false, |
117 results.KEY__EXPECTATIONS__REVIEWED: true, | 107 results.KEY__EXPECTATIONS__REVIEWED: true, |
118 }, | 108 }, |
119 imagepair.KEY__EXTRA_COLUMN_VALUES: { | 109 imagepair.KEY__EXTRA_COLUMN_VALUES: { |
120 results.KEY__EXTRACOLUMN__BUILDER: 'Test-Mac10.6-MacMini4.1-GeFor
ce320M-x86-Debug', | 110 results.KEY__EXTRACOLUMN__BUILDER: 'Test-Mac10.6-MacMini4.1-GeFor
ce320M-x86-Debug', |
121 results.KEY__EXTRACOLUMN__CONFIG: '8888', | 111 results.KEY__EXTRACOLUMN__CONFIG: '8888', |
122 results.KEY__EXTRACOLUMN__TEST: 'bigmatrix', | 112 results.KEY__EXTRACOLUMN__TEST: 'bigmatrix', |
123 }, | 113 }, |
124 results.KEY__NEW_IMAGE_URL: 'bitmap-64bitMD5/bigmatrix/108944080240
79689926.png', | 114 results.KEY__NEW_IMAGE_URL: 'bitmap-64bitMD5/bigmatrix/108944080240
79689926.png', |
125 }, | 115 }, |
126 ... | 116 ... |
127 ] | 117 ] |
128 | 118 |
129 """ | 119 """ |
130 expected_builder_dicts = Results._read_dicts_from_root(self._expected_root) | 120 expected_builder_dicts = ExpectationComparisons._read_dicts_from_root( |
| 121 self._expected_root) |
131 for mod in modifications: | 122 for mod in modifications: |
132 image_name = results.IMAGE_FILENAME_FORMATTER % ( | 123 image_name = results.IMAGE_FILENAME_FORMATTER % ( |
133 mod[imagepair.KEY__EXTRA_COLUMN_VALUES] | 124 mod[imagepair.KEY__EXTRA_COLUMN_VALUES] |
134 [results.KEY__EXTRACOLUMN__TEST], | 125 [results.KEY__EXTRACOLUMN__TEST], |
135 mod[imagepair.KEY__EXTRA_COLUMN_VALUES] | 126 mod[imagepair.KEY__EXTRA_COLUMN_VALUES] |
136 [results.KEY__EXTRACOLUMN__CONFIG]) | 127 [results.KEY__EXTRACOLUMN__CONFIG]) |
137 _, hash_type, hash_digest = gm_json.SplitGmRelativeUrl( | 128 _, hash_type, hash_digest = gm_json.SplitGmRelativeUrl( |
138 mod[results.KEY__NEW_IMAGE_URL]) | 129 mod[results.KEY__NEW_IMAGE_URL]) |
139 allowed_digests = [[hash_type, int(hash_digest)]] | 130 allowed_digests = [[hash_type, int(hash_digest)]] |
140 new_expectations = { | 131 new_expectations = { |
141 gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS: allowed_digests, | 132 gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS: allowed_digests, |
142 } | 133 } |
143 for field in EXPECTATION_FIELDS_PASSED_THRU_VERBATIM: | 134 for field in EXPECTATION_FIELDS_PASSED_THRU_VERBATIM: |
144 value = mod[imagepair.KEY__EXPECTATIONS_DATA].get(field) | 135 value = mod[imagepair.KEY__EXPECTATIONS_DATA].get(field) |
145 if value is not None: | 136 if value is not None: |
146 new_expectations[field] = value | 137 new_expectations[field] = value |
147 builder_dict = expected_builder_dicts[ | 138 builder_dict = expected_builder_dicts[ |
148 mod[imagepair.KEY__EXTRA_COLUMN_VALUES] | 139 mod[imagepair.KEY__EXTRA_COLUMN_VALUES] |
149 [results.KEY__EXTRACOLUMN__BUILDER]] | 140 [results.KEY__EXTRACOLUMN__BUILDER]] |
150 builder_expectations = builder_dict.get(gm_json.JSONKEY_EXPECTEDRESULTS) | 141 builder_expectations = builder_dict.get(gm_json.JSONKEY_EXPECTEDRESULTS) |
151 if not builder_expectations: | 142 if not builder_expectations: |
152 builder_expectations = {} | 143 builder_expectations = {} |
153 builder_dict[gm_json.JSONKEY_EXPECTEDRESULTS] = builder_expectations | 144 builder_dict[gm_json.JSONKEY_EXPECTEDRESULTS] = builder_expectations |
154 builder_expectations[image_name] = new_expectations | 145 builder_expectations[image_name] = new_expectations |
155 Results._write_dicts_to_root(expected_builder_dicts, self._expected_root) | 146 ExpectationComparisons._write_dicts_to_root( |
156 | 147 expected_builder_dicts, self._expected_root) |
157 def get_results_of_type(self, results_type): | |
158 """Return results of some/all tests (depending on 'results_type' parameter). | |
159 | |
160 Args: | |
161 results_type: string describing which types of results to include; must | |
162 be one of the RESULTS_* constants | |
163 | |
164 Results are returned in a dictionary as output by ImagePairSet.as_dict(). | |
165 """ | |
166 return self._results[results_type] | |
167 | |
168 def get_packaged_results_of_type(self, results_type, reload_seconds=None, | |
169 is_editable=False, is_exported=True): | |
170 """ Package the results of some/all tests as a complete response_dict. | |
171 | |
172 Args: | |
173 results_type: string indicating which set of results to return; | |
174 must be one of the RESULTS_* constants | |
175 reload_seconds: if specified, note that new results may be available once | |
176 these results are reload_seconds old | |
177 is_editable: whether clients are allowed to submit new baselines | |
178 is_exported: whether these results are being made available to other | |
179 network hosts | |
180 """ | |
181 response_dict = self._results[results_type] | |
182 time_updated = self.get_timestamp() | |
183 response_dict[results.KEY__HEADER] = { | |
184 results.KEY__HEADER__SCHEMA_VERSION: ( | |
185 results.REBASELINE_SERVER_SCHEMA_VERSION_NUMBER), | |
186 | |
187 # Timestamps: | |
188 # 1. when this data was last updated | |
189 # 2. when the caller should check back for new data (if ever) | |
190 results.KEY__HEADER__TIME_UPDATED: time_updated, | |
191 results.KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE: ( | |
192 (time_updated+reload_seconds) if reload_seconds else None), | |
193 | |
194 # The type we passed to get_results_of_type() | |
195 results.KEY__HEADER__TYPE: results_type, | |
196 | |
197 # Hash of dataset, which the client must return with any edits-- | |
198 # this ensures that the edits were made to a particular dataset. | |
199 results.KEY__HEADER__DATAHASH: str(hash(repr( | |
200 response_dict[imagepairset.KEY__IMAGEPAIRS]))), | |
201 | |
202 # Whether the server will accept edits back. | |
203 results.KEY__HEADER__IS_EDITABLE: is_editable, | |
204 | |
205 # Whether the service is accessible from other hosts. | |
206 results.KEY__HEADER__IS_EXPORTED: is_exported, | |
207 } | |
208 return response_dict | |
209 | |
210 @staticmethod | |
211 def _ignore_builder(builder): | |
212 """Returns True if we should ignore expectations and actuals for a builder. | |
213 | |
214 This allows us to ignore builders for which we don't maintain expectations | |
215 (trybots, Valgrind, ASAN, TSAN), and avoid problems like | |
216 https://code.google.com/p/skia/issues/detail?id=2036 ('rebaseline_server | |
217 produces error when trying to add baselines for ASAN/TSAN builders') | |
218 | |
219 Args: | |
220 builder: name of this builder, as a string | |
221 | |
222 Returns: | |
223 True if we should ignore expectations and actuals for this builder. | |
224 """ | |
225 return (builder.endswith('-Trybot') or | |
226 ('Valgrind' in builder) or | |
227 ('TSAN' in builder) or | |
228 ('ASAN' in builder)) | |
229 | |
230 @staticmethod | |
231 def _read_dicts_from_root(root, pattern='*.json'): | |
232 """Read all JSON dictionaries within a directory tree. | |
233 | |
234 Args: | |
235 root: path to root of directory tree | |
236 pattern: which files to read within root (fnmatch-style pattern) | |
237 | |
238 Returns: | |
239 A meta-dictionary containing all the JSON dictionaries found within | |
240 the directory tree, keyed by the builder name of each dictionary. | |
241 | |
242 Raises: | |
243 IOError if root does not refer to an existing directory | |
244 """ | |
245 if not os.path.isdir(root): | |
246 raise IOError('no directory found at path %s' % root) | |
247 meta_dict = {} | |
248 for dirpath, dirnames, filenames in os.walk(root): | |
249 for matching_filename in fnmatch.filter(filenames, pattern): | |
250 builder = os.path.basename(dirpath) | |
251 if Results._ignore_builder(builder): | |
252 continue | |
253 fullpath = os.path.join(dirpath, matching_filename) | |
254 meta_dict[builder] = gm_json.LoadFromFile(fullpath) | |
255 return meta_dict | |
256 | |
257 @staticmethod | |
258 def _create_relative_url(hashtype_and_digest, test_name): | |
259 """Returns the URL for this image, relative to GM_ACTUALS_ROOT_HTTP_URL. | |
260 | |
261 If we don't have a record of this image, returns None. | |
262 | |
263 Args: | |
264 hashtype_and_digest: (hash_type, hash_digest) tuple, or None if we | |
265 don't have a record of this image | |
266 test_name: string; name of the GM test that created this image | |
267 """ | |
268 if not hashtype_and_digest: | |
269 return None | |
270 return gm_json.CreateGmRelativeUrl( | |
271 test_name=test_name, | |
272 hash_type=hashtype_and_digest[0], | |
273 hash_digest=hashtype_and_digest[1]) | |
274 | 148 |
275 @staticmethod | 149 @staticmethod |
276 def _write_dicts_to_root(meta_dict, root, pattern='*.json'): | 150 def _write_dicts_to_root(meta_dict, root, pattern='*.json'): |
277 """Write all per-builder dictionaries within meta_dict to files under | 151 """Write all per-builder dictionaries within meta_dict to files under |
278 the root path. | 152 the root path. |
279 | 153 |
280 Security note: this will only write to files that already exist within | 154 Security note: this will only write to files that already exist within |
281 the root path (as found by os.walk() within root), so we don't need to | 155 the root path (as found by os.walk() within root), so we don't need to |
282 worry about malformed content writing to disk outside of root. | 156 worry about malformed content writing to disk outside of root. |
283 However, the data written to those files is not double-checked, so it | 157 However, the data written to those files is not double-checked, so it |
284 could contain poisonous data. | 158 could contain poisonous data. |
285 | 159 |
286 Args: | 160 Args: |
287 meta_dict: a builder-keyed meta-dictionary containing all the JSON | 161 meta_dict: a builder-keyed meta-dictionary containing all the JSON |
288 dictionaries we want to write out | 162 dictionaries we want to write out |
289 root: path to root of directory tree within which to write files | 163 root: path to root of directory tree within which to write files |
290 pattern: which files to write within root (fnmatch-style pattern) | 164 pattern: which files to write within root (fnmatch-style pattern) |
291 | 165 |
292 Raises: | 166 Raises: |
293 IOError if root does not refer to an existing directory | 167 IOError if root does not refer to an existing directory |
294 KeyError if the set of per-builder dictionaries written out was | 168 KeyError if the set of per-builder dictionaries written out was |
295 different than expected | 169 different than expected |
296 """ | 170 """ |
297 if not os.path.isdir(root): | 171 if not os.path.isdir(root): |
298 raise IOError('no directory found at path %s' % root) | 172 raise IOError('no directory found at path %s' % root) |
299 actual_builders_written = [] | 173 actual_builders_written = [] |
300 for dirpath, dirnames, filenames in os.walk(root): | 174 for dirpath, dirnames, filenames in os.walk(root): |
301 for matching_filename in fnmatch.filter(filenames, pattern): | 175 for matching_filename in fnmatch.filter(filenames, pattern): |
302 builder = os.path.basename(dirpath) | 176 builder = os.path.basename(dirpath) |
303 if Results._ignore_builder(builder): | 177 if ExpectationComparisons._ignore_builder(builder): |
304 continue | 178 continue |
305 per_builder_dict = meta_dict.get(builder) | 179 per_builder_dict = meta_dict.get(builder) |
306 if per_builder_dict is not None: | 180 if per_builder_dict is not None: |
307 fullpath = os.path.join(dirpath, matching_filename) | 181 fullpath = os.path.join(dirpath, matching_filename) |
308 gm_json.WriteToFile(per_builder_dict, fullpath) | 182 gm_json.WriteToFile(per_builder_dict, fullpath) |
309 actual_builders_written.append(builder) | 183 actual_builders_written.append(builder) |
310 | 184 |
311 # Check: did we write out the set of per-builder dictionaries we | 185 # Check: did we write out the set of per-builder dictionaries we |
312 # expected to? | 186 # expected to? |
313 expected_builders_written = sorted(meta_dict.keys()) | 187 expected_builders_written = sorted(meta_dict.keys()) |
314 actual_builders_written.sort() | 188 actual_builders_written.sort() |
315 if expected_builders_written != actual_builders_written: | 189 if expected_builders_written != actual_builders_written: |
316 raise KeyError( | 190 raise KeyError( |
317 'expected to write dicts for builders %s, but actually wrote them ' | 191 'expected to write dicts for builders %s, but actually wrote them ' |
318 'for builders %s' % ( | 192 'for builders %s' % ( |
319 expected_builders_written, actual_builders_written)) | 193 expected_builders_written, actual_builders_written)) |
320 | 194 |
321 def _load_actual_and_expected(self): | 195 def _load_actual_and_expected(self): |
322 """Loads the results of all tests, across all builders (based on the | 196 """Loads the results of all tests, across all builders (based on the |
323 files within self._actuals_root and self._expected_root), | 197 files within self._actuals_root and self._expected_root), |
324 and stores them in self._results. | 198 and stores them in self._results. |
325 """ | 199 """ |
326 logging.info('Reading actual-results JSON files from %s...' % | 200 logging.info('Reading actual-results JSON files from %s...' % |
327 self._actuals_root) | 201 self._actuals_root) |
328 actual_builder_dicts = Results._read_dicts_from_root(self._actuals_root) | 202 actual_builder_dicts = ExpectationComparisons._read_dicts_from_root( |
| 203 self._actuals_root) |
329 logging.info('Reading expected-results JSON files from %s...' % | 204 logging.info('Reading expected-results JSON files from %s...' % |
330 self._expected_root) | 205 self._expected_root) |
331 expected_builder_dicts = Results._read_dicts_from_root(self._expected_root) | 206 expected_builder_dicts = ExpectationComparisons._read_dicts_from_root( |
| 207 self._expected_root) |
332 | 208 |
333 all_image_pairs = imagepairset.ImagePairSet( | 209 all_image_pairs = imagepairset.ImagePairSet( |
334 descriptions=IMAGEPAIR_SET_DESCRIPTIONS, | 210 descriptions=IMAGEPAIR_SET_DESCRIPTIONS, |
335 diff_base_url=self._diff_base_url) | 211 diff_base_url=self._diff_base_url) |
336 failing_image_pairs = imagepairset.ImagePairSet( | 212 failing_image_pairs = imagepairset.ImagePairSet( |
337 descriptions=IMAGEPAIR_SET_DESCRIPTIONS, | 213 descriptions=IMAGEPAIR_SET_DESCRIPTIONS, |
338 diff_base_url=self._diff_base_url) | 214 diff_base_url=self._diff_base_url) |
339 | 215 |
340 all_image_pairs.ensure_extra_column_values_in_summary( | 216 all_image_pairs.ensure_extra_column_values_in_summary( |
341 column_id=results.KEY__EXTRACOLUMN__RESULT_TYPE, values=[ | 217 column_id=results.KEY__EXTRACOLUMN__RESULT_TYPE, values=[ |
(...skipping 17 matching lines...) Expand all Loading... |
359 logging.info('Generating pixel diffs for builder #%d of %d, "%s"...' % | 235 logging.info('Generating pixel diffs for builder #%d of %d, "%s"...' % |
360 (builder_num, num_builders, builder)) | 236 (builder_num, num_builders, builder)) |
361 actual_results_for_this_builder = ( | 237 actual_results_for_this_builder = ( |
362 actual_builder_dicts[builder][gm_json.JSONKEY_ACTUALRESULTS]) | 238 actual_builder_dicts[builder][gm_json.JSONKEY_ACTUALRESULTS]) |
363 for result_type in sorted(actual_results_for_this_builder.keys()): | 239 for result_type in sorted(actual_results_for_this_builder.keys()): |
364 results_of_this_type = actual_results_for_this_builder[result_type] | 240 results_of_this_type = actual_results_for_this_builder[result_type] |
365 if not results_of_this_type: | 241 if not results_of_this_type: |
366 continue | 242 continue |
367 for image_name in sorted(results_of_this_type.keys()): | 243 for image_name in sorted(results_of_this_type.keys()): |
368 (test, config) = results.IMAGE_FILENAME_RE.match(image_name).groups() | 244 (test, config) = results.IMAGE_FILENAME_RE.match(image_name).groups() |
369 actual_image_relative_url = Results._create_relative_url( | 245 actual_image_relative_url = ( |
370 hashtype_and_digest=results_of_this_type[image_name], | 246 ExpectationComparisons._create_relative_url( |
371 test_name=test) | 247 hashtype_and_digest=results_of_this_type[image_name], |
| 248 test_name=test)) |
372 | 249 |
373 # Default empty expectations; overwrite these if we find any real ones | 250 # Default empty expectations; overwrite these if we find any real ones |
374 expectations_per_test = None | 251 expectations_per_test = None |
375 expected_image_relative_url = None | 252 expected_image_relative_url = None |
376 expectations_dict = None | 253 expectations_dict = None |
377 try: | 254 try: |
378 expectations_per_test = ( | 255 expectations_per_test = ( |
379 expected_builder_dicts | 256 expected_builder_dicts |
380 [builder][gm_json.JSONKEY_EXPECTEDRESULTS][image_name]) | 257 [builder][gm_json.JSONKEY_EXPECTEDRESULTS][image_name]) |
381 # TODO(epoger): assumes a single allowed digest per test, which is | 258 # TODO(epoger): assumes a single allowed digest per test, which is |
382 # fine; see https://code.google.com/p/skia/issues/detail?id=1787 | 259 # fine; see https://code.google.com/p/skia/issues/detail?id=1787 |
383 expected_image_hashtype_and_digest = ( | 260 expected_image_hashtype_and_digest = ( |
384 expectations_per_test | 261 expectations_per_test |
385 [gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS][0]) | 262 [gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS][0]) |
386 expected_image_relative_url = Results._create_relative_url( | 263 expected_image_relative_url = ( |
387 hashtype_and_digest=expected_image_hashtype_and_digest, | 264 ExpectationComparisons._create_relative_url( |
388 test_name=test) | 265 hashtype_and_digest=expected_image_hashtype_and_digest, |
| 266 test_name=test)) |
389 expectations_dict = {} | 267 expectations_dict = {} |
390 for field in EXPECTATION_FIELDS_PASSED_THRU_VERBATIM: | 268 for field in EXPECTATION_FIELDS_PASSED_THRU_VERBATIM: |
391 expectations_dict[field] = expectations_per_test.get(field) | 269 expectations_dict[field] = expectations_per_test.get(field) |
392 except (KeyError, TypeError): | 270 except (KeyError, TypeError): |
393 # There are several cases in which we would expect to find | 271 # There are several cases in which we would expect to find |
394 # no expectations for a given test: | 272 # no expectations for a given test: |
395 # | 273 # |
396 # 1. result_type == NOCOMPARISON | 274 # 1. result_type == NOCOMPARISON |
397 # There are no expectations for this test yet! | 275 # There are no expectations for this test yet! |
398 # | 276 # |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
458 results.KEY__HEADER__RESULTS_FAILURES: failing_image_pairs.as_dict(), | 336 results.KEY__HEADER__RESULTS_FAILURES: failing_image_pairs.as_dict(), |
459 } | 337 } |
460 | 338 |
461 | 339 |
462 def main(): | 340 def main(): |
463 logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', | 341 logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', |
464 datefmt='%m/%d/%Y %H:%M:%S', | 342 datefmt='%m/%d/%Y %H:%M:%S', |
465 level=logging.INFO) | 343 level=logging.INFO) |
466 parser = argparse.ArgumentParser() | 344 parser = argparse.ArgumentParser() |
467 parser.add_argument( | 345 parser.add_argument( |
468 '--actuals', default=DEFAULT_ACTUALS_DIR, | 346 '--actuals', default=results.DEFAULT_ACTUALS_DIR, |
469 help='Directory containing all actual-result JSON files; defaults to ' | 347 help='Directory containing all actual-result JSON files; defaults to ' |
470 '\'%(default)s\' .') | 348 '\'%(default)s\' .') |
471 parser.add_argument( | 349 parser.add_argument( |
472 '--expectations', default=DEFAULT_EXPECTATIONS_DIR, | 350 '--expectations', default=DEFAULT_EXPECTATIONS_DIR, |
473 help='Directory containing all expected-result JSON files; defaults to ' | 351 help='Directory containing all expected-result JSON files; defaults to ' |
474 '\'%(default)s\' .') | 352 '\'%(default)s\' .') |
475 parser.add_argument( | 353 parser.add_argument( |
476 '--outfile', required=True, | 354 '--outfile', required=True, |
477 help='File to write result summary into, in JSON format.') | 355 help='File to write result summary into, in JSON format.') |
478 parser.add_argument( | 356 parser.add_argument( |
479 '--results', default=results.KEY__HEADER__RESULTS_FAILURES, | 357 '--results', default=results.KEY__HEADER__RESULTS_FAILURES, |
480 help='Which result types to include. Defaults to \'%(default)s\'; ' | 358 help='Which result types to include. Defaults to \'%(default)s\'; ' |
481 'must be one of ' + | 359 'must be one of ' + |
482 str([results.KEY__HEADER__RESULTS_FAILURES, | 360 str([results.KEY__HEADER__RESULTS_FAILURES, |
483 results.KEY__HEADER__RESULTS_ALL])) | 361 results.KEY__HEADER__RESULTS_ALL])) |
484 parser.add_argument( | 362 parser.add_argument( |
485 '--workdir', default=DEFAULT_GENERATED_IMAGES_ROOT, | 363 '--workdir', default=results.DEFAULT_GENERATED_IMAGES_ROOT, |
486 help='Directory within which to download images and generate diffs; ' | 364 help='Directory within which to download images and generate diffs; ' |
487 'defaults to \'%(default)s\' .') | 365 'defaults to \'%(default)s\' .') |
488 args = parser.parse_args() | 366 args = parser.parse_args() |
489 results_obj = Results(actuals_root=args.actuals, | 367 results_obj = ExpectationComparisons(actuals_root=args.actuals, |
490 expected_root=args.expectations, | 368 expected_root=args.expectations, |
491 generated_images_root=args.workdir) | 369 generated_images_root=args.workdir) |
492 gm_json.WriteToFile( | 370 gm_json.WriteToFile( |
493 results_obj.get_packaged_results_of_type(results_type=args.results), | 371 results_obj.get_packaged_results_of_type(results_type=args.results), |
494 args.outfile) | 372 args.outfile) |
495 | 373 |
496 | 374 |
497 if __name__ == '__main__': | 375 if __name__ == '__main__': |
498 main() | 376 main() |
OLD | NEW |