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 | 15 import json |
16 import logging | 16 import logging |
17 import os | 17 import os |
18 import re | 18 import re |
19 import sys | 19 import sys |
20 import time | 20 import time |
21 | 21 |
22 # Imports from within Skia | 22 # Imports from within Skia |
23 # | 23 # |
| 24 # TODO(epoger): Once we move the create_filepath_url() function out of |
| 25 # download_actuals into a shared utility module, we won't need to import |
| 26 # download_actuals anymore. |
| 27 # |
24 # We need to add the 'gm' directory, so that we can import gm_json.py within | 28 # We need to add the 'gm' directory, so that we can import gm_json.py within |
25 # that directory. That script allows us to parse the actual-results.json file | 29 # that directory. That script allows us to parse the actual-results.json file |
26 # written out by the GM tool. | 30 # written out by the GM tool. |
27 # Make sure that the 'gm' dir is in the PYTHONPATH, but add it at the *end* | 31 # Make sure that the 'gm' dir is in the PYTHONPATH, but add it at the *end* |
28 # so any dirs that are already in the PYTHONPATH will be preferred. | 32 # so any dirs that are already in the PYTHONPATH will be preferred. |
29 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) | 33 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) |
30 GM_DIRECTORY = os.path.dirname(PARENT_DIRECTORY) | 34 GM_DIRECTORY = os.path.dirname(PARENT_DIRECTORY) |
31 TRUNK_DIRECTORY = os.path.dirname(GM_DIRECTORY) | 35 TRUNK_DIRECTORY = os.path.dirname(GM_DIRECTORY) |
32 if GM_DIRECTORY not in sys.path: | 36 if GM_DIRECTORY not in sys.path: |
33 sys.path.append(GM_DIRECTORY) | 37 sys.path.append(GM_DIRECTORY) |
| 38 import download_actuals |
34 import gm_json | 39 import gm_json |
35 import imagediffdb | 40 import imagediffdb |
36 import imagepair | 41 import imagepair |
37 import imagepairset | 42 import imagepairset |
38 | 43 |
39 # Keys used to link an image to a particular GM test. | 44 # Keys used to link an image to a particular GM test. |
40 # NOTE: Keep these in sync with static/constants.js | 45 # NOTE: Keep these in sync with static/constants.js |
41 REBASELINE_SERVER_SCHEMA_VERSION_NUMBER = 2 | 46 REBASELINE_SERVER_SCHEMA_VERSION_NUMBER = 2 |
42 KEY__EXPECTATIONS__BUGS = gm_json.JSONKEY_EXPECTEDRESULTS_BUGS | 47 KEY__EXPECTATIONS__BUGS = gm_json.JSONKEY_EXPECTEDRESULTS_BUGS |
43 KEY__EXPECTATIONS__IGNOREFAILURE = gm_json.JSONKEY_EXPECTEDRESULTS_IGNOREFAILURE | 48 KEY__EXPECTATIONS__IGNOREFAILURE = gm_json.JSONKEY_EXPECTEDRESULTS_IGNOREFAILURE |
(...skipping 25 matching lines...) Expand all Loading... |
69 KEY__EXPECTATIONS__REVIEWED, | 74 KEY__EXPECTATIONS__REVIEWED, |
70 ] | 75 ] |
71 | 76 |
72 IMAGE_FILENAME_RE = re.compile(gm_json.IMAGE_FILENAME_PATTERN) | 77 IMAGE_FILENAME_RE = re.compile(gm_json.IMAGE_FILENAME_PATTERN) |
73 IMAGE_FILENAME_FORMATTER = '%s_%s.png' # pass in (testname, config) | 78 IMAGE_FILENAME_FORMATTER = '%s_%s.png' # pass in (testname, config) |
74 | 79 |
75 IMAGEPAIR_SET_DESCRIPTIONS = ('expected image', 'actual image') | 80 IMAGEPAIR_SET_DESCRIPTIONS = ('expected image', 'actual image') |
76 | 81 |
77 DEFAULT_ACTUALS_DIR = '.gm-actuals' | 82 DEFAULT_ACTUALS_DIR = '.gm-actuals' |
78 DEFAULT_EXPECTATIONS_DIR = os.path.join(TRUNK_DIRECTORY, 'expectations', 'gm') | 83 DEFAULT_EXPECTATIONS_DIR = os.path.join(TRUNK_DIRECTORY, 'expectations', 'gm') |
79 DEFAULT_GENERATED_IMAGES_ROOT = os.path.join(PARENT_DIRECTORY, 'static', | 84 DEFAULT_GENERATED_IMAGES_ROOT = os.path.join( |
80 'generated-images') | 85 PARENT_DIRECTORY, '.generated-images') |
81 | 86 |
82 | 87 |
83 class Results(object): | 88 class Results(object): |
84 """ Loads actual and expected GM results into an ImagePairSet. | 89 """ Loads actual and expected GM results into an ImagePairSet. |
85 | 90 |
86 Loads actual and expected results from all builders, except for those skipped | 91 Loads actual and expected results from all builders, except for those skipped |
87 by _ignore_builder(). | 92 by _ignore_builder(). |
88 | 93 |
89 Once this object has been constructed, the results (in self._results[]) | 94 Once this object has been constructed, the results (in self._results[]) |
90 are immutable. If you want to update the results based on updated JSON | 95 are immutable. If you want to update the results based on updated JSON |
91 file contents, you will need to create a new Results object.""" | 96 file contents, you will need to create a new Results object.""" |
92 | 97 |
93 def __init__(self, actuals_root=DEFAULT_ACTUALS_DIR, | 98 def __init__(self, actuals_root=DEFAULT_ACTUALS_DIR, |
94 expected_root=DEFAULT_EXPECTATIONS_DIR, | 99 expected_root=DEFAULT_EXPECTATIONS_DIR, |
95 generated_images_root=DEFAULT_GENERATED_IMAGES_ROOT): | 100 generated_images_root=DEFAULT_GENERATED_IMAGES_ROOT, |
| 101 diff_base_url=None): |
96 """ | 102 """ |
97 Args: | 103 Args: |
98 actuals_root: root directory containing all actual-results.json files | 104 actuals_root: root directory containing all actual-results.json files |
99 expected_root: root directory containing all expected-results.json files | 105 expected_root: root directory containing all expected-results.json files |
100 generated_images_root: directory within which to create all pixel diffs; | 106 generated_images_root: directory within which to create all pixel diffs; |
101 if this directory does not yet exist, it will be created | 107 if this directory does not yet exist, it will be created |
| 108 diff_base_url: base URL within which the client should look for diff |
| 109 images; if not specified, defaults to a "file:///" URL representation |
| 110 of generated_images_root |
102 """ | 111 """ |
103 time_start = int(time.time()) | 112 time_start = int(time.time()) |
104 self._image_diff_db = imagediffdb.ImageDiffDB(generated_images_root) | 113 self._image_diff_db = imagediffdb.ImageDiffDB(generated_images_root) |
| 114 self._diff_base_url = ( |
| 115 diff_base_url or |
| 116 download_actuals.create_filepath_url(generated_images_root)) |
105 self._actuals_root = actuals_root | 117 self._actuals_root = actuals_root |
106 self._expected_root = expected_root | 118 self._expected_root = expected_root |
107 self._load_actual_and_expected() | 119 self._load_actual_and_expected() |
108 self._timestamp = int(time.time()) | 120 self._timestamp = int(time.time()) |
109 logging.info('Results complete; took %d seconds.' % | 121 logging.info('Results complete; took %d seconds.' % |
110 (self._timestamp - time_start)) | 122 (self._timestamp - time_start)) |
111 | 123 |
112 def get_timestamp(self): | 124 def get_timestamp(self): |
113 """Return the time at which this object was created, in seconds past epoch | 125 """Return the time at which this object was created, in seconds past epoch |
114 (UTC). | 126 (UTC). |
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
336 files within self._actuals_root and self._expected_root), | 348 files within self._actuals_root and self._expected_root), |
337 and stores them in self._results. | 349 and stores them in self._results. |
338 """ | 350 """ |
339 logging.info('Reading actual-results JSON files from %s...' % | 351 logging.info('Reading actual-results JSON files from %s...' % |
340 self._actuals_root) | 352 self._actuals_root) |
341 actual_builder_dicts = Results._read_dicts_from_root(self._actuals_root) | 353 actual_builder_dicts = Results._read_dicts_from_root(self._actuals_root) |
342 logging.info('Reading expected-results JSON files from %s...' % | 354 logging.info('Reading expected-results JSON files from %s...' % |
343 self._expected_root) | 355 self._expected_root) |
344 expected_builder_dicts = Results._read_dicts_from_root(self._expected_root) | 356 expected_builder_dicts = Results._read_dicts_from_root(self._expected_root) |
345 | 357 |
346 all_image_pairs = imagepairset.ImagePairSet(IMAGEPAIR_SET_DESCRIPTIONS) | 358 all_image_pairs = imagepairset.ImagePairSet( |
347 failing_image_pairs = imagepairset.ImagePairSet(IMAGEPAIR_SET_DESCRIPTIONS) | 359 descriptions=IMAGEPAIR_SET_DESCRIPTIONS, |
| 360 diff_base_url=self._diff_base_url) |
| 361 failing_image_pairs = imagepairset.ImagePairSet( |
| 362 descriptions=IMAGEPAIR_SET_DESCRIPTIONS, |
| 363 diff_base_url=self._diff_base_url) |
348 | 364 |
349 all_image_pairs.ensure_extra_column_values_in_summary( | 365 all_image_pairs.ensure_extra_column_values_in_summary( |
350 column_id=KEY__EXTRACOLUMN__RESULT_TYPE, values=[ | 366 column_id=KEY__EXTRACOLUMN__RESULT_TYPE, values=[ |
351 KEY__RESULT_TYPE__FAILED, | 367 KEY__RESULT_TYPE__FAILED, |
352 KEY__RESULT_TYPE__FAILUREIGNORED, | 368 KEY__RESULT_TYPE__FAILUREIGNORED, |
353 KEY__RESULT_TYPE__NOCOMPARISON, | 369 KEY__RESULT_TYPE__NOCOMPARISON, |
354 KEY__RESULT_TYPE__SUCCEEDED, | 370 KEY__RESULT_TYPE__SUCCEEDED, |
355 ]) | 371 ]) |
356 failing_image_pairs.ensure_extra_column_values_in_summary( | 372 failing_image_pairs.ensure_extra_column_values_in_summary( |
357 column_id=KEY__EXTRACOLUMN__RESULT_TYPE, values=[ | 373 column_id=KEY__EXTRACOLUMN__RESULT_TYPE, values=[ |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
496 results = Results(actuals_root=args.actuals, | 512 results = Results(actuals_root=args.actuals, |
497 expected_root=args.expectations, | 513 expected_root=args.expectations, |
498 generated_images_root=args.workdir) | 514 generated_images_root=args.workdir) |
499 gm_json.WriteToFile( | 515 gm_json.WriteToFile( |
500 results.get_packaged_results_of_type(results_type=args.results), | 516 results.get_packaged_results_of_type(results_type=args.results), |
501 args.outfile) | 517 args.outfile) |
502 | 518 |
503 | 519 |
504 if __name__ == '__main__': | 520 if __name__ == '__main__': |
505 main() | 521 main() |
OLD | NEW |