Home | History | Annotate | Download | only in image_comparison
      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 
      5 import logging
      6 import os
      7 
      8 from autotest_lib.client.cros.video import method_logger
      9 from autotest_lib.client.common_lib import error
     10 
     11 
     12 class 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.***")