1# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import os 7 8from autotest_lib.client.cros.video import method_logger 9from autotest_lib.client.common_lib import error 10 11 12class Verifier(object): 13 """ 14 Verifies that received screenshots are same as expected. 15 16 This class relies on a provided image comparer to decide if two images are 17 one and the same. 18 19 Clients who have many images to compare should use this class and pass in 20 a comparer of their choice. 21 22 Comparer are just about comparing two images and this class takes over with 23 test-related things: logging, deciding pass or fail. 24 25 """ 26 27 28 @method_logger.log 29 def __init__(self, image_comparer, stop_on_first_failure, threshold=0, 30 box=None): 31 """ 32 @param image_comparer: object, image comparer to use. 33 @param stop_on_first_failure: bool, true if the test should be stopped 34 once a test image doesn't match its ref. 35 @param threshold: int, a value which the pixel difference between test 36 image and golden image has to exceed before the 37 doublecheck comparer is used. 38 @param box: int tuple, left, upper, right, lower pixel coordinates 39 defining a box region within which the comparison is made. 40 41 """ 42 self.image_comparer = image_comparer 43 self.stop_on_first_failure = stop_on_first_failure 44 self.threshold = threshold 45 self.box = box 46 47 48 @method_logger.log 49 def verify(self, golden_image_paths, test_image_paths): 50 """ 51 Verifies that two sets of images are the same using provided comparer. 52 53 @param golden_image_paths: list of complete paths to golden images. 54 @param test_image_paths: list of complete paths to test images. 55 56 57 """ 58 59 if type(golden_image_paths) is not list: 60 golden_image_paths = [golden_image_paths] 61 62 if type(test_image_paths) is not list: 63 test_image_paths = [test_image_paths] 64 65 failure_count = 0 66 67 logging.debug("***BEGIN Image Verification***") 68 69 log_msgs = ["Threshold for diff pixel count = %d" % self.threshold] 70 71 test_run_comp_url = '' 72 73 for g_image, t_image in zip(golden_image_paths, test_image_paths): 74 75 with self.image_comparer: 76 comp_res = self.image_comparer.compare(g_image, 77 t_image, 78 self.box) 79 diff_pixels = comp_res.diff_pixel_count 80 81 """ 82 If remote comparer was used, compare() returns a comparison 83 url. 84 85 If remote comparer was not invoked (local comparer succeeded) 86 compare() returns '' as comparison url. The first one you get 87 will be the same for all since they are part of the same test 88 run. 89 """ 90 if test_run_comp_url == '' and comp_res.comparison_url != '': 91 test_run_comp_url = os.path.dirname(comp_res.comparison_url) 92 93 if diff_pixels > self.threshold: 94 failure_count += 1 95 96 log_msg = ("Image: %s. Pixel diff: %d." % 97 (os.path.basename(g_image), diff_pixels)) 98 99 logging.debug(log_msg) 100 log_msgs.append(log_msg) 101 102 if self.stop_on_first_failure: 103 raise error.TestError("%s. Bailing out." % log_msg) 104 105 if failure_count > 0: 106 cnt = len(golden_image_paths) 107 report_mes = ("*** WARNING: UI Based Image Comparison - Test " 108 "Failure typically needs further investigation ***\t" 109 "%d / %d test images differed substantially from the " 110 "golden images. Comparison url: %s. %s " 111 % (failure_count, 112 cnt, 113 test_run_comp_url, 114 '\t'.join(log_msgs))) 115 116 raise error.TestFail(report_mes) 117 118 logging.debug("***All Good.***")