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 fnmatch | 13 import fnmatch |
14 import os | 14 import os |
15 import re | 15 import re |
16 | 16 |
17 # Must fix up PYTHONPATH before importing from within Skia | 17 # Must fix up PYTHONPATH before importing from within Skia |
18 import fix_pythonpath # pylint: disable=W0611 | 18 import fix_pythonpath # pylint: disable=W0611 |
19 | 19 |
20 # Imports from within Skia | 20 # Imports from within Skia |
21 import gm_json | 21 import gm_json |
22 import imagepairset | 22 import imagepairset |
23 | 23 |
24 # Keys used to link an image to a particular GM test. | 24 # Keys used to link an image to a particular GM test. |
25 # NOTE: Keep these in sync with static/constants.js | 25 # NOTE: Keep these in sync with static/constants.js |
26 VALUE__HEADER__SCHEMA_VERSION = 4 | 26 VALUE__HEADER__SCHEMA_VERSION = 5 |
27 KEY__EXPECTATIONS__BUGS = gm_json.JSONKEY_EXPECTEDRESULTS_BUGS | 27 KEY__EXPECTATIONS__BUGS = gm_json.JSONKEY_EXPECTEDRESULTS_BUGS |
28 KEY__EXPECTATIONS__IGNOREFAILURE = gm_json.JSONKEY_EXPECTEDRESULTS_IGNOREFAILURE | 28 KEY__EXPECTATIONS__IGNOREFAILURE = gm_json.JSONKEY_EXPECTEDRESULTS_IGNOREFAILURE |
29 KEY__EXPECTATIONS__REVIEWED = gm_json.JSONKEY_EXPECTEDRESULTS_REVIEWED | 29 KEY__EXPECTATIONS__REVIEWED = gm_json.JSONKEY_EXPECTEDRESULTS_REVIEWED |
30 KEY__EXTRACOLUMNS__BUILDER = 'builder' | 30 KEY__EXTRACOLUMNS__BUILDER = 'builder' |
31 KEY__EXTRACOLUMNS__CONFIG = 'config' | 31 KEY__EXTRACOLUMNS__CONFIG = 'config' |
32 KEY__EXTRACOLUMNS__RESULT_TYPE = 'resultType' | 32 KEY__EXTRACOLUMNS__RESULT_TYPE = 'resultType' |
33 KEY__EXTRACOLUMNS__TEST = 'test' | 33 KEY__EXTRACOLUMNS__TEST = 'test' |
34 KEY__HEADER__DATAHASH = 'dataHash' | 34 KEY__HEADER__DATAHASH = 'dataHash' |
35 KEY__HEADER__IS_EDITABLE = 'isEditable' | 35 KEY__HEADER__IS_EDITABLE = 'isEditable' |
36 KEY__HEADER__IS_EXPORTED = 'isExported' | 36 KEY__HEADER__IS_EXPORTED = 'isExported' |
37 KEY__HEADER__IS_STILL_LOADING = 'resultsStillLoading' | 37 KEY__HEADER__IS_STILL_LOADING = 'resultsStillLoading' |
38 KEY__HEADER__RESULTS_ALL = 'all' | 38 KEY__HEADER__RESULTS_ALL = 'all' |
39 KEY__HEADER__RESULTS_FAILURES = 'failures' | 39 KEY__HEADER__RESULTS_FAILURES = 'failures' |
40 KEY__HEADER__SCHEMA_VERSION = 'schemaVersion' | 40 KEY__HEADER__SCHEMA_VERSION = 'schemaVersion' |
| 41 KEY__HEADER__SET_A_DESCRIPTIONS = 'setA' |
| 42 KEY__HEADER__SET_B_DESCRIPTIONS = 'setB' |
41 KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE = 'timeNextUpdateAvailable' | 43 KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE = 'timeNextUpdateAvailable' |
42 KEY__HEADER__TIME_UPDATED = 'timeUpdated' | 44 KEY__HEADER__TIME_UPDATED = 'timeUpdated' |
43 KEY__HEADER__TYPE = 'type' | 45 KEY__HEADER__TYPE = 'type' |
44 KEY__RESULT_TYPE__FAILED = gm_json.JSONKEY_ACTUALRESULTS_FAILED | 46 KEY__RESULT_TYPE__FAILED = gm_json.JSONKEY_ACTUALRESULTS_FAILED |
45 KEY__RESULT_TYPE__FAILUREIGNORED = gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED | 47 KEY__RESULT_TYPE__FAILUREIGNORED = gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED |
46 KEY__RESULT_TYPE__NOCOMPARISON = gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON | 48 KEY__RESULT_TYPE__NOCOMPARISON = gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON |
47 KEY__RESULT_TYPE__SUCCEEDED = gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED | 49 KEY__RESULT_TYPE__SUCCEEDED = gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED |
| 50 KEY__SET_DESCRIPTIONS__DIR = 'dir' |
| 51 KEY__SET_DESCRIPTIONS__REPO_REVISION = 'repoRevision' |
| 52 KEY__SET_DESCRIPTIONS__SECTION = 'section' |
48 | 53 |
49 IMAGE_FILENAME_RE = re.compile(gm_json.IMAGE_FILENAME_PATTERN) | 54 IMAGE_FILENAME_RE = re.compile(gm_json.IMAGE_FILENAME_PATTERN) |
50 IMAGE_FILENAME_FORMATTER = '%s_%s.png' # pass in (testname, config) | 55 IMAGE_FILENAME_FORMATTER = '%s_%s.png' # pass in (testname, config) |
51 | 56 |
52 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) | 57 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) |
53 DEFAULT_ACTUALS_DIR = '.gm-actuals' | 58 DEFAULT_ACTUALS_DIR = '.gm-actuals' |
54 DEFAULT_GENERATED_IMAGES_ROOT = os.path.join( | 59 DEFAULT_GENERATED_IMAGES_ROOT = os.path.join( |
55 PARENT_DIRECTORY, '.generated-images') | 60 PARENT_DIRECTORY, '.generated-images') |
56 | 61 |
57 # Define the default set of builders we will process expectations/actuals for. | 62 # Define the default set of builders we will process expectations/actuals for. |
58 # This allows us to ignore builders for which we don't maintain expectations | 63 # This allows us to ignore builders for which we don't maintain expectations |
59 # (trybots, Valgrind, ASAN, TSAN), and avoid problems like | 64 # (trybots, Valgrind, ASAN, TSAN), and avoid problems like |
60 # https://code.google.com/p/skia/issues/detail?id=2036 ('rebaseline_server | 65 # https://code.google.com/p/skia/issues/detail?id=2036 ('rebaseline_server |
61 # produces error when trying to add baselines for ASAN/TSAN builders') | 66 # produces error when trying to add baselines for ASAN/TSAN builders') |
62 DEFAULT_MATCH_BUILDERS_PATTERN_LIST = ['.*'] | 67 DEFAULT_MATCH_BUILDERS_PATTERN_LIST = ['.*'] |
63 DEFAULT_SKIP_BUILDERS_PATTERN_LIST = [ | 68 DEFAULT_SKIP_BUILDERS_PATTERN_LIST = [ |
64 '.*-Trybot', '.*Valgrind.*', '.*TSAN.*', '.*ASAN.*'] | 69 '.*-Trybot', '.*Valgrind.*', '.*TSAN.*', '.*ASAN.*'] |
65 | 70 |
66 | 71 |
67 class BaseComparisons(object): | 72 class BaseComparisons(object): |
68 """Base class for generating summary of comparisons between two image sets. | 73 """Base class for generating summary of comparisons between two image sets. |
69 """ | 74 """ |
70 | 75 |
| 76 def __init__(self): |
| 77 """Base constructor; most subclasses will override.""" |
| 78 self._setA_descriptions = None |
| 79 self._setB_descriptions = None |
| 80 |
71 def get_results_of_type(self, results_type): | 81 def get_results_of_type(self, results_type): |
72 """Return results of some/all tests (depending on 'results_type' parameter). | 82 """Return results of some/all tests (depending on 'results_type' parameter). |
73 | 83 |
74 Args: | 84 Args: |
75 results_type: string describing which types of results to include; must | 85 results_type: string describing which types of results to include; must |
76 be one of the RESULTS_* constants | 86 be one of the RESULTS_* constants |
77 | 87 |
78 Results are returned in a dictionary as output by ImagePairSet.as_dict(). | 88 Results are returned in a dictionary as output by ImagePairSet.as_dict(). |
79 """ | 89 """ |
80 return self._results[results_type] | 90 return self._results[results_type] |
81 | 91 |
82 def get_packaged_results_of_type(self, results_type, reload_seconds=None, | 92 def get_packaged_results_of_type(self, results_type, reload_seconds=None, |
83 is_editable=False, is_exported=True): | 93 is_editable=False, is_exported=True): |
84 """Package the results of some/all tests as a complete response_dict. | 94 """Package the results of some/all tests as a complete response_dict. |
85 | 95 |
86 Args: | 96 Args: |
87 results_type: string indicating which set of results to return; | 97 results_type: string indicating which set of results to return; |
88 must be one of the RESULTS_* constants | 98 must be one of the RESULTS_* constants |
89 reload_seconds: if specified, note that new results may be available once | 99 reload_seconds: if specified, note that new results may be available once |
90 these results are reload_seconds old | 100 these results are reload_seconds old |
91 is_editable: whether clients are allowed to submit new baselines | 101 is_editable: whether clients are allowed to submit new baselines |
92 is_exported: whether these results are being made available to other | 102 is_exported: whether these results are being made available to other |
93 network hosts | 103 network hosts |
94 """ | 104 """ |
95 response_dict = self._results[results_type] | 105 response_dict = self._results[results_type] |
96 time_updated = self.get_timestamp() | 106 time_updated = self.get_timestamp() |
97 response_dict[imagepairset.KEY__ROOT__HEADER] = { | 107 header_dict = { |
98 KEY__HEADER__SCHEMA_VERSION: ( | 108 KEY__HEADER__SCHEMA_VERSION: ( |
99 VALUE__HEADER__SCHEMA_VERSION), | 109 VALUE__HEADER__SCHEMA_VERSION), |
100 | 110 |
101 # Timestamps: | 111 # Timestamps: |
102 # 1. when this data was last updated | 112 # 1. when this data was last updated |
103 # 2. when the caller should check back for new data (if ever) | 113 # 2. when the caller should check back for new data (if ever) |
104 KEY__HEADER__TIME_UPDATED: time_updated, | 114 KEY__HEADER__TIME_UPDATED: time_updated, |
105 KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE: ( | 115 KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE: ( |
106 (time_updated+reload_seconds) if reload_seconds else None), | 116 (time_updated+reload_seconds) if reload_seconds else None), |
107 | 117 |
108 # The type we passed to get_results_of_type() | 118 # The type we passed to get_results_of_type() |
109 KEY__HEADER__TYPE: results_type, | 119 KEY__HEADER__TYPE: results_type, |
110 | 120 |
111 # Hash of dataset, which the client must return with any edits-- | 121 # Hash of dataset, which the client must return with any edits-- |
112 # this ensures that the edits were made to a particular dataset. | 122 # this ensures that the edits were made to a particular dataset. |
113 KEY__HEADER__DATAHASH: str(hash(repr( | 123 KEY__HEADER__DATAHASH: str(hash(repr( |
114 response_dict[imagepairset.KEY__ROOT__IMAGEPAIRS]))), | 124 response_dict[imagepairset.KEY__ROOT__IMAGEPAIRS]))), |
115 | 125 |
116 # Whether the server will accept edits back. | 126 # Whether the server will accept edits back. |
117 KEY__HEADER__IS_EDITABLE: is_editable, | 127 KEY__HEADER__IS_EDITABLE: is_editable, |
118 | 128 |
119 # Whether the service is accessible from other hosts. | 129 # Whether the service is accessible from other hosts. |
120 KEY__HEADER__IS_EXPORTED: is_exported, | 130 KEY__HEADER__IS_EXPORTED: is_exported, |
121 } | 131 } |
| 132 if self._setA_descriptions: |
| 133 header_dict[KEY__HEADER__SET_A_DESCRIPTIONS] = self._setA_descriptions |
| 134 if self._setB_descriptions: |
| 135 header_dict[KEY__HEADER__SET_B_DESCRIPTIONS] = self._setB_descriptions |
| 136 response_dict[imagepairset.KEY__ROOT__HEADER] = header_dict |
122 return response_dict | 137 return response_dict |
123 | 138 |
124 def get_timestamp(self): | 139 def get_timestamp(self): |
125 """Return the time at which this object was created, in seconds past epoch | 140 """Return the time at which this object was created, in seconds past epoch |
126 (UTC). | 141 (UTC). |
127 """ | 142 """ |
128 return self._timestamp | 143 return self._timestamp |
129 | 144 |
130 _match_builders_pattern_list = [ | 145 _match_builders_pattern_list = [ |
131 re.compile(p) for p in DEFAULT_MATCH_BUILDERS_PATTERN_LIST] | 146 re.compile(p) for p in DEFAULT_MATCH_BUILDERS_PATTERN_LIST] |
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
315 default_value: value to return if input_dict is None or any key cannot | 330 default_value: value to return if input_dict is None or any key cannot |
316 be found along the way | 331 be found along the way |
317 """ | 332 """ |
318 if input_dict == None: | 333 if input_dict == None: |
319 return default_value | 334 return default_value |
320 for key in keys: | 335 for key in keys: |
321 input_dict = input_dict.get(key, None) | 336 input_dict = input_dict.get(key, None) |
322 if input_dict == None: | 337 if input_dict == None: |
323 return default_value | 338 return default_value |
324 return input_dict | 339 return input_dict |
OLD | NEW |