1# Copyright 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 5import logging 6import time 7 8from autotest_lib.client.bin import utils 9from autotest_lib.client.cros.chameleon import screen_utility_factory 10 11 12class ChameleonScreenTest(object): 13 """Utility to test the screen between Chameleon and CrOS. 14 15 This class contains the screen-related testing operations. 16 17 """ 18 # Time in seconds to wait for notation bubbles, including bubbles for 19 # external detection, mirror mode and fullscreen, to disappear. 20 _TEST_IMAGE_STABILIZE_TIME = 10 21 22 def __init__(self, chameleon_port, display_facade, output_dir): 23 """Initializes the ScreenUtilityFactory objects.""" 24 self._display_facade = display_facade 25 factory = screen_utility_factory.ScreenUtilityFactory( 26 chameleon_port, display_facade) 27 self._resolution_comparer = factory.create_resolution_comparer() 28 self._screen_comparer = factory.create_screen_comparer(output_dir) 29 self._mirror_comparer = factory.create_mirror_comparer(output_dir) 30 self._calibration_image_tab_descriptor = None 31 32 33 def test_resolution(self, expected_resolution): 34 """Tests if the resolution of Chameleon matches with the one of CrOS. 35 36 @param expected_resolution: A tuple (width, height) for the expected 37 resolution. 38 @return: None if the check passes; otherwise, a string of error message. 39 """ 40 return self._resolution_comparer.compare(expected_resolution) 41 42 43 def test_screen(self, expected_resolution, test_mirrored=None, 44 error_list=None): 45 """Tests if the screen of Chameleon matches with the one of CrOS. 46 47 @param expected_resolution: A tuple (width, height) for the expected 48 resolution. 49 @param test_mirrored: True to test mirrored mode. False not to. None 50 to test mirrored mode iff the current mode is 51 mirrored. 52 @param error_list: A list to append the error message to or None. 53 @return: None if the check passes; otherwise, a string of error message. 54 """ 55 if test_mirrored is None: 56 test_mirrored = self._display_facade.is_mirrored_enabled() 57 58 error = self._resolution_comparer.compare(expected_resolution) 59 if not error: 60 # Do two screen comparisons with and without hiding cursor, to 61 # work-around some devices still showing cursor on CrOS FB. 62 # TODO: Remove this work-around once crosbug/p/34524 got fixed. 63 error = self._screen_comparer.compare() 64 if error: 65 logging.info('Hide cursor and do screen comparison again...') 66 self._display_facade.hide_cursor() 67 error = self._screen_comparer.compare() 68 if not error and test_mirrored: 69 error = self._mirror_comparer.compare() 70 if error and error_list is not None: 71 error_list.append(error) 72 return error 73 74 75 def load_test_image(self, image_size, test_mirrored=None): 76 """Loads calibration image on the CrOS with logging 77 78 @param image_size: A tuple (width, height) conforms the resolution. 79 @param test_mirrored: True to test mirrored mode. False not to. None 80 to test mirrored mode iff the current mode is 81 mirrored. 82 """ 83 if test_mirrored is None: 84 test_mirrored = self._display_facade.is_mirrored_enabled() 85 self._calibration_image_tab_descriptor = \ 86 self._display_facade.load_calibration_image(image_size) 87 if not test_mirrored: 88 self._display_facade.move_to_display( 89 self._display_facade.get_first_external_display_id()) 90 self._display_facade.set_fullscreen(True) 91 logging.info('Waiting for calibration image to stabilize...') 92 time.sleep(self._TEST_IMAGE_STABILIZE_TIME) 93 94 95 def unload_test_image(self): 96 """Closes the tab in browser to unload the fullscreen test image.""" 97 self._display_facade.close_tab(self._calibration_image_tab_descriptor) 98 99 100 def test_screen_with_image(self, expected_resolution, test_mirrored=None, 101 error_list=None, chameleon_supported=True, 102 retry_count=2): 103 """Tests the screen with image loaded. 104 105 @param expected_resolution: A tuple (width, height) for the expected 106 resolution. 107 @param test_mirrored: True to test mirrored mode. False not to. None 108 to test mirrored mode iff the current mode is 109 mirrored. 110 @param error_list: A list to append the error message to or None. 111 @param retry_count: A count to retry the screen test. 112 @param chameleon_supported: Whether resolution is supported by 113 chameleon. The DP RX doesn't support 114 4K resolution. The max supported resolution 115 is 2560x1600. See crbug/585900. 116 @return: None if the check passes; otherwise, a string of error message. 117 """ 118 if test_mirrored is None: 119 test_mirrored = self._display_facade.is_mirrored_enabled() 120 121 if test_mirrored: 122 test_image_size = self._display_facade.get_internal_resolution() 123 else: 124 # DUT needs time to respond to the plug event 125 test_image_size = utils.wait_for_value_changed( 126 self._display_facade.get_external_resolution, 127 old_value=None) 128 error = None 129 if test_image_size != expected_resolution: 130 error = ('Screen size %s is not as expected %s!' 131 % (str(test_image_size), str(expected_resolution))) 132 if test_mirrored: 133 # For the case of mirroring, depending on hardware vs 134 # software mirroring, screen size can be different. 135 logging.info('Warning: %s', error) 136 error = None 137 else: 138 error_list.append(error) 139 140 if chameleon_supported: 141 error = self._resolution_comparer.compare(expected_resolution) 142 if not error: 143 while retry_count: 144 retry_count = retry_count - 1 145 try: 146 self.load_test_image(test_image_size) 147 error = self.test_screen(expected_resolution, test_mirrored) 148 if error is None: 149 return error 150 elif retry_count > 0: 151 logging.info('Retry screen comparison again...') 152 finally: 153 self.unload_test_image() 154 155 if error and error_list is not None: 156 error_list.append(error) 157 return error 158 159 160 def check_external_display_connected(self, expected_display, 161 error_list=None): 162 """Checks the given external display connected. 163 164 @param expected_display: Name of the expected display or False 165 if no external display is expected. 166 @param error_list: A list to append the error message to or None. 167 @return: None if the check passes; otherwise, a string of error message. 168 """ 169 error = None 170 if not self._display_facade.wait_external_display_connected( 171 expected_display): 172 error = 'Waited for display %s but timed out' % expected_display 173 174 if error and error_list is not None: 175 logging.error(error) 176 error_list.append(error) 177 return error 178