| # 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.common_lib import error |
| |
| from autotest_lib.server.cros.chameleon import chameleon_test |
| |
| |
| class display_EndToEnd(chameleon_test.ChameleonTest): |
| """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 = 15 |
| # Allowed timeout for the transition of suspend. |
| SUSPEND_TIMEOUT = 7 |
| # Allowed timeout for the transition of resume. |
| RESUME_TIMEOUT = 20 |
| #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'), |
| ('DELL_U3011T_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' |
| self.set_mirrored(self.test_mirrored) |
| logging.debug('Switched from %s to %s mode', from_mode, to_mode) |
| time.sleep(self.WAIT_TIME) |
| self.reconnect_and_get_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.set_plug(plugged_before) |
| self.reboot(wait=False) |
| self.host.test_wait_for_shutdown() |
| self.host.test_wait_for_boot(boot_id) |
| self.set_plug(plugged_after) |
| self.display_facade.connect() |
| |
| |
| def 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.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) |
| self.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 ') |
| |
| self.set_plug(plugged_after_resume) |
| |
| |
| def wait_to_suspend(self, suspend_timeout): |
| """Wait for DUT to suspend. |
| |
| @param suspend_timeout: Time in seconds to wait for suspend |
| |
| @exception TestFail: If fail to suspend in time |
| """ |
| if not self.host.ping_wait_down(timeout=suspend_timeout): |
| raise error.TestFail('Failed to SUSPEND after %d seconds' % |
| suspend_timeout) |
| |
| logging.debug('Dut is suspended.') |
| |
| def wait_to_resume(self, resume_timeout): |
| """Wait for DUT to resume. |
| |
| @param resume_timeout: Time in seconds to wait for resuming |
| |
| @exception TestFail: if fail to resume in time |
| """ |
| if not self.host.wait_up(timeout=resume_timeout): |
| raise error.TestFail( |
| 'Failed to RESUME after %d seconds' % |
| resume_timeout) |
| logging.debug('Dut is up.') |
| |
| |
| def check_external_display(self): |
| """Display status check""" |
| self.test_name = '%s-%s-%s' % (self.connector_used, |
| str(self.resolution), |
| 'mirror' if self.test_mirrored else 'extended') |
| #Check connector |
| self.check_external_display_connector(self.connector_used) |
| #Check test image |
| self.load_test_image_and_check( |
| self.test_name, self.resolution, |
| under_mirrored_mode=self.test_mirrored, |
| error_list=self.errors) |
| #Check for crashes. |
| if self.is_crash_data_present(): |
| self.errors.append('Crash data is detected on DUT') |
| self.raise_on_errors(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 reconnect_and_get_external_resolution(self): |
| """Reconnect the display and get the external screen resolution.""" |
| self.reconnect_output() |
| # Get the resolution for the edid applied |
| self.resolution = self.display_facade.get_external_resolution() |
| logging.debug('External display resolution: %s', |
| str(self.resolution)) |
| |
| |
| def apply_edid_and_reconnect(self, edid_file, suspended=False): |
| """Apply EDID from a file |
| |
| @param edid_file: file path to edid data |
| @param suspended: a boolean, to not reconnect ouptut when suspended |
| |
| """ |
| self.apply_edid_file(edid_file) |
| #reconnect for the new edid if not suspended |
| if not suspended: |
| self.reconnect_and_get_external_resolution() |
| |
| 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 = [] |
| |
| #Remove any crash data before test procedure |
| if self.is_crash_data_present(): |
| self.remove_crash_data() |
| |
| 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 |
| self.apply_edid_and_reconnect(first_edid) |
| #Set main display mode for the test |
| self.set_mirrored(self.test_mirrored) |
| |
| #Reboot the device as connected and login |
| self.reboot_device(plugged_before=True, plugged_after=True) |
| #Check status |
| self.check_external_display() |
| |
| #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 |
| self.suspend_resume() |
| |
| #Unplug-Suspend-Plug-Resume |
| self.suspend_resume(plugged_before_suspend=False, |
| plugged_after_suspend=True, |
| plugged_after_resume=True) |
| #Check status |
| self.check_external_display() |
| |
| #Switch mode |
| self.switch_display_mode() |
| #Switch mode back |
| self.switch_display_mode() |
| |
| #Suspens-Unplug-Resume-Plug |
| self.suspend_resume(plugged_before_suspend=True, |
| plugged_after_suspend=False, |
| plugged_after_resume=True) |
| #Check status |
| self.check_external_display() |
| |
| #Docked mode(close lid) |
| if self.dock_dut(): |
| logging.debug('Unplug display') |
| #Unplug, thus DUT should suspend |
| self.set_plug(False) |
| self.wait_to_suspend(self.SUSPEND_TIMEOUT) |
| logging.debug('DUT is suspended') |
| |
| #Plug the second monitor while suspended |
| self.apply_edid_and_reconnect(second_edid, suspended=True) |
| #Plug back |
| self.set_plug(True) |
| |
| #Resume(open lid), doesn't hurt if DUT is not docked |
| self.undock_dut() |
| self.wait_to_resume(self.RESUME_TIMEOUT) |
| |
| self.reconnect_and_get_external_resolution() |
| #Check status |
| self.check_external_display() |
| |
| #Switch mode |
| self.switch_display_mode() |
| #Switch mode back |
| self.switch_display_mode() |
| |
| #Unplug and plug the original monitor |
| self.set_plug(False) |
| self.apply_edid_and_reconnect(first_edid) |
| self.set_plug(True) |
| self.check_external_display() |
| |