Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: gm/rebaseline_server/compare_rendered_pictures.py

Issue 216103004: teach rebaseline_server how to compare results of multiple render_pictures runs (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: ravi comments Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/python
2
3 """
4 Copyright 2014 Google Inc.
5
6 Use of this source code is governed by a BSD-style license that can be
7 found in the LICENSE file.
8
9 Compare results of two render_pictures runs.
10 """
11
12 # System-level imports
13 import logging
14 import os
15 import re
16 import sys
17 import time
18
19 # Imports from within Skia
20 #
21 # TODO(epoger): Once we move the create_filepath_url() function out of
22 # download_actuals into a shared utility module, we won't need to import
23 # download_actuals anymore.
24 #
25 # We need to add the 'gm' directory, so that we can import gm_json.py within
26 # that directory. That script allows us to parse the actual-results.json file
27 # written out by the GM tool.
28 # Make sure that the 'gm' dir is in the PYTHONPATH, but add it at the *end*
29 # so any dirs that are already in the PYTHONPATH will be preferred.
30 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
31 GM_DIRECTORY = os.path.dirname(PARENT_DIRECTORY)
32 TRUNK_DIRECTORY = os.path.dirname(GM_DIRECTORY)
33 if GM_DIRECTORY not in sys.path:
34 sys.path.append(GM_DIRECTORY)
35 import download_actuals
36 import gm_json
37 import imagediffdb
38 import imagepair
39 import imagepairset
40 import results
41
42 # Characters we don't want popping up just anywhere within filenames.
43 DISALLOWED_FILEPATH_CHAR_REGEX = re.compile('[^\w\-]')
44
45 # URL under which all render_pictures images can be found in Google Storage.
46 # TODO(epoger): Move this default value into
47 # https://skia.googlesource.com/buildbot/+/master/site_config/global_variables.j son
48 DEFAULT_IMAGE_BASE_URL = 'http://chromium-skia-gm.commondatastorage.googleapis.c om/render_pictures/images'
49
50
51 class RenderedPicturesComparisons(results.BaseComparisons):
52 """Loads results from two different render_pictures runs into an ImagePairSet.
53 """
54
55 def __init__(self, subdirs, actuals_root,
56 generated_images_root=results.DEFAULT_GENERATED_IMAGES_ROOT,
57 image_base_url=DEFAULT_IMAGE_BASE_URL,
58 diff_base_url=None):
59 """
60 Args:
61 actuals_root: root directory containing all render_pictures-generated
62 JSON files
63 subdirs: (string, string) tuple; pair of subdirectories within
64 actuals_root to compare
65 generated_images_root: directory within which to create all pixel diffs;
66 if this directory does not yet exist, it will be created
67 image_base_url: URL under which all render_pictures result images can
68 be found; this will be used to read images for comparison within
69 this code, and included in the ImagePairSet so its consumers know
70 where to download the images from
71 diff_base_url: base URL within which the client should look for diff
72 images; if not specified, defaults to a "file:///" URL representation
73 of generated_images_root
74 """
75 time_start = int(time.time())
76 self._image_diff_db = imagediffdb.ImageDiffDB(generated_images_root)
77 self._image_base_url = image_base_url
78 self._diff_base_url = (
79 diff_base_url or
80 download_actuals.create_filepath_url(generated_images_root))
81 self._load_result_pairs(actuals_root, subdirs)
82 self._timestamp = int(time.time())
83 logging.info('Results complete; took %d seconds.' %
84 (self._timestamp - time_start))
85
86 def _load_result_pairs(self, actuals_root, subdirs):
87 """Loads all JSON files found within two subdirs in actuals_root,
88 compares across those two subdirs, and stores the summary in self._results.
89
90 Args:
91 actuals_root: root directory containing all render_pictures-generated
92 JSON files
93 subdirs: (string, string) tuple; pair of subdirectories within
94 actuals_root to compare
95 """
96 logging.info(
97 'Reading actual-results JSON files from %s subdirs within %s...' % (
98 subdirs, actuals_root))
99 subdirA, subdirB = subdirs
100 subdirA_builder_dicts = results.BaseComparisons._read_dicts_from_root(
101 os.path.join(actuals_root, subdirA))
102 subdirB_builder_dicts = results.BaseComparisons._read_dicts_from_root(
103 os.path.join(actuals_root, subdirB))
104 logging.info('Comparing subdirs %s and %s...' % (subdirA, subdirB))
105
106 all_image_pairs = imagepairset.ImagePairSet(
107 descriptions=subdirs,
108 diff_base_url=self._diff_base_url)
109 failing_image_pairs = imagepairset.ImagePairSet(
110 descriptions=subdirs,
111 diff_base_url=self._diff_base_url)
112
113 all_image_pairs.ensure_extra_column_values_in_summary(
114 column_id=results.KEY__EXTRACOLUMN__RESULT_TYPE, values=[
115 results.KEY__RESULT_TYPE__FAILED,
116 results.KEY__RESULT_TYPE__NOCOMPARISON,
117 results.KEY__RESULT_TYPE__SUCCEEDED,
118 ])
119 failing_image_pairs.ensure_extra_column_values_in_summary(
120 column_id=results.KEY__EXTRACOLUMN__RESULT_TYPE, values=[
121 results.KEY__RESULT_TYPE__FAILED,
122 results.KEY__RESULT_TYPE__NOCOMPARISON,
123 ])
124
125 builders = sorted(set(subdirA_builder_dicts.keys() +
126 subdirB_builder_dicts.keys()))
127 num_builders = len(builders)
128 builder_num = 0
129 for builder in builders:
130 builder_num += 1
131 logging.info('Generating pixel diffs for builder #%d of %d, "%s"...' %
132 (builder_num, num_builders, builder))
133 # TODO(epoger): This will fail if we have results for this builder in
134 # subdirA but not subdirB (or vice versa).
135 subdirA_results = results.BaseComparisons.combine_subdicts(
136 subdirA_builder_dicts[builder][gm_json.JSONKEY_ACTUALRESULTS])
137 subdirB_results = results.BaseComparisons.combine_subdicts(
138 subdirB_builder_dicts[builder][gm_json.JSONKEY_ACTUALRESULTS])
139 image_names = sorted(set(subdirA_results.keys() +
140 subdirB_results.keys()))
141 for image_name in image_names:
142 # The image name may contain funny characters or be ridiculously long
143 # (see https://code.google.com/p/skia/issues/detail?id=2344#c10 ),
144 # so make sure we sanitize it before using it in a URL path.
145 #
146 # TODO(epoger): Rather than sanitizing/truncating the image name here,
147 # do it in render_pictures instead.
148 # Reason: we will need to be consistent in applying this rule, so that
149 # the process which uploads the files to GS using these paths will
150 # match the paths created by downstream processes.
151 # So, we should make render_pictures write out images to paths that are
152 # "ready to upload" to Google Storage, like gm does.
153 sanitized_test_name = DISALLOWED_FILEPATH_CHAR_REGEX.sub(
154 '_', image_name)[:30]
155
156 subdirA_image_relative_url = (
157 results.BaseComparisons._create_relative_url(
158 hashtype_and_digest=subdirA_results.get(image_name),
159 test_name=sanitized_test_name))
160 subdirB_image_relative_url = (
161 results.BaseComparisons._create_relative_url(
162 hashtype_and_digest=subdirB_results.get(image_name),
163 test_name=sanitized_test_name))
164
165 # If we have images for at least one of these two subdirs,
166 # add them to our list.
167 if subdirA_image_relative_url or subdirB_image_relative_url:
168 if subdirA_image_relative_url == subdirB_image_relative_url:
169 result_type = results.KEY__RESULT_TYPE__SUCCEEDED
170 elif not subdirA_image_relative_url:
171 result_type = results.KEY__RESULT_TYPE__NOCOMPARISON
172 elif not subdirB_image_relative_url:
173 result_type = results.KEY__RESULT_TYPE__NOCOMPARISON
174 else:
175 result_type = results.KEY__RESULT_TYPE__FAILED
176
177 extra_columns_dict = {
178 results.KEY__EXTRACOLUMN__RESULT_TYPE: result_type,
179 results.KEY__EXTRACOLUMN__BUILDER: builder,
180 results.KEY__EXTRACOLUMN__TEST: image_name,
181 # TODO(epoger): Right now, the client UI crashes if it receives
182 # results that do not include a 'config' column.
183 # Until we fix that, keep the client happy.
184 results.KEY__EXTRACOLUMN__CONFIG: 'TODO',
185 }
186
187 try:
188 image_pair = imagepair.ImagePair(
189 image_diff_db=self._image_diff_db,
190 base_url=self._image_base_url,
191 imageA_relative_url=subdirA_image_relative_url,
192 imageB_relative_url=subdirB_image_relative_url,
193 extra_columns=extra_columns_dict)
194 all_image_pairs.add_image_pair(image_pair)
195 if result_type != results.KEY__RESULT_TYPE__SUCCEEDED:
196 failing_image_pairs.add_image_pair(image_pair)
197 except (KeyError, TypeError):
198 logging.exception(
199 'got exception while creating ImagePair for image_name '
200 '"%s", builder "%s"' % (image_name, builder))
201
202 self._results = {
203 results.KEY__HEADER__RESULTS_ALL: all_image_pairs.as_dict(),
204 results.KEY__HEADER__RESULTS_FAILURES: failing_image_pairs.as_dict(),
205 }
206
207
208 # TODO(epoger): Add main() so this can be called by vm_run_skia_try.sh
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698