blob: 61d5c849e5f1f565463eba300da718ad3b7fe09f [file] [log] [blame]
# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging
import os
from autotest_lib.client.cros.video import method_logger
from autotest_lib.client.common_lib import error
class Verifier(object):
"""
Verifies that received screenshots are same as expected.
This class relies on a provided image comparer to decide if two images are
one and the same.
Clients who have many images to compare should use this class and pass in
a comparer of their choice.
Comparer are just about comparing two images and this class takes over with
test-related things: logging, deciding pass or fail.
"""
@method_logger.log
def __init__(self, image_comparer, stop_on_first_failure, threshold=0,
box=None):
"""
@param image_comparer: object, image comparer to use.
@param stop_on_first_failure: bool, true if the test should be stopped
once a test image doesn't match its ref.
@param threshold: int, a value which the pixel difference between test
image and golden image has to exceed before the
doublecheck comparer is used.
@param box: int tuple, left, upper, right, lower pixel coordinates
defining a box region within which the comparison is made.
"""
self.image_comparer = image_comparer
self.stop_on_first_failure = stop_on_first_failure
self.threshold = threshold
self.box = box
@method_logger.log
def verify(self, golden_image_paths, test_image_paths):
"""
Verifies that two sets of images are the same using provided comparer.
@param golden_image_paths: list of complete paths to golden images.
@param test_image_paths: list of complete paths to test images.
"""
if type(golden_image_paths) is not list:
golden_image_paths = [golden_image_paths]
if type(test_image_paths) is not list:
test_image_paths = [test_image_paths]
failure_count = 0
logging.debug("***BEGIN Image Verification***")
log_msgs = ["Threshold for diff pixel count = %d" % self.threshold]
test_run_comp_url = ''
for g_image, t_image in zip(golden_image_paths, test_image_paths):
with self.image_comparer:
comp_res = self.image_comparer.compare(g_image,
t_image,
self.box)
diff_pixels = comp_res.diff_pixel_count
"""
If biopic was used, compare() returns a comparison url of this
form https://biopic.corp.google.com/a/b/this_comparison_result/
We want this: https://biopic.corp.google.com/a/b, so we can see
links for the entire test run (many comparison urls).
If biopic was not invoked (local comparer succeeded) compare()
returns '' as comparison url. The first one you get will be
the same for all since they are part of the same test run.
"""
#TODO(mussa): Use the more robust urlparse.ParseResult if
# biopic URL scheme changes
if test_run_comp_url == '' and comp_res.comparison_url != '':
test_run_comp_url = os.path.dirname(comp_res.comparison_url)
if diff_pixels > self.threshold:
failure_count += 1
log_msg = ("Image: %s. Pixel diff: %d." %
(os.path.basename(g_image), diff_pixels))
logging.debug(log_msg)
log_msgs.append(log_msg)
if self.stop_on_first_failure:
raise error.TestError("%s. Bailing out." % log_msg)
if failure_count > 0:
cnt = len(golden_image_paths)
report_mes = ("*** WARNING: UI Based Image Comparison - Test "
"Failure typically needs further investigation ***\t"
"%d / %d test images differed substantially from the "
"golden images. Comparison url: %s. %s "
% (failure_count,
cnt,
test_run_comp_url,
'\t'.join(log_msgs)))
raise error.TestFail(report_mes)
logging.debug("***All Good.***")