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 HTTP server for our HTML rebaseline viewer. | 9 HTTP server for our HTML rebaseline viewer. |
10 """ | 10 """ |
(...skipping 22 matching lines...) Expand all Loading... |
33 import gm_json | 33 import gm_json |
34 | 34 |
35 # Imports from local dir | 35 # Imports from local dir |
36 # | 36 # |
37 # pylint: disable=C0301 | 37 # pylint: disable=C0301 |
38 # Note: we import results under a different name, to avoid confusion with the | 38 # Note: we import results under a different name, to avoid confusion with the |
39 # Server.results() property. See discussion at | 39 # Server.results() property. See discussion at |
40 # https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.p
y#newcode44 | 40 # https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.p
y#newcode44 |
41 # pylint: enable=C0301 | 41 # pylint: enable=C0301 |
42 import compare_configs | 42 import compare_configs |
| 43 import compare_rendered_pictures |
43 import compare_to_expectations | 44 import compare_to_expectations |
44 import download_actuals | 45 import download_actuals |
45 import imagediffdb | 46 import imagediffdb |
46 import imagepairset | 47 import imagepairset |
47 import results as results_mod | 48 import results as results_mod |
48 | 49 |
49 PATHSPLIT_RE = re.compile('/([^/]+)/(.+)') | 50 PATHSPLIT_RE = re.compile('/([^/]+)/(.+)') |
50 | 51 |
51 # A simple dictionary of file name extensions to MIME types. The empty string | 52 # A simple dictionary of file name extensions to MIME types. The empty string |
52 # entry is used as the default when no extension was given or if the extension | 53 # entry is used as the default when no extension was given or if the extension |
(...skipping 13 matching lines...) Expand all Loading... |
66 KEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType' | 67 KEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType' |
67 | 68 |
68 DEFAULT_ACTUALS_DIR = results_mod.DEFAULT_ACTUALS_DIR | 69 DEFAULT_ACTUALS_DIR = results_mod.DEFAULT_ACTUALS_DIR |
69 DEFAULT_GM_SUMMARIES_BUCKET = download_actuals.GM_SUMMARIES_BUCKET | 70 DEFAULT_GM_SUMMARIES_BUCKET = download_actuals.GM_SUMMARIES_BUCKET |
70 DEFAULT_JSON_FILENAME = download_actuals.DEFAULT_JSON_FILENAME | 71 DEFAULT_JSON_FILENAME = download_actuals.DEFAULT_JSON_FILENAME |
71 DEFAULT_PORT = 8888 | 72 DEFAULT_PORT = 8888 |
72 | 73 |
73 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) | 74 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) |
74 TRUNK_DIRECTORY = os.path.dirname(os.path.dirname(PARENT_DIRECTORY)) | 75 TRUNK_DIRECTORY = os.path.dirname(os.path.dirname(PARENT_DIRECTORY)) |
75 # Directory, relative to PARENT_DIRECTORY, within which the server will serve | 76 # Directory, relative to PARENT_DIRECTORY, within which the server will serve |
76 # out live results (not static files). | 77 # out image diff data from within the precomputed _SERVER.results . |
77 RESULTS_SUBDIR = 'results' | 78 PRECOMPUTED_RESULTS_SUBDIR = 'results' |
| 79 # Directory, relative to PARENT_DIRECTORY, within which the server will serve |
| 80 # out live-generated image diff data. |
| 81 LIVE_RESULTS_SUBDIR = 'live-results' |
78 # Directory, relative to PARENT_DIRECTORY, within which the server will serve | 82 # Directory, relative to PARENT_DIRECTORY, within which the server will serve |
79 # out static files. | 83 # out static files. |
80 STATIC_CONTENTS_SUBDIR = 'static' | 84 STATIC_CONTENTS_SUBDIR = 'static' |
81 # All of the GENERATED_*_SUBDIRS are relative to STATIC_CONTENTS_SUBDIR | 85 # All of the GENERATED_*_SUBDIRS are relative to STATIC_CONTENTS_SUBDIR |
82 GENERATED_HTML_SUBDIR = 'generated-html' | 86 GENERATED_HTML_SUBDIR = 'generated-html' |
83 GENERATED_IMAGES_SUBDIR = 'generated-images' | 87 GENERATED_IMAGES_SUBDIR = 'generated-images' |
84 GENERATED_JSON_SUBDIR = 'generated-json' | 88 GENERATED_JSON_SUBDIR = 'generated-json' |
85 | 89 |
| 90 # Parameters we use within do_GET_live_results() |
| 91 LIVE_PARAM__ACTUALS_DIR = 'actualsDir' |
| 92 LIVE_PARAM__EXPECTATIONS_DIR = 'expectationsDir' |
| 93 |
86 # How often (in seconds) clients should reload while waiting for initial | 94 # How often (in seconds) clients should reload while waiting for initial |
87 # results to load. | 95 # results to load. |
88 RELOAD_INTERVAL_UNTIL_READY = 10 | 96 RELOAD_INTERVAL_UNTIL_READY = 10 |
89 | 97 |
90 SUMMARY_TYPES = [ | 98 SUMMARY_TYPES = [ |
91 results_mod.KEY__HEADER__RESULTS_FAILURES, | 99 results_mod.KEY__HEADER__RESULTS_FAILURES, |
92 results_mod.KEY__HEADER__RESULTS_ALL, | 100 results_mod.KEY__HEADER__RESULTS_ALL, |
93 ] | 101 ] |
94 # If --compare-configs is specified, compare these configs. | 102 # If --compare-configs is specified, compare these configs. |
95 CONFIG_PAIRS_TO_COMPARE = [('8888', 'gpu')] | 103 CONFIG_PAIRS_TO_COMPARE = [('8888', 'gpu')] |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
158 '<!DOCTYPE html><html>' | 166 '<!DOCTYPE html><html>' |
159 '<head><title>rebaseline_server</title></head>' | 167 '<head><title>rebaseline_server</title></head>' |
160 '<body><ul>') | 168 '<body><ul>') |
161 if SUMMARY_TYPES: | 169 if SUMMARY_TYPES: |
162 file_handle.write('<li>Expectations vs Actuals</li><ul>') | 170 file_handle.write('<li>Expectations vs Actuals</li><ul>') |
163 for summary_type in SUMMARY_TYPES: | 171 for summary_type in SUMMARY_TYPES: |
164 file_handle.write( | 172 file_handle.write( |
165 '<li><a href="/{static_subdir}/view.html#/view.html?' | 173 '<li><a href="/{static_subdir}/view.html#/view.html?' |
166 'resultsToLoad=/{results_subdir}/{summary_type}">' | 174 'resultsToLoad=/{results_subdir}/{summary_type}">' |
167 '{summary_type}</a></li>'.format( | 175 '{summary_type}</a></li>'.format( |
168 results_subdir=RESULTS_SUBDIR, | 176 results_subdir=PRECOMPUTED_RESULTS_SUBDIR, |
169 static_subdir=STATIC_CONTENTS_SUBDIR, | 177 static_subdir=STATIC_CONTENTS_SUBDIR, |
170 summary_type=summary_type)) | 178 summary_type=summary_type)) |
171 file_handle.write('</ul>') | 179 file_handle.write('</ul>') |
172 if config_pairs: | 180 if config_pairs: |
173 file_handle.write('<li>Comparing configs within actual results</li><ul>') | 181 file_handle.write('<li>Comparing configs within actual results</li><ul>') |
174 for config_pair in config_pairs: | 182 for config_pair in config_pairs: |
175 file_handle.write('<li>%s vs %s:' % config_pair) | 183 file_handle.write('<li>%s vs %s:' % config_pair) |
176 for summary_type in SUMMARY_TYPES: | 184 for summary_type in SUMMARY_TYPES: |
177 file_handle.write( | 185 file_handle.write( |
178 ' <a href="/%s/view.html#/view.html?' | 186 ' <a href="/%s/view.html#/view.html?' |
179 'resultsToLoad=/%s/%s/%s-vs-%s_%s.json">%s</a>' % ( | 187 'resultsToLoad=/%s/%s/%s-vs-%s_%s.json">%s</a>' % ( |
180 STATIC_CONTENTS_SUBDIR, STATIC_CONTENTS_SUBDIR, | 188 STATIC_CONTENTS_SUBDIR, STATIC_CONTENTS_SUBDIR, |
181 GENERATED_JSON_SUBDIR, config_pair[0], config_pair[1], | 189 GENERATED_JSON_SUBDIR, config_pair[0], config_pair[1], |
182 summary_type, summary_type)) | 190 summary_type, summary_type)) |
183 file_handle.write('</li>') | 191 file_handle.write('</li>') |
184 file_handle.write('</ul>') | 192 file_handle.write('</ul>') |
185 file_handle.write('</ul></body></html>') | 193 file_handle.write('</ul></body></html>') |
186 | 194 |
187 | 195 |
188 class Server(object): | 196 class Server(object): |
189 """ HTTP server for our HTML rebaseline viewer. """ | 197 """ HTTP server for our HTML rebaseline viewer. """ |
190 | 198 |
191 def __init__(self, | 199 def __init__(self, |
192 actuals_dir=DEFAULT_ACTUALS_DIR, | 200 actuals_dir=DEFAULT_ACTUALS_DIR, |
193 json_filename=DEFAULT_JSON_FILENAME, | 201 json_filename=DEFAULT_JSON_FILENAME, |
194 gm_summaries_bucket=DEFAULT_GM_SUMMARIES_BUCKET, | 202 gm_summaries_bucket=DEFAULT_GM_SUMMARIES_BUCKET, |
195 port=DEFAULT_PORT, export=False, editable=True, | 203 port=DEFAULT_PORT, export=False, editable=True, |
196 reload_seconds=0, config_pairs=None, builder_regex_list=None): | 204 reload_seconds=0, config_pairs=None, builder_regex_list=None, |
| 205 boto_file_path=None, |
| 206 imagediffdb_threads=imagediffdb.DEFAULT_NUM_WORKER_THREADS): |
197 """ | 207 """ |
198 Args: | 208 Args: |
199 actuals_dir: directory under which we will check out the latest actual | 209 actuals_dir: directory under which we will check out the latest actual |
200 GM results | 210 GM results |
201 json_filename: basename of the JSON summary file to load for each builder | 211 json_filename: basename of the JSON summary file to load for each builder |
202 gm_summaries_bucket: Google Storage bucket to download json_filename | 212 gm_summaries_bucket: Google Storage bucket to download json_filename |
203 files from; if None or '', don't fetch new actual-results files | 213 files from; if None or '', don't fetch new actual-results files |
204 at all, just compare to whatever files are already in actuals_dir | 214 at all, just compare to whatever files are already in actuals_dir |
205 port: which TCP port to listen on for HTTP requests | 215 port: which TCP port to listen on for HTTP requests |
206 export: whether to allow HTTP clients on other hosts to access this server | 216 export: whether to allow HTTP clients on other hosts to access this server |
207 editable: whether HTTP clients are allowed to submit new baselines | 217 editable: whether HTTP clients are allowed to submit new baselines |
208 reload_seconds: polling interval with which to check for new results; | 218 reload_seconds: polling interval with which to check for new results; |
209 if 0, don't check for new results at all | 219 if 0, don't check for new results at all |
210 config_pairs: List of (string, string) tuples; for each tuple, compare | 220 config_pairs: List of (string, string) tuples; for each tuple, compare |
211 actual results of these two configs. If None or empty, | 221 actual results of these two configs. If None or empty, |
212 don't compare configs at all. | 222 don't compare configs at all. |
213 builder_regex_list: List of regular expressions specifying which builders | 223 builder_regex_list: List of regular expressions specifying which builders |
214 we will process. If None, process all builders. | 224 we will process. If None, process all builders. |
| 225 boto_file_path: Path to .boto file giving us credentials to access |
| 226 Google Storage buckets; if None, we will only be able to access |
| 227 public GS buckets. |
| 228 imagediffdb_threads: How many threads to spin up within imagediffdb. |
215 """ | 229 """ |
216 self._actuals_dir = actuals_dir | 230 self._actuals_dir = actuals_dir |
217 self._json_filename = json_filename | 231 self._json_filename = json_filename |
218 self._gm_summaries_bucket = gm_summaries_bucket | 232 self._gm_summaries_bucket = gm_summaries_bucket |
219 self._port = port | 233 self._port = port |
220 self._export = export | 234 self._export = export |
221 self._editable = editable | 235 self._editable = editable |
222 self._reload_seconds = reload_seconds | 236 self._reload_seconds = reload_seconds |
223 self._config_pairs = config_pairs or [] | 237 self._config_pairs = config_pairs or [] |
224 self._builder_regex_list = builder_regex_list | 238 self._builder_regex_list = builder_regex_list |
225 self._gs = gs_utils.GSUtils() | 239 self.truncate_results = False |
| 240 |
| 241 if boto_file_path: |
| 242 self._gs = gs_utils.GSUtils(boto_file_path=boto_file_path) |
| 243 else: |
| 244 self._gs = gs_utils.GSUtils() |
| 245 |
226 _create_index( | 246 _create_index( |
227 file_path=os.path.join( | 247 file_path=os.path.join( |
228 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_HTML_SUBDIR, | 248 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_HTML_SUBDIR, |
229 "index.html"), | 249 "index.html"), |
230 config_pairs=config_pairs) | 250 config_pairs=config_pairs) |
231 | 251 |
232 # Reentrant lock that must be held whenever updating EITHER of: | 252 # Reentrant lock that must be held whenever updating EITHER of: |
233 # 1. self._results | 253 # 1. self._results |
234 # 2. the expected or actual results on local disk | 254 # 2. the expected or actual results on local disk |
235 self.results_rlock = threading.RLock() | 255 self.results_rlock = threading.RLock() |
236 | 256 |
237 # These will be filled in by calls to update_results() | 257 # Create a single ImageDiffDB instance that is used by all our differs. |
| 258 self._image_diff_db = imagediffdb.ImageDiffDB( |
| 259 gs=self._gs, |
| 260 storage_root=os.path.join( |
| 261 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, |
| 262 GENERATED_IMAGES_SUBDIR), |
| 263 num_worker_threads=imagediffdb_threads) |
| 264 |
| 265 # This will be filled in by calls to update_results() |
238 self._results = None | 266 self._results = None |
239 self._image_diff_db = None | |
240 | 267 |
241 @property | 268 @property |
242 def results(self): | 269 def results(self): |
243 """ Returns the most recently generated results, or None if we don't have | 270 """ Returns the most recently generated results, or None if we don't have |
244 any valid results (update_results() has not completed yet). """ | 271 any valid results (update_results() has not completed yet). """ |
245 return self._results | 272 return self._results |
246 | 273 |
247 @property | 274 @property |
| 275 def image_diff_db(self): |
| 276 """ Returns reference to our ImageDiffDB object.""" |
| 277 return self._image_diff_db |
| 278 |
| 279 @property |
| 280 def gs(self): |
| 281 """ Returns reference to our GSUtils object.""" |
| 282 return self._gs |
| 283 |
| 284 @property |
248 def is_exported(self): | 285 def is_exported(self): |
249 """ Returns true iff HTTP clients on other hosts are allowed to access | 286 """ Returns true iff HTTP clients on other hosts are allowed to access |
250 this server. """ | 287 this server. """ |
251 return self._export | 288 return self._export |
252 | 289 |
253 @property | 290 @property |
254 def is_editable(self): | 291 def is_editable(self): |
255 """ Returns true iff HTTP clients are allowed to submit new baselines. """ | 292 """ Returns true iff HTTP clients are allowed to submit new baselines. """ |
256 return self._editable | 293 return self._editable |
257 | 294 |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
336 # updated expectations into a temp directory, and leaving the rest of | 373 # updated expectations into a temp directory, and leaving the rest of |
337 # the checkout alone. This could be done using "git show", or by | 374 # the checkout alone. This could be done using "git show", or by |
338 # downloading individual expectation JSON files from | 375 # downloading individual expectation JSON files from |
339 # skia.googlesource.com . | 376 # skia.googlesource.com . |
340 if self._reload_seconds: | 377 if self._reload_seconds: |
341 logging.info( | 378 logging.info( |
342 'Updating expected GM results in %s by syncing Skia repo ...' % | 379 'Updating expected GM results in %s by syncing Skia repo ...' % |
343 compare_to_expectations.DEFAULT_EXPECTATIONS_DIR) | 380 compare_to_expectations.DEFAULT_EXPECTATIONS_DIR) |
344 _run_command(['gclient', 'sync'], TRUNK_DIRECTORY) | 381 _run_command(['gclient', 'sync'], TRUNK_DIRECTORY) |
345 | 382 |
346 if not self._image_diff_db: | |
347 self._image_diff_db = imagediffdb.ImageDiffDB( | |
348 storage_root=os.path.join( | |
349 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, | |
350 GENERATED_IMAGES_SUBDIR)) | |
351 | |
352 self._results = compare_to_expectations.ExpectationComparisons( | 383 self._results = compare_to_expectations.ExpectationComparisons( |
353 image_diff_db=self._image_diff_db, | 384 image_diff_db=self._image_diff_db, |
354 actuals_root=self._actuals_dir, | 385 actuals_root=self._actuals_dir, |
355 diff_base_url=posixpath.join( | 386 diff_base_url=posixpath.join( |
356 os.pardir, STATIC_CONTENTS_SUBDIR, GENERATED_IMAGES_SUBDIR), | 387 os.pardir, STATIC_CONTENTS_SUBDIR, GENERATED_IMAGES_SUBDIR), |
357 builder_regex_list=self._builder_regex_list) | 388 builder_regex_list=self._builder_regex_list) |
358 | 389 |
359 json_dir = os.path.join( | 390 json_dir = os.path.join( |
360 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_JSON_SUBDIR) | 391 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_JSON_SUBDIR) |
361 if not os.path.isdir(json_dir): | 392 if not os.path.isdir(json_dir): |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
436 self.redirect_to('/%s/favicon.ico' % STATIC_CONTENTS_SUBDIR) | 467 self.redirect_to('/%s/favicon.ico' % STATIC_CONTENTS_SUBDIR) |
437 return | 468 return |
438 | 469 |
439 # All requests must be of this form: | 470 # All requests must be of this form: |
440 # /dispatcher/remainder | 471 # /dispatcher/remainder |
441 # where 'dispatcher' indicates which do_GET_* dispatcher to run | 472 # where 'dispatcher' indicates which do_GET_* dispatcher to run |
442 # and 'remainder' is the remaining path sent to that dispatcher. | 473 # and 'remainder' is the remaining path sent to that dispatcher. |
443 normpath = posixpath.normpath(self.path) | 474 normpath = posixpath.normpath(self.path) |
444 (dispatcher_name, remainder) = PATHSPLIT_RE.match(normpath).groups() | 475 (dispatcher_name, remainder) = PATHSPLIT_RE.match(normpath).groups() |
445 dispatchers = { | 476 dispatchers = { |
446 RESULTS_SUBDIR: self.do_GET_results, | 477 PRECOMPUTED_RESULTS_SUBDIR: self.do_GET_precomputed_results, |
| 478 LIVE_RESULTS_SUBDIR: self.do_GET_live_results, |
447 STATIC_CONTENTS_SUBDIR: self.do_GET_static, | 479 STATIC_CONTENTS_SUBDIR: self.do_GET_static, |
448 } | 480 } |
449 dispatcher = dispatchers[dispatcher_name] | 481 dispatcher = dispatchers[dispatcher_name] |
450 dispatcher(remainder) | 482 dispatcher(remainder) |
451 except: | 483 except: |
452 self.send_error(404) | 484 self.send_error(404) |
453 raise | 485 raise |
454 | 486 |
455 def do_GET_results(self, results_type): | 487 def do_GET_precomputed_results(self, results_type): |
456 """ Handle a GET request for GM results. | 488 """ Handle a GET request for part of the precomputed _SERVER.results object. |
457 | 489 |
458 Args: | 490 Args: |
459 results_type: string indicating which set of results to return; | 491 results_type: string indicating which set of results to return; |
460 must be one of the results_mod.RESULTS_* constants | 492 must be one of the results_mod.RESULTS_* constants |
461 """ | 493 """ |
462 logging.debug('do_GET_results: sending results of type "%s"' % results_type) | 494 logging.debug('do_GET_precomputed_results: sending results of type "%s"' % |
| 495 results_type) |
463 # Since we must make multiple calls to the ExpectationComparisons object, | 496 # Since we must make multiple calls to the ExpectationComparisons object, |
464 # grab a reference to it in case it is updated to point at a new | 497 # grab a reference to it in case it is updated to point at a new |
465 # ExpectationComparisons object within another thread. | 498 # ExpectationComparisons object within another thread. |
466 # | 499 # |
467 # TODO(epoger): Rather than using a global variable for the handler | 500 # TODO(epoger): Rather than using a global variable for the handler |
468 # to refer to the Server object, make Server a subclass of | 501 # to refer to the Server object, make Server a subclass of |
469 # HTTPServer, and then it could be available to the handler via | 502 # HTTPServer, and then it could be available to the handler via |
470 # the handler's .server instance variable. | 503 # the handler's .server instance variable. |
471 results_obj = _SERVER.results | 504 results_obj = _SERVER.results |
472 if results_obj: | 505 if results_obj: |
473 response_dict = results_obj.get_packaged_results_of_type( | 506 response_dict = results_obj.get_packaged_results_of_type( |
474 results_type=results_type, reload_seconds=_SERVER.reload_seconds, | 507 results_type=results_type, reload_seconds=_SERVER.reload_seconds, |
475 is_editable=_SERVER.is_editable, is_exported=_SERVER.is_exported) | 508 is_editable=_SERVER.is_editable, is_exported=_SERVER.is_exported) |
476 else: | 509 else: |
477 now = int(time.time()) | 510 now = int(time.time()) |
478 response_dict = { | 511 response_dict = { |
479 imagepairset.KEY__ROOT__HEADER: { | 512 imagepairset.KEY__ROOT__HEADER: { |
480 results_mod.KEY__HEADER__SCHEMA_VERSION: ( | 513 results_mod.KEY__HEADER__SCHEMA_VERSION: ( |
481 results_mod.VALUE__HEADER__SCHEMA_VERSION), | 514 results_mod.VALUE__HEADER__SCHEMA_VERSION), |
482 results_mod.KEY__HEADER__IS_STILL_LOADING: True, | 515 results_mod.KEY__HEADER__IS_STILL_LOADING: True, |
483 results_mod.KEY__HEADER__TIME_UPDATED: now, | 516 results_mod.KEY__HEADER__TIME_UPDATED: now, |
484 results_mod.KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE: ( | 517 results_mod.KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE: ( |
485 now + RELOAD_INTERVAL_UNTIL_READY), | 518 now + RELOAD_INTERVAL_UNTIL_READY), |
486 }, | 519 }, |
487 } | 520 } |
488 self.send_json_dict(response_dict) | 521 self.send_json_dict(response_dict) |
489 | 522 |
| 523 def do_GET_live_results(self, url_remainder): |
| 524 """ Handle a GET request for live-generated image diff data. |
| 525 |
| 526 Args: |
| 527 url_remainder: string indicating which image diffs to generate |
| 528 """ |
| 529 logging.debug('do_GET_live_results: url_remainder="%s"' % url_remainder) |
| 530 param_dict = urlparse.parse_qs(url_remainder) |
| 531 results_obj = compare_rendered_pictures.RenderedPicturesComparisons( |
| 532 actuals_dirs=param_dict[LIVE_PARAM__ACTUALS_DIR], |
| 533 expectations_dirs=param_dict[LIVE_PARAM__EXPECTATIONS_DIR], |
| 534 image_diff_db=_SERVER.image_diff_db, |
| 535 diff_base_url='/static/generated-images', |
| 536 gs=_SERVER.gs, truncate_results=_SERVER.truncate_results) |
| 537 self.send_json_dict(results_obj.get_packaged_results_of_type( |
| 538 results_mod.KEY__HEADER__RESULTS_ALL)) |
| 539 |
490 def do_GET_static(self, path): | 540 def do_GET_static(self, path): |
491 """ Handle a GET request for a file under STATIC_CONTENTS_SUBDIR . | 541 """ Handle a GET request for a file under STATIC_CONTENTS_SUBDIR . |
492 Only allow serving of files within STATIC_CONTENTS_SUBDIR that is a | 542 Only allow serving of files within STATIC_CONTENTS_SUBDIR that is a |
493 filesystem sibling of this script. | 543 filesystem sibling of this script. |
494 | 544 |
495 Args: | 545 Args: |
496 path: path to file (within STATIC_CONTENTS_SUBDIR) to retrieve | 546 path: path to file (within STATIC_CONTENTS_SUBDIR) to retrieve |
497 """ | 547 """ |
498 # Strip arguments ('?resultsToLoad=all') from the path | 548 # Strip arguments ('?resultsToLoad=all') from the path |
499 path = urlparse.urlparse(path).path | 549 path = urlparse.urlparse(path).path |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
636 def main(): | 686 def main(): |
637 logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', | 687 logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', |
638 datefmt='%m/%d/%Y %H:%M:%S', | 688 datefmt='%m/%d/%Y %H:%M:%S', |
639 level=logging.INFO) | 689 level=logging.INFO) |
640 parser = argparse.ArgumentParser() | 690 parser = argparse.ArgumentParser() |
641 parser.add_argument('--actuals-dir', | 691 parser.add_argument('--actuals-dir', |
642 help=('Directory into which we will check out the latest ' | 692 help=('Directory into which we will check out the latest ' |
643 'actual GM results. If this directory does not ' | 693 'actual GM results. If this directory does not ' |
644 'exist, it will be created. Defaults to %(default)s'), | 694 'exist, it will be created. Defaults to %(default)s'), |
645 default=DEFAULT_ACTUALS_DIR) | 695 default=DEFAULT_ACTUALS_DIR) |
| 696 parser.add_argument('--boto', |
| 697 help=('Path to .boto file giving us credentials to access ' |
| 698 'Google Storage buckets. If not specified, we will ' |
| 699 'only be able to access public GS buckets (and thus ' |
| 700 'won\'t be able to download SKP images).'), |
| 701 default='') |
646 # TODO(epoger): Before https://codereview.chromium.org/310093003 , | 702 # TODO(epoger): Before https://codereview.chromium.org/310093003 , |
647 # when this tool downloaded the JSON summaries from skia-autogen, | 703 # when this tool downloaded the JSON summaries from skia-autogen, |
648 # it had an --actuals-revision the caller could specify to download | 704 # it had an --actuals-revision the caller could specify to download |
649 # actual results as of a specific point in time. We should add similar | 705 # actual results as of a specific point in time. We should add similar |
650 # functionality when retrieving the summaries from Google Storage. | 706 # functionality when retrieving the summaries from Google Storage. |
651 parser.add_argument('--builders', metavar='BUILDER_REGEX', nargs='+', | 707 parser.add_argument('--builders', metavar='BUILDER_REGEX', nargs='+', |
652 help=('Only process builders matching these regular ' | 708 help=('Only process builders matching these regular ' |
653 'expressions. If unspecified, process all ' | 709 'expressions. If unspecified, process all ' |
654 'builders.')) | 710 'builders.')) |
655 parser.add_argument('--compare-configs', action='store_true', | 711 parser.add_argument('--compare-configs', action='store_true', |
(...skipping 25 matching lines...) Expand all Loading... |
681 'defaults to %(default)s'), | 737 'defaults to %(default)s'), |
682 default=DEFAULT_PORT) | 738 default=DEFAULT_PORT) |
683 parser.add_argument('--reload', type=int, | 739 parser.add_argument('--reload', type=int, |
684 help=('How often (a period in seconds) to update the ' | 740 help=('How often (a period in seconds) to update the ' |
685 'results. If specified, both expected and actual ' | 741 'results. If specified, both expected and actual ' |
686 'results will be updated by running "gclient sync" ' | 742 'results will be updated by running "gclient sync" ' |
687 'on your Skia checkout as a whole. ' | 743 'on your Skia checkout as a whole. ' |
688 'By default, we do not reload at all, and you ' | 744 'By default, we do not reload at all, and you ' |
689 'must restart the server to pick up new data.'), | 745 'must restart the server to pick up new data.'), |
690 default=0) | 746 default=0) |
| 747 parser.add_argument('--threads', type=int, |
| 748 help=('How many parallel threads we use to download ' |
| 749 'images and generate diffs; defaults to ' |
| 750 '%(default)s'), |
| 751 default=imagediffdb.DEFAULT_NUM_WORKER_THREADS) |
| 752 parser.add_argument('--truncate', action='store_true', |
| 753 help=('FOR TESTING ONLY: truncate the set of images we ' |
| 754 'process, to speed up testing.')) |
691 args = parser.parse_args() | 755 args = parser.parse_args() |
692 if args.compare_configs: | 756 if args.compare_configs: |
693 config_pairs = CONFIG_PAIRS_TO_COMPARE | 757 config_pairs = CONFIG_PAIRS_TO_COMPARE |
694 else: | 758 else: |
695 config_pairs = None | 759 config_pairs = None |
696 | 760 |
697 global _SERVER | 761 global _SERVER |
698 _SERVER = Server(actuals_dir=args.actuals_dir, | 762 _SERVER = Server(actuals_dir=args.actuals_dir, |
699 json_filename=args.json_filename, | 763 json_filename=args.json_filename, |
700 gm_summaries_bucket=args.gm_summaries_bucket, | 764 gm_summaries_bucket=args.gm_summaries_bucket, |
701 port=args.port, export=args.export, editable=args.editable, | 765 port=args.port, export=args.export, editable=args.editable, |
702 reload_seconds=args.reload, config_pairs=config_pairs, | 766 reload_seconds=args.reload, config_pairs=config_pairs, |
703 builder_regex_list=args.builders) | 767 builder_regex_list=args.builders, boto_file_path=args.boto, |
| 768 imagediffdb_threads=args.threads) |
| 769 if args.truncate: |
| 770 _SERVER.truncate_results = True |
704 _SERVER.run() | 771 _SERVER.run() |
705 | 772 |
706 | 773 |
707 if __name__ == '__main__': | 774 if __name__ == '__main__': |
708 main() | 775 main() |
OLD | NEW |