blob: 7739b31536fa315135574e23ea3386fa438cb89c [file] [log] [blame]
# Copyright 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.
"""This is a display end-to-end test using the Chameleon board."""
import logging, os, shutil, time
from autotest_lib.client.bin import utils
from autotest_lib.client.common_lib import error
from autotest_lib.client.cros.chameleon import chameleon_port_finder
from autotest_lib.client.cros.chameleon import chameleon_screen_test
from autotest_lib.client.cros.chameleon import edid
from autotest_lib.server import test
from autotest_lib.server.cros.multimedia import remote_facade_factory
class display_EndToEnd(test.test):
"""External Display end-toend test.
This test talks to a Chameleon board and a DUT to set up, run, and verify
DUT behavior response to reboot, login, docked mode, suspend and resume,
switching mode, etc.
"""
version = 1
# Duration of suspend, in second.
SUSPEND_DURATION = 30
# Allowed timeout for the transition of suspend.
SUSPEND_TIMEOUT = 30
# Allowed timeout for the transition of resume.
RESUME_TIMEOUT = 30
# Allowed timeout for reboot.
REBOOT_TIMEOUT = 60
# Default waiting time in sec
WAIT_TIME = 5
# Crash paths to check for crash meta data
CRASH_PATHS = ['/var/spool/crash',
'/chronos/home/crash'
'/home/chronos/user/crash'
]
# EDID data files names for different ports
EDID_FILE_NAMES = [('DELL_U3011T_HDMI.txt', 'ASUS_VE258_HDMI.txt'),
('MEL_18546_MDT551S_DP.txt', 'ASUS_VE258_DP.txt')]
NO_LID_BOARDS = ['stumpy', 'panther', 'zako', 'tricky', 'mccloud']
def remove_crash_data(self):
"""delete crash meta files"""
for crash_path in self.CRASH_PATHS:
if os.path.isdir(crash_path):
shutil.rmtree(crash_path)
def is_crash_data_present(self):
"""Check for crash meta files"""
for crash_path in self.CRASH_PATHS:
if os.path.isdir(crash_path):
logging.debug('CRASH detected!')
return True
return False
def switch_display_mode(self):
"""Switch from extended to mirror and the opposite"""
from_mode = 'MIRRORED' if self.test_mirrored else 'EXTENDED'
self.test_mirrored = not self.test_mirrored
to_mode = 'MIRRORED' if self.test_mirrored else 'EXTENDED'
logging.debug('Set mirrored: %s', self.test_mirrored)
self.display_facade.set_mirrored(self.test_mirrored)
logging.debug('Switched from %s to %s mode', from_mode, to_mode)
time.sleep(self.WAIT_TIME)
self.check_external_resolution()
def reboot_device(self, plugged_before, plugged_after):
"""Reboot DUT
@param plugged_before: a boolean, plugged status before reboot
@param plugged_after: a boolean, plugged status after reboot
"""
boot_id = self.host.get_boot_id()
self.chameleon_port.set_plug(plugged_before)
logging.debug('Reboot...')
self.host.reboot(wait=False)
time.sleep(self.WAIT_TIME)
self.host.test_wait_for_shutdown(self.REBOOT_TIMEOUT)
self.host.test_wait_for_boot(boot_id)
self.chameleon_port.set_plug(plugged_after)
def test_suspend_resume(self, plugged_before_suspend,
plugged_after_suspend, plugged_after_resume):
"""Suspends and resumes the DUT with different connections status
before suspend, after suspend, and after resume
@param plugged_before_suspend: a boolean, plugged before suspend
@param plugged_after_suspend: a boolean, plugged after suspend
@param plugged_after_resume: a boolean, plugged after resume
"""
boot_id = self.host.get_boot_id()
# Plug before suspend
self.chameleon_port.set_plug(plugged_before_suspend)
time.sleep(self.WAIT_TIME)
logging.debug('Going to suspend, for %d seconds...',
self.SUSPEND_DURATION)
time_before_suspend = time.time()
self.display_facade.suspend_resume_bg(self.SUSPEND_DURATION)
# Confirm DUT suspended.
self.host.test_wait_for_sleep(self.SUSPEND_TIMEOUT)
if plugged_before_suspend is not plugged_after_suspend:
self.chameleon_port.set_plug(plugged_after_suspend)
current_time = time.time()
sleep_time = (self.SUSPEND_DURATION -
(current_time - time_before_suspend))
logging.debug('Wait for %.2f seconds...', sleep_time)
time.sleep(sleep_time)
self.host.test_wait_for_resume(boot_id, self.RESUME_TIMEOUT)
logging.debug('Resumed ')
if plugged_after_resume is not plugged_after_suspend:
self.chameleon_port.set_plug(plugged_after_resume)
def check_external_display(self):
"""Display status check"""
# Check connector and test image
if self.screen_test.check_external_display_connected(
self.connector_used, self.errors) is None:
self.screen_test.test_screen_with_image(
self.resolution, self.test_mirrored, self.errors)
# Check for crashes.
if self.is_crash_data_present():
self.errors.append('Crash data is detected on DUT')
# Check for errors
if self.errors:
raise error.TestFail('; '.join(set(self.errors)))
def get_edids_filepaths(self):
"""Gets the EDID data files for the connector type used"""
if self.connector_used.startswith('HDMI'):
first_edid,second_edid = self.EDID_FILE_NAMES[0]
elif self.connector_used.startswith('DP'):
first_edid,second_edid = self.EDID_FILE_NAMES[1]
first_edid = os.path.join(self.bindir, 'test_data/edids', first_edid)
second_edid = os.path.join(self.bindir, 'test_data/edids', second_edid)
return (first_edid, second_edid)
def check_external_resolution(self):
"""Checks the external screen resolution."""
# Wait video stable, making sure CrOS switches to a proper resolution.
self.chameleon_port.wait_video_input_stable()
# Get the resolution for the edid applied
self.resolution = utils.wait_for_value_changed(
self.display_facade.get_external_resolution,
old_value=self.resolution)
if self.resolution is None:
self.resolution = utils.wait_for_value_changed(
self.display_facade.get_external_resolution,
old_value=None)
if self.resolution is None:
raise error.TestFail('No external display detected on DUT')
logging.debug('External display resolution: %dx%d', *self.resolution)
def apply_edid(self, edid_file):
"""Apply EDID from a file
@param edid_file: file path to edid data
"""
self.display_facade.apply_edid(edid.Edid.from_file(edid_file))
def dock_dut(self):
"""Close lid(assumes device is connected to chameleon)"""
board = self.host.get_board().split(':')[1]
logging.debug('Docking the DUT!')
if board not in self.NO_LID_BOARDS:
self.host.servo.lid_close()
time.sleep(self.WAIT_TIME)
return True
else:
logging.debug('DUT does not dock!')
return False
def undock_dut(self):
"""Open the lid"""
self.host.servo.lid_open()
time.sleep(self.WAIT_TIME)
def run_once(self, host, test_mirrored=False):
self.host = host
self.test_mirrored = test_mirrored
self.errors = []
self.resolution = None
# Check the servo object
if self.host.servo is None:
raise error.TestError('Invalid servo object found on the host.')
# Remove any crash data before test procedure
if self.is_crash_data_present():
self.remove_crash_data()
factory = remote_facade_factory.RemoteFacadeFactory(host)
display_facade = factory.create_display_facade()
chameleon_board = host.chameleon
chameleon_board.reset()
finder = chameleon_port_finder.ChameleonVideoInputFinder(
chameleon_board, display_facade)
for chameleon_port in finder.iterate_all_ports():
self.run_test_on_port(chameleon_port, display_facade)
def run_test_on_port(self, chameleon_port, display_facade):
"""Run the test on the given Chameleon port.
@param chameleon_port: a ChameleonPorts object.
@param display_facade: a display facade object.
"""
self.chameleon_port = chameleon_port
self.display_facade = display_facade
self.screen_test = chameleon_screen_test.ChameleonScreenTest(
chameleon_port, display_facade, self.outputdir)
self.connector_used = self.display_facade.get_external_connector_name()
first_edid, second_edid = self.get_edids_filepaths()
# Set first monitor/EDID and tracked resolution
with self.chameleon_port.use_edid_file(first_edid):
self.check_external_resolution()
# Set main display mode for the test
logging.debug('Set mirrored: %s', self.test_mirrored)
self.display_facade.set_mirrored(self.test_mirrored)
logging.debug('- Stage 1 - reboot')
# Reboot the device as connected and login
self.reboot_device(plugged_before=True, plugged_after=True)
# Check status
self.check_external_display()
logging.debug('- Stage 2 - dock-undock-switch mode and back')
# Dock and undock (close lid and open lid)
if self.dock_dut():
self.undock_dut();
# Switch mode
self.switch_display_mode()
# Switch mode back
self.switch_display_mode()
self.check_external_display()
# Suspend and resume as currently plugged
logging.debug('- Stage 3 - suspend-resume')
self.test_suspend_resume(plugged_before_suspend=True,
plugged_after_suspend=True,
plugged_after_resume=True)
# Check status
self.check_external_display()
logging.debug('- Stage 4 - unplug-suspend-plug-resume')
# Unplug-Suspend-Plug-Resume
self.test_suspend_resume(plugged_before_suspend=False,
plugged_after_suspend=True,
plugged_after_resume=True)
# Check status
self.check_external_display()
logging.debug('- Stage 5 - suspend-unplug-resume-plug')
# Suspens-Unplug-Resume-Plug
self.test_suspend_resume(plugged_before_suspend=True,
plugged_after_suspend=False,
plugged_after_resume=True)
# Check status
self.check_external_display()
logging.debug('- Stage 6 - dock-unplug1/suspend-plug2-undock')
# Docked mode(close lid)
if self.dock_dut():
logging.debug('Unplug display')
# Unplug, thus DUT should suspend
self.chameleon_port.set_plug(False)
self.host.test_wait_for_sleep(self.SUSPEND_TIMEOUT)
logging.debug('DUT is suspended')
# Plug the second monitor while suspended
with self.chameleon_port.use_edid_file(second_edid):
self.chameleon_port.set_plug(True)
# Resume(open lid), doesn't hurt if DUT is not docked
self.undock_dut()
self.host.wait_up(self.RESUME_TIMEOUT)
# Set mode back to mirror after resuming with diff monitor
if self.test_mirrored:
self.switch_display_mode()
else:
# Update the resolution
self.check_external_resolution()
# Check status
self.check_external_display()
logging.debug('- Stage 7 - unplug2-plug1')
# Unplug and plug the original monitor
self.chameleon_port.set_plug(False)
with self.chameleon_port.use_edid_file(first_edid):
self.chameleon_port.set_plug(True)
# Update the resolution
self.check_external_resolution()
# Check status
self.check_external_display()