OLD | NEW |
---|---|
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 | 2 |
3 """ | 3 """ |
4 Copyright 2014 Google Inc. | 4 Copyright 2014 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 Compare results of two render_pictures runs. | 9 Compare results of two render_pictures runs. |
10 | 10 |
11 TODO(epoger): Start using this module to compare ALL images (whether they | 11 TODO(epoger): Start using this module to compare ALL images (whether they |
12 were generated from GMs or SKPs), and rename it accordingly. | 12 were generated from GMs or SKPs), and rename it accordingly. |
13 """ | 13 """ |
14 | 14 |
15 # System-level imports | 15 # System-level imports |
16 import logging | 16 import logging |
17 import os | 17 import os |
18 import shutil | 18 import shutil |
19 import subprocess | 19 import subprocess |
20 import tempfile | 20 import tempfile |
21 import time | 21 import time |
22 | 22 |
23 # Must fix up PYTHONPATH before importing from within Skia | 23 # Must fix up PYTHONPATH before importing from within Skia |
24 import fix_pythonpath # pylint: disable=W0611 | 24 import rs_fixpypath # pylint: disable=W0611 |
25 | 25 |
26 # Imports from within Skia | 26 # Imports from within Skia |
27 from py.utils import git_utils | 27 from py.utils import git_utils |
28 from py.utils import gs_utils | 28 from py.utils import gs_utils |
29 from py.utils import url_utils | 29 from py.utils import url_utils |
30 import buildbot_globals | 30 import buildbot_globals |
31 import column | 31 import column |
32 import gm_json | 32 import gm_json |
33 import imagediffdb | 33 import imagediffdb |
34 import imagepair | 34 import imagepair |
35 import imagepairset | 35 import imagepairset |
36 import results | 36 import results |
37 | 37 |
38 # URL under which all render_pictures images can be found in Google Storage. | 38 # URL under which all render_pictures images can be found in Google Storage. |
39 # | 39 # |
40 # TODO(epoger): In order to allow live-view of GMs and other images, read this | 40 # TODO(epoger): In order to allow live-view of GMs and other images, read this |
41 # from the input summary files, or allow the caller to set it within the | 41 # from the input summary files, or allow the caller to set it within the |
42 # GET_live_results call. | 42 # GET_live_results call. |
43 DEFAULT_IMAGE_BASE_GS_URL = 'gs://' + buildbot_globals.Get('skp_images_bucket') | 43 DEFAULT_IMAGE_BASE_GS_URL = 'gs://' + buildbot_globals.Get('skp_images_bucket') |
44 | 44 |
45 # Column descriptors, and display preferences for them. | 45 # Column descriptors, and display preferences for them. |
46 COLUMN__RESULT_TYPE = results.KEY__EXTRACOLUMNS__RESULT_TYPE | 46 COLUMN__RESULT_TYPE = results.KEY__EXTRACOLUMNS__RESULT_TYPE |
47 COLUMN__SOURCE_SKP = 'sourceSkpFile' | 47 COLUMN__SOURCE_SKP = 'sourceSkpFile' |
48 COLUMN__TILED_OR_WHOLE = 'tiledOrWhole' | 48 COLUMN__TILED_OR_WHOLE = 'tiledOrWhole' |
49 COLUMN__TILENUM = 'tilenum' | 49 COLUMN__TILENUM = 'tilenum' |
50 COLUMN__BUILDER_A = 'builderA' | |
epoger
2014/08/18 19:56:31
Interesting. One thing that's potentially tricky:
stephana
2014/08/18 20:24:53
IMO, if we are comparing output from two builders
epoger
2014/08/18 20:34:49
SGTM.
| |
51 COLUMN__RENDER_MODE_A = 'renderModeA' | |
52 COLUMN__BUILDER_B = 'builderB' | |
53 COLUMN__RENDER_MODE_B = 'renderModeB' | |
54 | |
50 FREEFORM_COLUMN_IDS = [ | 55 FREEFORM_COLUMN_IDS = [ |
51 COLUMN__SOURCE_SKP, | 56 COLUMN__SOURCE_SKP, |
52 COLUMN__TILENUM, | 57 COLUMN__TILENUM, |
53 ] | 58 ] |
54 ORDERED_COLUMN_IDS = [ | 59 ORDERED_COLUMN_IDS = [ |
55 COLUMN__RESULT_TYPE, | 60 COLUMN__RESULT_TYPE, |
56 COLUMN__SOURCE_SKP, | 61 COLUMN__SOURCE_SKP, |
57 COLUMN__TILED_OR_WHOLE, | 62 COLUMN__TILED_OR_WHOLE, |
58 COLUMN__TILENUM, | 63 COLUMN__TILENUM, |
64 COLUMN__BUILDER_A, | |
65 COLUMN__RENDER_MODE_A, | |
66 COLUMN__BUILDER_B, | |
67 COLUMN__RENDER_MODE_B, | |
59 ] | 68 ] |
60 | 69 |
61 # A special "repo:" URL type that we use to refer to Skia repo contents. | 70 # A special "repo:" URL type that we use to refer to Skia repo contents. |
62 # (Useful for comparing against expectations files we store in our repo.) | 71 # (Useful for comparing against expectations files we store in our repo.) |
63 REPO_URL_PREFIX = 'repo:' | 72 REPO_URL_PREFIX = 'repo:' |
64 REPO_BASEPATH = os.path.abspath(os.path.join( | 73 REPO_BASEPATH = os.path.abspath(os.path.join( |
65 os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir)) | 74 os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir)) |
66 | 75 |
67 # Which sections within a JSON summary file can contain results. | 76 # Which sections within a JSON summary file can contain results. |
68 ALLOWED_SECTION_NAMES = [ | 77 ALLOWED_SECTION_NAMES = [ |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
255 (dict_num, num_union_dict_paths, dict_path)) | 264 (dict_num, num_union_dict_paths, dict_path)) |
256 | 265 |
257 dictA = self.get_default(setA_dicts, None, dict_path) | 266 dictA = self.get_default(setA_dicts, None, dict_path) |
258 self._validate_dict_version(dictA) | 267 self._validate_dict_version(dictA) |
259 dictA_results = self.get_default(dictA, {}, setA_section) | 268 dictA_results = self.get_default(dictA, {}, setA_section) |
260 | 269 |
261 dictB = self.get_default(setB_dicts, None, dict_path) | 270 dictB = self.get_default(setB_dicts, None, dict_path) |
262 self._validate_dict_version(dictB) | 271 self._validate_dict_version(dictB) |
263 dictB_results = self.get_default(dictB, {}, setB_section) | 272 dictB_results = self.get_default(dictB, {}, setB_section) |
264 | 273 |
274 get2 = lambda dic, k: None if not dic else \ | |
epoger
2014/08/18 19:56:32
I think this provides a subset of functionality th
stephana
2014/08/18 20:24:53
Done.
| |
275 (dic.get(gm_json.JSONKEY_DESCRIPTIONS, {}) or {}).get(k, None) | |
276 builder_A = get2(dictA, gm_json.JSONKEY_DESCRIPTIONS_BUILDER) | |
277 render_mode_A = get2(dictA, gm_json.JSONKEY_DESCRIPTIONS_RENDER_MODE) | |
278 builder_B = get2(dictB, gm_json.JSONKEY_DESCRIPTIONS_BUILDER) | |
279 render_mode_B = get2(dictB, gm_json.JSONKEY_DESCRIPTIONS_RENDER_MODE) | |
280 | |
265 skp_names = sorted(set(dictA_results.keys() + dictB_results.keys())) | 281 skp_names = sorted(set(dictA_results.keys() + dictB_results.keys())) |
266 # Just for manual testing... truncate to an arbitrary subset. | 282 # Just for manual testing... truncate to an arbitrary subset. |
267 if self.truncate_results: | 283 if self.truncate_results: |
268 skp_names = skp_names[1:3] | 284 skp_names = skp_names[1:3] |
269 for skp_name in skp_names: | 285 for skp_name in skp_names: |
270 imagepairs_for_this_skp = [] | 286 imagepairs_for_this_skp = [] |
271 | 287 |
272 whole_image_A = self.get_default( | 288 whole_image_A = self.get_default( |
273 dictA_results, None, | 289 dictA_results, None, |
274 skp_name, gm_json.JSONKEY_SOURCE_WHOLEIMAGE) | 290 skp_name, gm_json.JSONKEY_SOURCE_WHOLEIMAGE) |
275 whole_image_B = self.get_default( | 291 whole_image_B = self.get_default( |
276 dictB_results, None, | 292 dictB_results, None, |
277 skp_name, gm_json.JSONKEY_SOURCE_WHOLEIMAGE) | 293 skp_name, gm_json.JSONKEY_SOURCE_WHOLEIMAGE) |
278 imagepairs_for_this_skp.append(self._create_image_pair( | 294 imagepairs_for_this_skp.append(self._create_image_pair( |
279 image_dict_A=whole_image_A, image_dict_B=whole_image_B, | 295 image_dict_A=whole_image_A, image_dict_B=whole_image_B, |
296 builder_A=builder_A, render_mode_A=render_mode_A, | |
297 builder_B=builder_B, render_mode_B=render_mode_B, | |
280 source_skp_name=skp_name, tilenum=None)) | 298 source_skp_name=skp_name, tilenum=None)) |
281 | 299 |
282 tiled_images_A = self.get_default( | 300 tiled_images_A = self.get_default( |
283 dictA_results, [], | 301 dictA_results, [], |
284 skp_name, gm_json.JSONKEY_SOURCE_TILEDIMAGES) | 302 skp_name, gm_json.JSONKEY_SOURCE_TILEDIMAGES) |
285 tiled_images_B = self.get_default( | 303 tiled_images_B = self.get_default( |
286 dictB_results, [], | 304 dictB_results, [], |
287 skp_name, gm_json.JSONKEY_SOURCE_TILEDIMAGES) | 305 skp_name, gm_json.JSONKEY_SOURCE_TILEDIMAGES) |
288 if tiled_images_A or tiled_images_B: | 306 if tiled_images_A or tiled_images_B: |
289 num_tiles_A = len(tiled_images_A) | 307 num_tiles_A = len(tiled_images_A) |
290 num_tiles_B = len(tiled_images_B) | 308 num_tiles_B = len(tiled_images_B) |
291 num_tiles = max(num_tiles_A, num_tiles_B) | 309 num_tiles = max(num_tiles_A, num_tiles_B) |
292 for tile_num in range(num_tiles): | 310 for tile_num in range(num_tiles): |
293 imagepairs_for_this_skp.append(self._create_image_pair( | 311 imagepairs_for_this_skp.append(self._create_image_pair( |
294 image_dict_A=(tiled_images_A[tile_num] | 312 image_dict_A=(tiled_images_A[tile_num] |
295 if tile_num < num_tiles_A else None), | 313 if tile_num < num_tiles_A else None), |
296 image_dict_B=(tiled_images_B[tile_num] | 314 image_dict_B=(tiled_images_B[tile_num] |
297 if tile_num < num_tiles_B else None), | 315 if tile_num < num_tiles_B else None), |
316 builder_A=builder_A, render_mode_A=render_mode_A, | |
317 builder_B=builder_B, render_mode_B=render_mode_B, | |
298 source_skp_name=skp_name, tilenum=tile_num)) | 318 source_skp_name=skp_name, tilenum=tile_num)) |
299 | 319 |
300 for one_imagepair in imagepairs_for_this_skp: | 320 for one_imagepair in imagepairs_for_this_skp: |
301 if one_imagepair: | 321 if one_imagepair: |
302 all_image_pairs.add_image_pair(one_imagepair) | 322 all_image_pairs.add_image_pair(one_imagepair) |
303 result_type = one_imagepair.extra_columns_dict\ | 323 result_type = one_imagepair.extra_columns_dict\ |
304 [COLUMN__RESULT_TYPE] | 324 [COLUMN__RESULT_TYPE] |
305 if result_type != results.KEY__RESULT_TYPE__SUCCEEDED: | 325 if result_type != results.KEY__RESULT_TYPE__SUCCEEDED: |
306 failing_image_pairs.add_image_pair(one_imagepair) | 326 failing_image_pairs.add_image_pair(one_imagepair) |
307 | 327 |
(...skipping 25 matching lines...) Expand all Loading... | |
333 header = result_dict[gm_json.JSONKEY_HEADER] | 353 header = result_dict[gm_json.JSONKEY_HEADER] |
334 header_type = header[gm_json.JSONKEY_HEADER_TYPE] | 354 header_type = header[gm_json.JSONKEY_HEADER_TYPE] |
335 if header_type != expected_header_type: | 355 if header_type != expected_header_type: |
336 raise Exception('expected header_type "%s", but got "%s"' % ( | 356 raise Exception('expected header_type "%s", but got "%s"' % ( |
337 expected_header_type, header_type)) | 357 expected_header_type, header_type)) |
338 header_revision = header[gm_json.JSONKEY_HEADER_REVISION] | 358 header_revision = header[gm_json.JSONKEY_HEADER_REVISION] |
339 if header_revision != expected_header_revision: | 359 if header_revision != expected_header_revision: |
340 raise Exception('expected header_revision %d, but got %d' % ( | 360 raise Exception('expected header_revision %d, but got %d' % ( |
341 expected_header_revision, header_revision)) | 361 expected_header_revision, header_revision)) |
342 | 362 |
343 def _create_image_pair(self, image_dict_A, image_dict_B, source_skp_name, | 363 def _create_image_pair(self, image_dict_A, image_dict_B, |
364 builder_A, render_mode_A, | |
365 builder_B, render_mode_B, | |
366 source_skp_name, | |
344 tilenum): | 367 tilenum): |
345 """Creates an ImagePair object for this pair of images. | 368 """Creates an ImagePair object for this pair of images. |
346 | 369 |
347 Args: | 370 Args: |
348 image_dict_A: dict with JSONKEY_IMAGE_* keys, or None if no image | 371 image_dict_A: dict with JSONKEY_IMAGE_* keys, or None if no image |
349 image_dict_B: dict with JSONKEY_IMAGE_* keys, or None if no image | 372 image_dict_B: dict with JSONKEY_IMAGE_* keys, or None if no image |
350 source_skp_name: string; name of the source SKP file | 373 source_skp_name: string; name of the source SKP file |
epoger
2014/08/18 19:56:32
please document the new args here
stephana
2014/08/18 20:24:53
Done.
| |
351 tilenum: which tile, or None if a wholeimage | 374 tilenum: which tile, or None if a wholeimage |
352 | 375 |
353 Returns: | 376 Returns: |
354 An ImagePair object, or None if both image_dict_A and image_dict_B are | 377 An ImagePair object, or None if both image_dict_A and image_dict_B are |
355 None. | 378 None. |
356 """ | 379 """ |
357 if (not image_dict_A) and (not image_dict_B): | 380 if (not image_dict_A) and (not image_dict_B): |
358 return None | 381 return None |
359 | 382 |
360 def _checksum_and_relative_url(dic): | 383 def _checksum_and_relative_url(dic): |
(...skipping 14 matching lines...) Expand all Loading... | |
375 elif not imageB_checksum: | 398 elif not imageB_checksum: |
376 result_type = results.KEY__RESULT_TYPE__NOCOMPARISON | 399 result_type = results.KEY__RESULT_TYPE__NOCOMPARISON |
377 elif imageA_checksum == imageB_checksum: | 400 elif imageA_checksum == imageB_checksum: |
378 result_type = results.KEY__RESULT_TYPE__SUCCEEDED | 401 result_type = results.KEY__RESULT_TYPE__SUCCEEDED |
379 else: | 402 else: |
380 result_type = results.KEY__RESULT_TYPE__FAILED | 403 result_type = results.KEY__RESULT_TYPE__FAILED |
381 | 404 |
382 extra_columns_dict = { | 405 extra_columns_dict = { |
383 COLUMN__RESULT_TYPE: result_type, | 406 COLUMN__RESULT_TYPE: result_type, |
384 COLUMN__SOURCE_SKP: source_skp_name, | 407 COLUMN__SOURCE_SKP: source_skp_name, |
408 COLUMN__BUILDER_A: builder_A, | |
409 COLUMN__RENDER_MODE_A: render_mode_A, | |
410 COLUMN__BUILDER_B: builder_B, | |
411 COLUMN__RENDER_MODE_B: render_mode_B, | |
385 } | 412 } |
386 if tilenum == None: | 413 if tilenum == None: |
387 extra_columns_dict[COLUMN__TILED_OR_WHOLE] = 'whole' | 414 extra_columns_dict[COLUMN__TILED_OR_WHOLE] = 'whole' |
388 extra_columns_dict[COLUMN__TILENUM] = 'N/A' | 415 extra_columns_dict[COLUMN__TILENUM] = 'N/A' |
389 else: | 416 else: |
390 extra_columns_dict[COLUMN__TILED_OR_WHOLE] = 'tiled' | 417 extra_columns_dict[COLUMN__TILED_OR_WHOLE] = 'tiled' |
391 extra_columns_dict[COLUMN__TILENUM] = str(tilenum) | 418 extra_columns_dict[COLUMN__TILENUM] = str(tilenum) |
392 | 419 |
393 try: | 420 try: |
394 return imagepair.ImagePair( | 421 return imagepair.ImagePair( |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
441 if source_dir.lower().startswith(REPO_URL_PREFIX): | 468 if source_dir.lower().startswith(REPO_URL_PREFIX): |
442 repo_dir = os.path.join(REPO_BASEPATH, source_dir[len(REPO_URL_PREFIX):]) | 469 repo_dir = os.path.join(REPO_BASEPATH, source_dir[len(REPO_URL_PREFIX):]) |
443 revision = subprocess.check_output( | 470 revision = subprocess.check_output( |
444 args=[git_utils.GIT, 'rev-parse', 'HEAD'], cwd=repo_dir).strip() | 471 args=[git_utils.GIT, 'rev-parse', 'HEAD'], cwd=repo_dir).strip() |
445 if assert_if_not and revision != assert_if_not: | 472 if assert_if_not and revision != assert_if_not: |
446 raise Exception('found revision %s that did not match %s' % ( | 473 raise Exception('found revision %s that did not match %s' % ( |
447 revision, assert_if_not)) | 474 revision, assert_if_not)) |
448 return revision | 475 return revision |
449 else: | 476 else: |
450 return None | 477 return None |
OLD | NEW |