blob: 37f5fe1fb7f58a56788d93458463cf0e1901a7c7 [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 operator
import os
from autotest_lib.client.common_lib import error
from autotest_lib.server import test
from autotest_lib.server.cros.chameleon import display_client
class ChameleonTest(test.test):
"""This is the base class of Chameleon tests.
This base class initializes Chameleon board and its related services,
like connecting Chameleond and DisplayClient. Also kills the connections
on cleanup.
"""
def initialize(self, host):
"""Initializes.
@param host: The Host object of DUT.
"""
self.display_client = display_client.DisplayClient(host)
self.display_client.initialize()
self.chameleon = host.chameleon
self.chameleon_port = self._get_connected_port()
if self.chameleon_port is None:
raise error.TestError('DUT and Chameleon board not connected')
self._backup_edid()
def _backup_edid(self):
"""Backups the original EDID."""
self._original_edid = self.chameleon_port.read_edid()
self._original_edid_path = os.path.join(self.outputdir, 'original_edid')
with open(self._original_edid_path, 'w+') as f:
f.write(self._original_edid)
def _restore_edid(self):
"Restores the original EDID."""
current_edid = self.chameleon_port.read_edid()
if (hasattr(self, '_original_edid') and self._original_edid and
self._original_edid != current_edid):
logging.info('Restore the original EDID...')
self.chameleon_port.apply_edid(self._original_edid)
# Remove the original EDID file after restore.
os.remove(self._original_edid_path)
def cleanup(self):
"""Cleans up."""
# Unplug the Chameleon port, not to affect other test cases.
if hasattr(self, 'chameleon_port') and self.chameleon_port:
self.chameleon_port.unplug()
self._restore_edid()
if hasattr(self, 'display_client') and self.display_client:
self.display_client.cleanup()
def _get_connected_port(self):
"""Gets the first connected output port between Chameleon and DUT.
@return: A ChameleonPort object.
"""
self.chameleon.reset()
# TODO(waihong): Support multiple connectors.
for chameleon_port in self.chameleon.get_all_ports():
# Plug to ensure the connector is plugged.
chameleon_port.plug()
connector_type = chameleon_port.get_connector_type()
output = self.display_client.get_connector_name()
# TODO(waihong): Make sure eDP work in this way.
if output and output.startswith(connector_type):
return chameleon_port
# Unplug the port if it is not the connected.
chameleon_port.unplug()
return None
def check_screen_with_chameleon(self,
tag, pixel_diff_value_margin=0, total_wrong_pixels_margin=0):
"""Checks the DUT external screen with Chameleon.
1. Capture the whole screen from the display buffer of Chameleon.
2. Capture the framebuffer on DUT.
3. Verify that the captured screen match the content of DUT framebuffer.
@param tag: A string of tag for the prefix of output filenames.
@param pixel_diff_value_margin: The margin for comparing a pixel. Only
if a pixel difference exceeds this margin, will treat as a wrong
pixel.
@param total_wrong_pixels_margin: The margin for the number of wrong
pixels. If the total number of wrong pixels exceeds this margin,
the check fails.
@return: None if the check passes; otherwise, a string of error message.
"""
logging.info('Checking screen with Chameleon (tag: %s)...', tag)
chameleon_path = os.path.join(self.outputdir, '%s-chameleon.bgra' % tag)
dut_path = os.path.join(self.outputdir, '%s-dut.bgra' % tag)
logging.info('Capturing framebuffer on Chameleon.')
chameleon_pixels = self.chameleon_port.capture_screen(chameleon_path)
chameleon_pixels_len = len(chameleon_pixels)
logging.info('Capturing framebuffer on DUT.')
dut_pixels = self.display_client.capture_external_screen(dut_path)
dut_pixels_len = len(dut_pixels)
if chameleon_pixels_len != dut_pixels_len:
message = ('Result of %s: lengths of pixels not match: %d != %d' %
(tag, chameleon_pixels_len, dut_pixels_len))
logging.error(message)
return message
logging.info('Comparing the pixels...')
total_wrong_pixels = 0
# The dut_pixels array are formatted in BGRA.
for i in xrange(0, len(dut_pixels), 4):
# Skip the fourth byte, i.e. the alpha value.
chameleon_pixel = tuple(ord(p) for p in chameleon_pixels[i:i+3])
dut_pixel = tuple(ord(p) for p in dut_pixels[i:i+3])
# Compute the maximal difference for a pixel.
diff_value = max(map(abs, map(
operator.sub, chameleon_pixel, dut_pixel)))
if (diff_value > pixel_diff_value_margin):
if total_wrong_pixels == 0:
first_pixel_message = ('offset %d, %r != %r' %
(i, chameleon_pixel, dut_pixel))
total_wrong_pixels += 1
if total_wrong_pixels > 0:
message = ('Result of %s: total %d wrong pixels, e.g. %s' %
(tag, total_wrong_pixels, first_pixel_message))
if total_wrong_pixels > total_wrong_pixels_margin:
logging.error(message)
return message
else:
message += (', within the acceptable range %d' %
total_wrong_pixels_margin)
logging.warn(message)
else:
logging.info('Result of %s: all pixels match', tag)
for file_path in (chameleon_path, dut_path):
os.remove(file_path)
return None