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' | |
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 # get the builders and render modes for each set | |
275 builder_A = self.get_default(dictA, None, | |
epoger
2014/08/18 20:34:49
Ravi will cite https://engdoc.corp.google.com/eng/
| |
276 gm_json.JSONKEY_DESCRIPTIONS, | |
277 gm_json.JSONKEY_DESCRIPTIONS_BUILDER) | |
278 render_mode_A = self.get_default(dictA, None, | |
279 gm_json.JSONKEY_DESCRIPTIONS, | |
280 gm_json.JSONKEY_DESCRIPTIONS_RENDER_MODE) | |
281 builder_B = self.get_default(dictB, None, | |
282 gm_json.JSONKEY_DESCRIPTIONS, | |
283 gm_json.JSONKEY_DESCRIPTIONS_BUILDER) | |
284 render_mode_B = self.get_default(dictB, None, | |
285 gm_json.JSONKEY_DESCRIPTIONS, | |
286 gm_json.JSONKEY_DESCRIPTIONS_RENDER_MODE) | |
287 | |
265 skp_names = sorted(set(dictA_results.keys() + dictB_results.keys())) | 288 skp_names = sorted(set(dictA_results.keys() + dictB_results.keys())) |
266 # Just for manual testing... truncate to an arbitrary subset. | 289 # Just for manual testing... truncate to an arbitrary subset. |
267 if self.truncate_results: | 290 if self.truncate_results: |
268 skp_names = skp_names[1:3] | 291 skp_names = skp_names[1:3] |
269 for skp_name in skp_names: | 292 for skp_name in skp_names: |
270 imagepairs_for_this_skp = [] | 293 imagepairs_for_this_skp = [] |
271 | 294 |
272 whole_image_A = self.get_default( | 295 whole_image_A = self.get_default( |
273 dictA_results, None, | 296 dictA_results, None, |
274 skp_name, gm_json.JSONKEY_SOURCE_WHOLEIMAGE) | 297 skp_name, gm_json.JSONKEY_SOURCE_WHOLEIMAGE) |
275 whole_image_B = self.get_default( | 298 whole_image_B = self.get_default( |
276 dictB_results, None, | 299 dictB_results, None, |
277 skp_name, gm_json.JSONKEY_SOURCE_WHOLEIMAGE) | 300 skp_name, gm_json.JSONKEY_SOURCE_WHOLEIMAGE) |
278 imagepairs_for_this_skp.append(self._create_image_pair( | 301 imagepairs_for_this_skp.append(self._create_image_pair( |
279 image_dict_A=whole_image_A, image_dict_B=whole_image_B, | 302 image_dict_A=whole_image_A, image_dict_B=whole_image_B, |
303 builder_A=builder_A, render_mode_A=render_mode_A, | |
304 builder_B=builder_B, render_mode_B=render_mode_B, | |
280 source_skp_name=skp_name, tilenum=None)) | 305 source_skp_name=skp_name, tilenum=None)) |
281 | 306 |
282 tiled_images_A = self.get_default( | 307 tiled_images_A = self.get_default( |
283 dictA_results, [], | 308 dictA_results, [], |
284 skp_name, gm_json.JSONKEY_SOURCE_TILEDIMAGES) | 309 skp_name, gm_json.JSONKEY_SOURCE_TILEDIMAGES) |
285 tiled_images_B = self.get_default( | 310 tiled_images_B = self.get_default( |
286 dictB_results, [], | 311 dictB_results, [], |
287 skp_name, gm_json.JSONKEY_SOURCE_TILEDIMAGES) | 312 skp_name, gm_json.JSONKEY_SOURCE_TILEDIMAGES) |
288 if tiled_images_A or tiled_images_B: | 313 if tiled_images_A or tiled_images_B: |
289 num_tiles_A = len(tiled_images_A) | 314 num_tiles_A = len(tiled_images_A) |
290 num_tiles_B = len(tiled_images_B) | 315 num_tiles_B = len(tiled_images_B) |
291 num_tiles = max(num_tiles_A, num_tiles_B) | 316 num_tiles = max(num_tiles_A, num_tiles_B) |
292 for tile_num in range(num_tiles): | 317 for tile_num in range(num_tiles): |
293 imagepairs_for_this_skp.append(self._create_image_pair( | 318 imagepairs_for_this_skp.append(self._create_image_pair( |
294 image_dict_A=(tiled_images_A[tile_num] | 319 image_dict_A=(tiled_images_A[tile_num] |
295 if tile_num < num_tiles_A else None), | 320 if tile_num < num_tiles_A else None), |
296 image_dict_B=(tiled_images_B[tile_num] | 321 image_dict_B=(tiled_images_B[tile_num] |
297 if tile_num < num_tiles_B else None), | 322 if tile_num < num_tiles_B else None), |
323 builder_A=builder_A, render_mode_A=render_mode_A, | |
324 builder_B=builder_B, render_mode_B=render_mode_B, | |
298 source_skp_name=skp_name, tilenum=tile_num)) | 325 source_skp_name=skp_name, tilenum=tile_num)) |
299 | 326 |
300 for one_imagepair in imagepairs_for_this_skp: | 327 for one_imagepair in imagepairs_for_this_skp: |
301 if one_imagepair: | 328 if one_imagepair: |
302 all_image_pairs.add_image_pair(one_imagepair) | 329 all_image_pairs.add_image_pair(one_imagepair) |
303 result_type = one_imagepair.extra_columns_dict\ | 330 result_type = one_imagepair.extra_columns_dict\ |
304 [COLUMN__RESULT_TYPE] | 331 [COLUMN__RESULT_TYPE] |
305 if result_type != results.KEY__RESULT_TYPE__SUCCEEDED: | 332 if result_type != results.KEY__RESULT_TYPE__SUCCEEDED: |
306 failing_image_pairs.add_image_pair(one_imagepair) | 333 failing_image_pairs.add_image_pair(one_imagepair) |
307 | 334 |
(...skipping 25 matching lines...) Expand all Loading... | |
333 header = result_dict[gm_json.JSONKEY_HEADER] | 360 header = result_dict[gm_json.JSONKEY_HEADER] |
334 header_type = header[gm_json.JSONKEY_HEADER_TYPE] | 361 header_type = header[gm_json.JSONKEY_HEADER_TYPE] |
335 if header_type != expected_header_type: | 362 if header_type != expected_header_type: |
336 raise Exception('expected header_type "%s", but got "%s"' % ( | 363 raise Exception('expected header_type "%s", but got "%s"' % ( |
337 expected_header_type, header_type)) | 364 expected_header_type, header_type)) |
338 header_revision = header[gm_json.JSONKEY_HEADER_REVISION] | 365 header_revision = header[gm_json.JSONKEY_HEADER_REVISION] |
339 if header_revision != expected_header_revision: | 366 if header_revision != expected_header_revision: |
340 raise Exception('expected header_revision %d, but got %d' % ( | 367 raise Exception('expected header_revision %d, but got %d' % ( |
341 expected_header_revision, header_revision)) | 368 expected_header_revision, header_revision)) |
342 | 369 |
343 def _create_image_pair(self, image_dict_A, image_dict_B, source_skp_name, | 370 def _create_image_pair(self, image_dict_A, image_dict_B, |
371 builder_A, render_mode_A, | |
372 builder_B, render_mode_B, | |
373 source_skp_name, | |
344 tilenum): | 374 tilenum): |
345 """Creates an ImagePair object for this pair of images. | 375 """Creates an ImagePair object for this pair of images. |
346 | 376 |
347 Args: | 377 Args: |
348 image_dict_A: dict with JSONKEY_IMAGE_* keys, or None if no image | 378 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 | 379 image_dict_B: dict with JSONKEY_IMAGE_* keys, or None if no image |
380 builder_A: builder that created image set A or None if unknow | |
381 render_mode_A: render mode used to generate image set A or None if | |
382 unknown. | |
383 builder_B: builder that created image set A or None if unknow | |
384 render_mode_B: render mode used to generate image set A or None if | |
385 unknown. | |
350 source_skp_name: string; name of the source SKP file | 386 source_skp_name: string; name of the source SKP file |
351 tilenum: which tile, or None if a wholeimage | 387 tilenum: which tile, or None if a wholeimage |
352 | 388 |
353 Returns: | 389 Returns: |
354 An ImagePair object, or None if both image_dict_A and image_dict_B are | 390 An ImagePair object, or None if both image_dict_A and image_dict_B are |
355 None. | 391 None. |
356 """ | 392 """ |
357 if (not image_dict_A) and (not image_dict_B): | 393 if (not image_dict_A) and (not image_dict_B): |
358 return None | 394 return None |
359 | 395 |
(...skipping 15 matching lines...) Expand all Loading... | |
375 elif not imageB_checksum: | 411 elif not imageB_checksum: |
376 result_type = results.KEY__RESULT_TYPE__NOCOMPARISON | 412 result_type = results.KEY__RESULT_TYPE__NOCOMPARISON |
377 elif imageA_checksum == imageB_checksum: | 413 elif imageA_checksum == imageB_checksum: |
378 result_type = results.KEY__RESULT_TYPE__SUCCEEDED | 414 result_type = results.KEY__RESULT_TYPE__SUCCEEDED |
379 else: | 415 else: |
380 result_type = results.KEY__RESULT_TYPE__FAILED | 416 result_type = results.KEY__RESULT_TYPE__FAILED |
381 | 417 |
382 extra_columns_dict = { | 418 extra_columns_dict = { |
383 COLUMN__RESULT_TYPE: result_type, | 419 COLUMN__RESULT_TYPE: result_type, |
384 COLUMN__SOURCE_SKP: source_skp_name, | 420 COLUMN__SOURCE_SKP: source_skp_name, |
421 COLUMN__BUILDER_A: builder_A, | |
422 COLUMN__RENDER_MODE_A: render_mode_A, | |
423 COLUMN__BUILDER_B: builder_B, | |
424 COLUMN__RENDER_MODE_B: render_mode_B, | |
385 } | 425 } |
386 if tilenum == None: | 426 if tilenum == None: |
387 extra_columns_dict[COLUMN__TILED_OR_WHOLE] = 'whole' | 427 extra_columns_dict[COLUMN__TILED_OR_WHOLE] = 'whole' |
388 extra_columns_dict[COLUMN__TILENUM] = 'N/A' | 428 extra_columns_dict[COLUMN__TILENUM] = 'N/A' |
389 else: | 429 else: |
390 extra_columns_dict[COLUMN__TILED_OR_WHOLE] = 'tiled' | 430 extra_columns_dict[COLUMN__TILED_OR_WHOLE] = 'tiled' |
391 extra_columns_dict[COLUMN__TILENUM] = str(tilenum) | 431 extra_columns_dict[COLUMN__TILENUM] = str(tilenum) |
392 | 432 |
393 try: | 433 try: |
394 return imagepair.ImagePair( | 434 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): | 481 if source_dir.lower().startswith(REPO_URL_PREFIX): |
442 repo_dir = os.path.join(REPO_BASEPATH, source_dir[len(REPO_URL_PREFIX):]) | 482 repo_dir = os.path.join(REPO_BASEPATH, source_dir[len(REPO_URL_PREFIX):]) |
443 revision = subprocess.check_output( | 483 revision = subprocess.check_output( |
444 args=[git_utils.GIT, 'rev-parse', 'HEAD'], cwd=repo_dir).strip() | 484 args=[git_utils.GIT, 'rev-parse', 'HEAD'], cwd=repo_dir).strip() |
445 if assert_if_not and revision != assert_if_not: | 485 if assert_if_not and revision != assert_if_not: |
446 raise Exception('found revision %s that did not match %s' % ( | 486 raise Exception('found revision %s that did not match %s' % ( |
447 revision, assert_if_not)) | 487 revision, assert_if_not)) |
448 return revision | 488 return revision |
449 else: | 489 else: |
450 return None | 490 return None |
OLD | NEW |