OLD | NEW |
---|---|
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 | 2 |
3 ''' | 3 ''' |
4 Copyright 2012 Google Inc. | 4 Copyright 2012 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 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
116 print >> sys.stderr, ('Encountered %d failures (see above).' % | 116 print >> sys.stderr, ('Encountered %d failures (see above).' % |
117 len(self._failures_encountered)) | 117 len(self._failures_encountered)) |
118 self._Exit() | 118 self._Exit() |
119 | 119 |
120 | 120 |
121 # Object that rebaselines a JSON expectations file (not individual image files). | 121 # Object that rebaselines a JSON expectations file (not individual image files). |
122 class JsonRebaseliner(object): | 122 class JsonRebaseliner(object): |
123 | 123 |
124 # params: | 124 # params: |
125 # expectations_root: root directory of all expectations JSON files | 125 # expectations_root: root directory of all expectations JSON files |
126 # expectations_filename: filename (under expectations_root) of JSON | 126 # expectations_input_filename: filename (under expectations_root) of JSON |
127 # expectations file; typically | 127 # expectations file to read; typically |
128 # "expected-results.json" | 128 # "expected-results.json" |
129 # expectations_output_filename: filename (under expectations_root) to | |
130 # which updated expectations should be | |
131 # written; typically the same as | |
132 # expectations_input_filename, to overwrite | |
133 # the old content | |
129 # actuals_base_url: base URL from which to read actual-result JSON files | 134 # actuals_base_url: base URL from which to read actual-result JSON files |
130 # actuals_filename: filename (under actuals_base_url) from which to read a | 135 # actuals_filename: filename (under actuals_base_url) from which to read a |
131 # summary of results; typically "actual-results.json" | 136 # summary of results; typically "actual-results.json" |
132 # exception_handler: reference to rebaseline.ExceptionHandler object | 137 # exception_handler: reference to rebaseline.ExceptionHandler object |
133 # tests: list of tests to rebaseline, or None if we should rebaseline | 138 # tests: list of tests to rebaseline, or None if we should rebaseline |
134 # whatever files the JSON results summary file tells us to | 139 # whatever files the JSON results summary file tells us to |
135 # configs: which configs to run for each test, or None if we should | 140 # configs: which configs to run for each test, or None if we should |
136 # rebaseline whatever configs the JSON results summary file tells | 141 # rebaseline whatever configs the JSON results summary file tells |
137 # us to | 142 # us to |
138 # add_new: if True, add expectations for tests which don't have any yet | 143 # add_new: if True, add expectations for tests which don't have any yet |
139 def __init__(self, expectations_root, expectations_filename, | 144 def __init__(self, expectations_root, expectations_input_filename, |
140 actuals_base_url, actuals_filename, exception_handler, | 145 expectations_output_filename, actuals_base_url, |
146 actuals_filename, exception_handler, | |
141 tests=None, configs=None, add_new=False): | 147 tests=None, configs=None, add_new=False): |
142 self._expectations_root = expectations_root | 148 self._expectations_root = expectations_root |
143 self._expectations_filename = expectations_filename | 149 self._expectations_input_filename = expectations_input_filename |
150 self._expectations_output_filename = expectations_output_filename | |
144 self._tests = tests | 151 self._tests = tests |
145 self._configs = configs | 152 self._configs = configs |
146 self._actuals_base_url = actuals_base_url | 153 self._actuals_base_url = actuals_base_url |
147 self._actuals_filename = actuals_filename | 154 self._actuals_filename = actuals_filename |
148 self._exception_handler = exception_handler | 155 self._exception_handler = exception_handler |
149 self._add_new = add_new | 156 self._add_new = add_new |
150 self._image_filename_re = re.compile(gm_json.IMAGE_FILENAME_PATTERN) | 157 self._image_filename_re = re.compile(gm_json.IMAGE_FILENAME_PATTERN) |
151 self._using_svn = os.path.isdir(os.path.join(expectations_root, '.svn')) | 158 self._using_svn = os.path.isdir(os.path.join(expectations_root, '.svn')) |
152 | 159 |
153 # Executes subprocess.call(cmd). | 160 # Executes subprocess.call(cmd). |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
226 # succeeding, but including the SUCCEEDED results will allow us to | 233 # succeeding, but including the SUCCEEDED results will allow us to |
227 # re-record expectations if they somehow get out of sync. | 234 # re-record expectations if they somehow get out of sync. |
228 sections = [gm_json.JSONKEY_ACTUALRESULTS_FAILED, | 235 sections = [gm_json.JSONKEY_ACTUALRESULTS_FAILED, |
229 gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED] | 236 gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED] |
230 if self._add_new: | 237 if self._add_new: |
231 sections.append(gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON) | 238 sections.append(gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON) |
232 results_to_update = self._GetActualResults(json_url=actuals_url, | 239 results_to_update = self._GetActualResults(json_url=actuals_url, |
233 sections=sections) | 240 sections=sections) |
234 | 241 |
235 # Read in current expectations. | 242 # Read in current expectations. |
236 expectations_json_filepath = os.path.join( | 243 expectations_input_filepath = os.path.join( |
237 self._expectations_root, subdir, self._expectations_filename) | 244 self._expectations_root, subdir, self._expectations_input_filename) |
238 expectations_dict = gm_json.LoadFromFile(expectations_json_filepath) | 245 expectations_dict = gm_json.LoadFromFile(expectations_input_filepath) |
239 expected_results = expectations_dict[gm_json.JSONKEY_EXPECTEDRESULTS] | 246 expected_results = expectations_dict[gm_json.JSONKEY_EXPECTEDRESULTS] |
240 | 247 |
241 # Update the expectations in memory, skipping any tests/configs that | 248 # Update the expectations in memory, skipping any tests/configs that |
242 # the caller asked to exclude. | 249 # the caller asked to exclude. |
243 skipped_images = [] | 250 skipped_images = [] |
244 if results_to_update: | 251 if results_to_update: |
245 for (image_name, image_results) in results_to_update.iteritems(): | 252 for (image_name, image_results) in results_to_update.iteritems(): |
246 (test, config) = \ | 253 (test, config) = \ |
247 self._image_filename_re.match(image_name).groups() | 254 self._image_filename_re.match(image_name).groups() |
248 if self._tests: | 255 if self._tests: |
249 if test not in self._tests: | 256 if test not in self._tests: |
250 skipped_images.append(image_name) | 257 skipped_images.append(image_name) |
251 continue | 258 continue |
252 if self._configs: | 259 if self._configs: |
253 if config not in self._configs: | 260 if config not in self._configs: |
254 skipped_images.append(image_name) | 261 skipped_images.append(image_name) |
255 continue | 262 continue |
256 if not expected_results.get(image_name): | 263 if not expected_results.get(image_name): |
257 expected_results[image_name] = {} | 264 expected_results[image_name] = {} |
258 expected_results[image_name] \ | 265 expected_results[image_name] \ |
259 [gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS] = \ | 266 [gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS] = \ |
260 [image_results] | 267 [image_results] |
261 | 268 |
262 # Write out updated expectations. | 269 # Write out updated expectations. |
263 gm_json.WriteToFile(expectations_dict, expectations_json_filepath) | 270 expectations_output_filepath = os.path.join( |
271 self._expectations_root, subdir, self._expectations_output_filename) | |
272 gm_json.WriteToFile(expectations_dict, expectations_output_filepath) | |
264 | 273 |
265 # Mark the JSON file as plaintext, so text-style diffs can be applied. | 274 # Mark the JSON file as plaintext, so text-style diffs can be applied. |
266 # Fixes https://code.google.com/p/skia/issues/detail?id=1442 | 275 # Fixes https://code.google.com/p/skia/issues/detail?id=1442 |
267 if self._using_svn: | 276 if self._using_svn: |
268 self._Call(['svn', 'propset', '--quiet', 'svn:mime-type', | 277 self._Call(['svn', 'propset', '--quiet', 'svn:mime-type', |
269 'text/x-json', expectations_json_filepath]) | 278 'text/x-json', expectations_output_filepath]) |
270 | 279 |
271 # main... | 280 # main... |
272 | 281 |
273 parser = argparse.ArgumentParser() | 282 parser = argparse.ArgumentParser() |
274 parser.add_argument('--actuals-base-url', | 283 parser.add_argument('--actuals-base-url', |
275 help='base URL from which to read files containing JSON ' + | 284 help='base URL from which to read files containing JSON ' + |
276 'summaries of actual GM results; defaults to %(default)s', | 285 'summaries of actual GM results; defaults to %(default)s', |
277 default='http://skia-autogen.googlecode.com/svn/gm-actual') | 286 default='http://skia-autogen.googlecode.com/svn/gm-actual') |
278 parser.add_argument('--actuals-filename', | 287 parser.add_argument('--actuals-filename', |
279 help='filename (within platform-specific subdirectories ' + | 288 help='filename (within platform-specific subdirectories ' + |
(...skipping 14 matching lines...) Expand all Loading... | |
294 '*all* configs that are available.') | 303 '*all* configs that are available.') |
295 # TODO(epoger): The --dry-run argument will no longer be needed once we | 304 # TODO(epoger): The --dry-run argument will no longer be needed once we |
296 # are only rebaselining JSON files. | 305 # are only rebaselining JSON files. |
297 parser.add_argument('--dry-run', action='store_true', | 306 parser.add_argument('--dry-run', action='store_true', |
298 help='instead of actually downloading files or adding ' + | 307 help='instead of actually downloading files or adding ' + |
299 'files to checkout, display a list of operations that ' + | 308 'files to checkout, display a list of operations that ' + |
300 'we would normally perform') | 309 'we would normally perform') |
301 parser.add_argument('--expectations-filename', | 310 parser.add_argument('--expectations-filename', |
302 help='filename (under EXPECTATIONS_ROOT) to read ' + | 311 help='filename (under EXPECTATIONS_ROOT) to read ' + |
303 'current expectations from, and to write new ' + | 312 'current expectations from, and to write new ' + |
304 'expectations into; defaults to %(default)s', | 313 'expectations into (unless a separate ' + |
314 'EXPECTATIONS_FILENAME_OUTPUT has been specified); ' + | |
315 'defaults to %(default)s', | |
305 default='expected-results.json') | 316 default='expected-results.json') |
317 parser.add_argument('--expectations-filename-output', | |
epoger
2013/07/24 19:31:34
Here's what this section of the --help output look
| |
318 help='filename (under EXPECTATIONS_ROOT) to write ' + | |
319 'updated expectations into; by default, overwrites the ' + | |
320 'input file (EXPECTATIONS_FILENAME)', | |
321 default='') | |
306 parser.add_argument('--expectations-root', | 322 parser.add_argument('--expectations-root', |
307 help='root of expectations directory to update-- should ' + | 323 help='root of expectations directory to update-- should ' + |
308 'contain one or more base-* subdirectories. Defaults to ' + | 324 'contain one or more base-* subdirectories. Defaults to ' + |
309 '%(default)s', | 325 '%(default)s', |
310 default=os.path.join('expectations', 'gm')) | 326 default=os.path.join('expectations', 'gm')) |
311 parser.add_argument('--keep-going-on-failure', action='store_true', | 327 parser.add_argument('--keep-going-on-failure', action='store_true', |
312 help='instead of halting at the first error encountered, ' + | 328 help='instead of halting at the first error encountered, ' + |
313 'keep going and rebaseline as many tests as possible, ' + | 329 'keep going and rebaseline as many tests as possible, ' + |
314 'and then report the full set of errors at the end') | 330 'and then report the full set of errors at the end') |
315 parser.add_argument('--subdirs', metavar='SUBDIR', nargs='+', | 331 parser.add_argument('--subdirs', metavar='SUBDIR', nargs='+', |
(...skipping 27 matching lines...) Expand all Loading... | |
343 # individual image files. Different expectations/gm subdirectories may move | 359 # individual image files. Different expectations/gm subdirectories may move |
344 # from individual image files to JSON-format expectations at different | 360 # from individual image files to JSON-format expectations at different |
345 # times, so we need to make this determination per subdirectory. | 361 # times, so we need to make this determination per subdirectory. |
346 # | 362 # |
347 # See https://goto.google.com/ChecksumTransitionDetail | 363 # See https://goto.google.com/ChecksumTransitionDetail |
348 expectations_json_file = os.path.join(args.expectations_root, subdir, | 364 expectations_json_file = os.path.join(args.expectations_root, subdir, |
349 args.expectations_filename) | 365 args.expectations_filename) |
350 if os.path.isfile(expectations_json_file): | 366 if os.path.isfile(expectations_json_file): |
351 rebaseliner = JsonRebaseliner( | 367 rebaseliner = JsonRebaseliner( |
352 expectations_root=args.expectations_root, | 368 expectations_root=args.expectations_root, |
353 expectations_filename=args.expectations_filename, | 369 expectations_input_filename=args.expectations_filename, |
370 expectations_output_filename=(args.expectations_filename_output or | |
371 args.expectations_filename), | |
354 tests=args.tests, configs=args.configs, | 372 tests=args.tests, configs=args.configs, |
355 actuals_base_url=args.actuals_base_url, | 373 actuals_base_url=args.actuals_base_url, |
356 actuals_filename=args.actuals_filename, | 374 actuals_filename=args.actuals_filename, |
357 exception_handler=exception_handler, | 375 exception_handler=exception_handler, |
358 add_new=args.add_new) | 376 add_new=args.add_new) |
359 else: | 377 else: |
360 # TODO(epoger): When we get rid of the ImageRebaseliner implementation, | 378 # TODO(epoger): When we get rid of the ImageRebaseliner implementation, |
361 # we should raise an Exception in this case (no JSON expectations file | 379 # we should raise an Exception in this case (no JSON expectations file |
362 # found to update), to prevent a recurrence of | 380 # found to update), to prevent a recurrence of |
363 # https://code.google.com/p/skia/issues/detail?id=1403 ('rebaseline.py | 381 # https://code.google.com/p/skia/issues/detail?id=1403 ('rebaseline.py |
364 # script fails with misleading output when run outside of gm-expected | 382 # script fails with misleading output when run outside of gm-expected |
365 # dir') | 383 # dir') |
366 rebaseliner = rebaseline_imagefiles.ImageRebaseliner( | 384 rebaseliner = rebaseline_imagefiles.ImageRebaseliner( |
367 expectations_root=args.expectations_root, | 385 expectations_root=args.expectations_root, |
368 tests=args.tests, configs=args.configs, | 386 tests=args.tests, configs=args.configs, |
369 dry_run=args.dry_run, | 387 dry_run=args.dry_run, |
370 json_base_url=args.actuals_base_url, | 388 json_base_url=args.actuals_base_url, |
371 json_filename=args.actuals_filename, | 389 json_filename=args.actuals_filename, |
372 exception_handler=exception_handler, | 390 exception_handler=exception_handler, |
373 add_new=args.add_new, | 391 add_new=args.add_new, |
374 missing_json_is_fatal=missing_json_is_fatal) | 392 missing_json_is_fatal=missing_json_is_fatal) |
375 | 393 |
376 try: | 394 try: |
377 rebaseliner.RebaselineSubdir(subdir=subdir, builder=builder) | 395 rebaseliner.RebaselineSubdir(subdir=subdir, builder=builder) |
378 except BaseException as e: | 396 except BaseException as e: |
379 exception_handler.RaiseExceptionOrContinue(e) | 397 exception_handler.RaiseExceptionOrContinue(e) |
380 | 398 |
381 exception_handler.ReportAllFailures() | 399 exception_handler.ReportAllFailures() |
OLD | NEW |