| 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 """ |
| 11 | 11 |
| 12 # System-level imports | 12 # System-level imports |
| 13 import argparse | 13 import argparse |
| 14 import fnmatch | 14 import fnmatch |
| 15 import json | |
| 16 import logging | 15 import logging |
| 17 import os | 16 import os |
| 18 import re | |
| 19 import sys | |
| 20 import time | 17 import time |
| 21 | 18 |
| 19 # Must fix up PYTHONPATH before importing from within Skia |
| 20 # pylint: disable=W0611 |
| 21 import fix_pythonpath |
| 22 # pylint: enable=W0611 |
| 23 |
| 22 # Imports from within Skia | 24 # Imports from within Skia |
| 23 import fix_pythonpath # must do this first | |
| 24 from pyutils import url_utils | 25 from pyutils import url_utils |
| 25 import gm_json | 26 import gm_json |
| 26 import imagediffdb | 27 import imagediffdb |
| 27 import imagepair | 28 import imagepair |
| 28 import imagepairset | 29 import imagepairset |
| 29 import results | 30 import results |
| 30 | 31 |
| 31 EXPECTATION_FIELDS_PASSED_THRU_VERBATIM = [ | 32 EXPECTATION_FIELDS_PASSED_THRU_VERBATIM = [ |
| 32 results.KEY__EXPECTATIONS__BUGS, | 33 results.KEY__EXPECTATIONS__BUGS, |
| 33 results.KEY__EXPECTATIONS__IGNOREFAILURE, | 34 results.KEY__EXPECTATIONS__IGNOREFAILURE, |
| 34 results.KEY__EXPECTATIONS__REVIEWED, | 35 results.KEY__EXPECTATIONS__REVIEWED, |
| 35 ] | 36 ] |
| 36 TRUNK_DIRECTORY = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) | 37 TRUNK_DIRECTORY = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) |
| 37 DEFAULT_EXPECTATIONS_DIR = os.path.join(TRUNK_DIRECTORY, 'expectations', 'gm') | 38 DEFAULT_EXPECTATIONS_DIR = os.path.join(TRUNK_DIRECTORY, 'expectations', 'gm') |
| 38 DEFAULT_IGNORE_FAILURES_FILE = 'ignored-tests.txt' | 39 DEFAULT_IGNORE_FAILURES_FILE = 'ignored-tests.txt' |
| 39 | 40 |
| 40 IMAGEPAIR_SET_DESCRIPTIONS = ('expected image', 'actual image') | 41 IMAGEPAIR_SET_DESCRIPTIONS = ('expected image', 'actual image') |
| 41 | 42 |
| 42 | 43 |
| 43 class ExpectationComparisons(results.BaseComparisons): | 44 class ExpectationComparisons(results.BaseComparisons): |
| 44 """Loads actual and expected GM results into an ImagePairSet. | 45 """Loads actual and expected GM results into an ImagePairSet. |
| 45 | 46 |
| 46 Loads actual and expected results from all builders, except for those skipped | 47 Loads actual and expected results from all builders, except for those skipped |
| 47 by _ignore_builder(). | 48 by _ignore_builder(). |
| 48 | 49 |
| 49 Once this object has been constructed, the results (in self._results[]) | 50 Once this object has been constructed, the results (in self._results[]) |
| 50 are immutable. If you want to update the results based on updated JSON | 51 are immutable. If you want to update the results based on updated JSON |
| 51 file contents, you will need to create a new ExpectationComparisons object.""" | 52 file contents, you will need to create a new ExpectationComparisons object.""" |
| 52 | 53 |
| 53 def __init__(self, actuals_root=results.DEFAULT_ACTUALS_DIR, | 54 def __init__(self, image_diff_db, actuals_root=results.DEFAULT_ACTUALS_DIR, |
| 54 expected_root=DEFAULT_EXPECTATIONS_DIR, | 55 expected_root=DEFAULT_EXPECTATIONS_DIR, |
| 55 ignore_failures_file=DEFAULT_IGNORE_FAILURES_FILE, | 56 ignore_failures_file=DEFAULT_IGNORE_FAILURES_FILE, |
| 56 generated_images_root=results.DEFAULT_GENERATED_IMAGES_ROOT, | |
| 57 diff_base_url=None, builder_regex_list=None): | 57 diff_base_url=None, builder_regex_list=None): |
| 58 """ | 58 """ |
| 59 Args: | 59 Args: |
| 60 image_diff_db: instance of ImageDiffDB we use to cache the image diffs |
| 60 actuals_root: root directory containing all actual-results.json files | 61 actuals_root: root directory containing all actual-results.json files |
| 61 expected_root: root directory containing all expected-results.json files | 62 expected_root: root directory containing all expected-results.json files |
| 62 ignore_failures_file: if a file with this name is found within | 63 ignore_failures_file: if a file with this name is found within |
| 63 expected_root, ignore failures for any tests listed in the file | 64 expected_root, ignore failures for any tests listed in the file |
| 64 generated_images_root: directory within which to create all pixel diffs; | |
| 65 if this directory does not yet exist, it will be created | |
| 66 diff_base_url: base URL within which the client should look for diff | 65 diff_base_url: base URL within which the client should look for diff |
| 67 images; if not specified, defaults to a "file:///" URL representation | 66 images; if not specified, defaults to a "file:///" URL representation |
| 68 of generated_images_root | 67 of image_diff_db's storage_root |
| 69 builder_regex_list: List of regular expressions specifying which builders | 68 builder_regex_list: List of regular expressions specifying which builders |
| 70 we will process. If None, process all builders. | 69 we will process. If None, process all builders. |
| 71 """ | 70 """ |
| 72 time_start = int(time.time()) | 71 time_start = int(time.time()) |
| 73 if builder_regex_list != None: | 72 if builder_regex_list != None: |
| 74 self.set_match_builders_pattern_list(builder_regex_list) | 73 self.set_match_builders_pattern_list(builder_regex_list) |
| 75 self._image_diff_db = imagediffdb.ImageDiffDB(generated_images_root) | 74 self._image_diff_db = image_diff_db |
| 76 self._diff_base_url = ( | 75 self._diff_base_url = ( |
| 77 diff_base_url or | 76 diff_base_url or |
| 78 url_utils.create_filepath_url(generated_images_root)) | 77 url_utils.create_filepath_url(image_diff_db.get_storage_root())) |
| 79 self._actuals_root = actuals_root | 78 self._actuals_root = actuals_root |
| 80 self._expected_root = expected_root | 79 self._expected_root = expected_root |
| 81 self._ignore_failures_on_these_tests = [] | 80 self._ignore_failures_on_these_tests = [] |
| 82 if ignore_failures_file: | 81 if ignore_failures_file: |
| 83 self._ignore_failures_on_these_tests = ( | 82 self._ignore_failures_on_these_tests = ( |
| 84 ExpectationComparisons._read_noncomment_lines( | 83 ExpectationComparisons._read_noncomment_lines( |
| 85 os.path.join(expected_root, ignore_failures_file))) | 84 os.path.join(expected_root, ignore_failures_file))) |
| 86 self._load_actual_and_expected() | 85 self._load_actual_and_expected() |
| 87 self._timestamp = int(time.time()) | 86 self._timestamp = int(time.time()) |
| 88 logging.info('Results complete; took %d seconds.' % | 87 logging.info('Results complete; took %d seconds.' % |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 164 pattern: which files to write within root (fnmatch-style pattern) | 163 pattern: which files to write within root (fnmatch-style pattern) |
| 165 | 164 |
| 166 Raises: | 165 Raises: |
| 167 IOError if root does not refer to an existing directory | 166 IOError if root does not refer to an existing directory |
| 168 KeyError if the set of per-builder dictionaries written out was | 167 KeyError if the set of per-builder dictionaries written out was |
| 169 different than expected | 168 different than expected |
| 170 """ | 169 """ |
| 171 if not os.path.isdir(root): | 170 if not os.path.isdir(root): |
| 172 raise IOError('no directory found at path %s' % root) | 171 raise IOError('no directory found at path %s' % root) |
| 173 actual_builders_written = [] | 172 actual_builders_written = [] |
| 174 for dirpath, dirnames, filenames in os.walk(root): | 173 for dirpath, _, filenames in os.walk(root): |
| 175 for matching_filename in fnmatch.filter(filenames, pattern): | 174 for matching_filename in fnmatch.filter(filenames, pattern): |
| 176 builder = os.path.basename(dirpath) | 175 builder = os.path.basename(dirpath) |
| 177 per_builder_dict = meta_dict.get(builder) | 176 per_builder_dict = meta_dict.get(builder) |
| 178 if per_builder_dict is not None: | 177 if per_builder_dict is not None: |
| 179 fullpath = os.path.join(dirpath, matching_filename) | 178 fullpath = os.path.join(dirpath, matching_filename) |
| 180 gm_json.WriteToFile(per_builder_dict, fullpath) | 179 gm_json.WriteToFile(per_builder_dict, fullpath) |
| 181 actual_builders_written.append(builder) | 180 actual_builders_written.append(builder) |
| 182 | 181 |
| 183 # Check: did we write out the set of per-builder dictionaries we | 182 # Check: did we write out the set of per-builder dictionaries we |
| 184 # expected to? | 183 # expected to? |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 332 imageA_relative_url=expected_image_relative_url, | 331 imageA_relative_url=expected_image_relative_url, |
| 333 imageB_relative_url=actual_image_relative_url, | 332 imageB_relative_url=actual_image_relative_url, |
| 334 expectations=expectations_dict, | 333 expectations=expectations_dict, |
| 335 extra_columns=extra_columns_dict) | 334 extra_columns=extra_columns_dict) |
| 336 all_image_pairs.add_image_pair(image_pair) | 335 all_image_pairs.add_image_pair(image_pair) |
| 337 if updated_result_type != results.KEY__RESULT_TYPE__SUCCEEDED: | 336 if updated_result_type != results.KEY__RESULT_TYPE__SUCCEEDED: |
| 338 failing_image_pairs.add_image_pair(image_pair) | 337 failing_image_pairs.add_image_pair(image_pair) |
| 339 except Exception: | 338 except Exception: |
| 340 logging.exception('got exception while creating new ImagePair') | 339 logging.exception('got exception while creating new ImagePair') |
| 341 | 340 |
| 341 # pylint: disable=W0201 |
| 342 self._results = { | 342 self._results = { |
| 343 results.KEY__HEADER__RESULTS_ALL: all_image_pairs.as_dict(), | 343 results.KEY__HEADER__RESULTS_ALL: all_image_pairs.as_dict(), |
| 344 results.KEY__HEADER__RESULTS_FAILURES: failing_image_pairs.as_dict(), | 344 results.KEY__HEADER__RESULTS_FAILURES: failing_image_pairs.as_dict(), |
| 345 } | 345 } |
| 346 | 346 |
| 347 | 347 |
| 348 def main(): | 348 def main(): |
| 349 logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', | 349 logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', |
| 350 datefmt='%m/%d/%Y %H:%M:%S', | 350 datefmt='%m/%d/%Y %H:%M:%S', |
| 351 level=logging.INFO) | 351 level=logging.INFO) |
| (...skipping 18 matching lines...) Expand all Loading... |
| 370 '--results', default=results.KEY__HEADER__RESULTS_FAILURES, | 370 '--results', default=results.KEY__HEADER__RESULTS_FAILURES, |
| 371 help='Which result types to include. Defaults to \'%(default)s\'; ' | 371 help='Which result types to include. Defaults to \'%(default)s\'; ' |
| 372 'must be one of ' + | 372 'must be one of ' + |
| 373 str([results.KEY__HEADER__RESULTS_FAILURES, | 373 str([results.KEY__HEADER__RESULTS_FAILURES, |
| 374 results.KEY__HEADER__RESULTS_ALL])) | 374 results.KEY__HEADER__RESULTS_ALL])) |
| 375 parser.add_argument( | 375 parser.add_argument( |
| 376 '--workdir', default=results.DEFAULT_GENERATED_IMAGES_ROOT, | 376 '--workdir', default=results.DEFAULT_GENERATED_IMAGES_ROOT, |
| 377 help='Directory within which to download images and generate diffs; ' | 377 help='Directory within which to download images and generate diffs; ' |
| 378 'defaults to \'%(default)s\' .') | 378 'defaults to \'%(default)s\' .') |
| 379 args = parser.parse_args() | 379 args = parser.parse_args() |
| 380 image_diff_db = imagediffdb.ImageDiffDB(storage_root=args.workdir) |
| 380 results_obj = ExpectationComparisons( | 381 results_obj = ExpectationComparisons( |
| 382 image_diff_db=image_diff_db, |
| 381 actuals_root=args.actuals, | 383 actuals_root=args.actuals, |
| 382 expected_root=args.expectations, | 384 expected_root=args.expectations, |
| 383 ignore_failures_file=args.ignore_failures_file, | 385 ignore_failures_file=args.ignore_failures_file) |
| 384 generated_images_root=args.workdir) | |
| 385 gm_json.WriteToFile( | 386 gm_json.WriteToFile( |
| 386 results_obj.get_packaged_results_of_type(results_type=args.results), | 387 results_obj.get_packaged_results_of_type(results_type=args.results), |
| 387 args.outfile) | 388 args.outfile) |
| 388 | 389 |
| 389 | 390 |
| 390 if __name__ == '__main__': | 391 if __name__ == '__main__': |
| 391 main() | 392 main() |
| OLD | NEW |