Chromium Code Reviews| 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 Calulate differences between image pairs, and store them in a database. | 9 Calulate differences between image pairs, and store them in a database. |
| 10 """ | 10 """ |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 94 expected_image_locator = _sanitize_locator(expected_image_locator) | 94 expected_image_locator = _sanitize_locator(expected_image_locator) |
| 95 actual_image_locator = _sanitize_locator(actual_image_locator) | 95 actual_image_locator = _sanitize_locator(actual_image_locator) |
| 96 | 96 |
| 97 # Download the expected/actual images, if we don't have them already. | 97 # Download the expected/actual images, if we don't have them already. |
| 98 expected_image_file = os.path.join( | 98 expected_image_file = os.path.join( |
| 99 storage_root, expected_images_subdir, | 99 storage_root, expected_images_subdir, |
| 100 str(expected_image_locator) + image_suffix) | 100 str(expected_image_locator) + image_suffix) |
| 101 actual_image_file = os.path.join( | 101 actual_image_file = os.path.join( |
| 102 storage_root, actual_images_subdir, | 102 storage_root, actual_images_subdir, |
| 103 str(actual_image_locator) + image_suffix) | 103 str(actual_image_locator) + image_suffix) |
| 104 try: | 104 for image_file, image_url in [ |
| 105 _download_file(gs, expected_image_file, expected_image_url) | 105 (expected_image_file, expected_image_url), |
| 106 except Exception: | 106 (actual_image_file, actual_image_url)]: |
| 107 logging.exception('unable to download expected_image_url %s to file %s' % | 107 if image_file and image_url: |
| 108 (expected_image_url, expected_image_file)) | 108 try: |
| 109 raise | 109 _download_file(gs, image_file, image_url) |
| 110 try: | 110 except Exception: |
| 111 _download_file(gs, actual_image_file, actual_image_url) | 111 logging.exception('unable to download image_url %s to file %s' % |
| 112 except Exception: | 112 (image_url, image_file)) |
| 113 logging.exception('unable to download actual_image_url %s to file %s' % | 113 raise |
| 114 (actual_image_url, actual_image_file)) | |
| 115 raise | |
| 116 | 114 |
| 117 # Get all diff images and values from skpdiff binary. | 115 # Return early if we do not need to generate diffs. |
| 116 if (expected_image_url == actual_image_url or | |
| 117 not expected_image_url or not actual_image_url): | |
| 118 return | |
| 119 | |
| 120 # Get all diff images and values using the skpdiff binary. | |
| 118 skpdiff_output_dir = tempfile.mkdtemp() | 121 skpdiff_output_dir = tempfile.mkdtemp() |
| 119 try: | 122 try: |
| 120 skpdiff_summary_file = os.path.join(skpdiff_output_dir, | 123 skpdiff_summary_file = os.path.join(skpdiff_output_dir, |
| 121 'skpdiff-output.json') | 124 'skpdiff-output.json') |
| 122 skpdiff_rgbdiff_dir = os.path.join(skpdiff_output_dir, 'rgbDiff') | 125 skpdiff_rgbdiff_dir = os.path.join(skpdiff_output_dir, 'rgbDiff') |
| 123 skpdiff_whitediff_dir = os.path.join(skpdiff_output_dir, 'whiteDiff') | 126 skpdiff_whitediff_dir = os.path.join(skpdiff_output_dir, 'whiteDiff') |
| 124 expected_img = os.path.join(storage_root, expected_images_subdir, | |
| 125 str(expected_image_locator) + image_suffix) | |
| 126 actual_img = os.path.join(storage_root, actual_images_subdir, | |
| 127 str(actual_image_locator) + image_suffix) | |
| 128 | 127 |
| 129 # TODO(epoger): Consider calling skpdiff ONCE for all image pairs, | 128 # TODO(epoger): Consider calling skpdiff ONCE for all image pairs, |
| 130 # instead of calling it separately for each image pair. | 129 # instead of calling it separately for each image pair. |
| 131 # Pro: we'll incur less overhead from making repeated system calls, | 130 # Pro: we'll incur less overhead from making repeated system calls, |
| 132 # spinning up the skpdiff binary, etc. | 131 # spinning up the skpdiff binary, etc. |
| 133 # Con: we would have to wait until all image pairs were loaded before | 132 # Con: we would have to wait until all image pairs were loaded before |
| 134 # generating any of the diffs? | 133 # generating any of the diffs? |
| 135 find_run_binary.run_command( | 134 find_run_binary.run_command( |
| 136 [SKPDIFF_BINARY, '-p', expected_img, actual_img, | 135 [SKPDIFF_BINARY, '-p', expected_image_file, actual_image_file, |
| 137 '--jsonp', 'false', | 136 '--jsonp', 'false', |
| 138 '--output', skpdiff_summary_file, | 137 '--output', skpdiff_summary_file, |
| 139 '--differs', 'perceptual', 'different_pixels', | 138 '--differs', 'perceptual', 'different_pixels', |
| 140 '--rgbDiffDir', skpdiff_rgbdiff_dir, | 139 '--rgbDiffDir', skpdiff_rgbdiff_dir, |
| 141 '--whiteDiffDir', skpdiff_whitediff_dir, | 140 '--whiteDiffDir', skpdiff_whitediff_dir, |
| 142 ]) | 141 ]) |
| 143 | 142 |
| 144 # Get information out of the skpdiff_summary_file. | 143 # Get information out of the skpdiff_summary_file. |
| 145 with contextlib.closing(open(skpdiff_summary_file)) as fp: | 144 with contextlib.closing(open(skpdiff_summary_file)) as fp: |
| 146 data = json.load(fp) | 145 data = json.load(fp) |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 254 # previously downloaded to local disk. | 253 # previously downloaded to local disk. |
| 255 # I guess we should figure out how expensive it is to download vs diff the | 254 # I guess we should figure out how expensive it is to download vs diff the |
| 256 # image pairs... if diffing them is expensive too, we can write these | 255 # image pairs... if diffing them is expensive too, we can write these |
| 257 # _diff_dict objects out to disk if there's too many to hold in RAM. | 256 # _diff_dict objects out to disk if there's too many to hold in RAM. |
| 258 # Or we could use virtual memory to handle that automatically. | 257 # Or we could use virtual memory to handle that automatically. |
| 259 self._diff_dict = {} | 258 self._diff_dict = {} |
| 260 self._diff_dict_writelock = threading.RLock() | 259 self._diff_dict_writelock = threading.RLock() |
| 261 | 260 |
| 262 # Set up the queue for asynchronously loading DiffRecords, and start the | 261 # Set up the queue for asynchronously loading DiffRecords, and start the |
| 263 # worker threads reading from it. | 262 # worker threads reading from it. |
| 264 self._tasks_queue = Queue.Queue(maxsize=2*num_worker_threads) | 263 # The queue maxsize must be 0 (infinite size queue), so that asynchronous |
| 264 # calls can return as soon as possible. | |
| 265 self._tasks_queue = Queue.Queue(maxsize=0) | |
| 265 self._workers = [] | 266 self._workers = [] |
| 266 for i in range(num_worker_threads): | 267 for i in range(num_worker_threads): |
| 267 worker = threading.Thread(target=self.worker, args=(i,)) | 268 worker = threading.Thread(target=self.worker, args=(i,)) |
| 268 worker.daemon = True | 269 worker.daemon = True |
| 269 worker.start() | 270 worker.start() |
| 270 self._workers.append(worker) | 271 self._workers.append(worker) |
| 271 | 272 |
| 272 def worker(self, worker_num): | 273 def worker(self, worker_num): |
| 273 """Launch a worker thread that pulls tasks off self._tasks_queue. | 274 """Launch a worker thread that pulls tasks off self._tasks_queue. |
| 274 | 275 |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 303 expected_image_url, expected_image_locator, | 304 expected_image_url, expected_image_locator, |
| 304 actual_image_url, actual_image_locator): | 305 actual_image_url, actual_image_locator): |
| 305 """Asynchronously prepare a DiffRecord for a pair of images. | 306 """Asynchronously prepare a DiffRecord for a pair of images. |
| 306 | 307 |
| 307 This method will return quickly; calls to get_diff_record() will block | 308 This method will return quickly; calls to get_diff_record() will block |
| 308 until the DiffRecord is available (or we have given up on creating it). | 309 until the DiffRecord is available (or we have given up on creating it). |
| 309 | 310 |
| 310 If we already have a DiffRecord for this particular image pair, no work | 311 If we already have a DiffRecord for this particular image pair, no work |
| 311 will be done. | 312 will be done. |
| 312 | 313 |
| 314 If expected_image_url (or its locator) is None, just download actual_image. | |
| 315 If actual_image_url (or its locator) is None, just download expected_image. | |
| 316 | |
| 313 Args: | 317 Args: |
| 314 expected_image_url: file, GS, or HTTP url from which we will download the | 318 expected_image_url: file, GS, or HTTP url from which we will download the |
| 315 expected image | 319 expected image |
| 316 expected_image_locator: a unique ID string under which we will store the | 320 expected_image_locator: a unique ID string under which we will store the |
| 317 expected image within storage_root (probably including a checksum to | 321 expected image within storage_root (probably including a checksum to |
| 318 guarantee uniqueness) | 322 guarantee uniqueness) |
| 319 actual_image_url: file, GS, or HTTP url from which we will download the | 323 actual_image_url: file, GS, or HTTP url from which we will download the |
| 320 actual image | 324 actual image |
| 321 actual_image_locator: a unique ID string under which we will store the | 325 actual_image_locator: a unique ID string under which we will store the |
| 322 actual image within storage_root (probably including a checksum to | 326 actual image within storage_root (probably including a checksum to |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 430 pass | 434 pass |
| 431 | 435 |
| 432 | 436 |
| 433 def _sanitize_locator(locator): | 437 def _sanitize_locator(locator): |
| 434 """Returns a sanitized version of a locator (one in which we know none of the | 438 """Returns a sanitized version of a locator (one in which we know none of the |
| 435 characters will have special meaning in filenames). | 439 characters will have special meaning in filenames). |
| 436 | 440 |
| 437 Args: | 441 Args: |
| 438 locator: string, or something that can be represented as a string | 442 locator: string, or something that can be represented as a string |
| 439 """ | 443 """ |
| 440 return DISALLOWED_FILEPATH_CHAR_REGEX.sub('_', str(locator)) | 444 if locator: |
| 441 | 445 return DISALLOWED_FILEPATH_CHAR_REGEX.sub('_', str(locator)) |
| 446 else: | |
| 447 return locator | |
|
rmistry
2014/08/06 17:49:01
Is it ok for this to be an empty string or None? s
epoger
2014/08/06 17:53:34
We actually need to allow for None. Added a comme
| |
| 442 | 448 |
| 443 def _get_difference_locator(expected_image_locator, actual_image_locator): | 449 def _get_difference_locator(expected_image_locator, actual_image_locator): |
| 444 """Returns the locator string used to look up the diffs between expected_image | 450 """Returns the locator string used to look up the diffs between expected_image |
| 445 and actual_image. | 451 and actual_image. |
| 446 | 452 |
| 447 We must keep this function in sync with getImageDiffRelativeUrl() in | 453 We must keep this function in sync with getImageDiffRelativeUrl() in |
| 448 static/loader.js | 454 static/loader.js |
| 449 | 455 |
| 450 Args: | 456 Args: |
| 451 expected_image_locator: locator string pointing at expected image | 457 expected_image_locator: locator string pointing at expected image |
| 452 actual_image_locator: locator string pointing at actual image | 458 actual_image_locator: locator string pointing at actual image |
| 453 | 459 |
| 454 Returns: already-sanitized locator where the diffs between expected and | 460 Returns: already-sanitized locator where the diffs between expected and |
| 455 actual images can be found | 461 actual images can be found |
| 456 """ | 462 """ |
| 457 return "%s-vs-%s" % (_sanitize_locator(expected_image_locator), | 463 return "%s-vs-%s" % (_sanitize_locator(expected_image_locator), |
| 458 _sanitize_locator(actual_image_locator)) | 464 _sanitize_locator(actual_image_locator)) |
| OLD | NEW |