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 7from collections import namedtuple 8 9from autotest_lib.client.bin import utils 10from autotest_lib.client.common_lib import error 11from autotest_lib.client.cros.chameleon import chameleon 12 13ChameleonPorts = namedtuple('ChameleonPorts', 'connected failed') 14 15 16class ChameleonPortFinder(object): 17 """ 18 Responsible for finding all ports connected to the chameleon board. 19 20 It does not verify if these ports are connected to DUT. 21 22 """ 23 24 def __init__(self, chameleon_board): 25 """ 26 @param chameleon_board: a ChameleonBoard object representing the 27 Chameleon board whose ports we are interested 28 in finding. 29 30 """ 31 self.chameleon_board = chameleon_board 32 self.connected = None 33 self.failed = None 34 35 36 def find_all_ports(self): 37 """ 38 @returns a named tuple ChameleonPorts() containing a list of connected 39 ports as the first element and failed ports as second element. 40 41 """ 42 self.connected = self.chameleon_board.get_all_ports() 43 self.failed = [] 44 45 return ChameleonPorts(self.connected, self.failed) 46 47 48 def find_port(self, interface): 49 """ 50 @param interface: string, the interface. e.g: HDMI, DP, VGA 51 @returns a ChameleonPort object if port is found, else None. 52 53 """ 54 connected_ports = self.find_all_ports().connected 55 56 for port in connected_ports: 57 if port.get_connector_type().lower() == interface.lower(): 58 return port 59 60 return None 61 62 63 def __str__(self): 64 ports_to_str = lambda ports: ', '.join( 65 '%s(%d)' % (p.get_connector_type(), p.get_connector_id()) 66 for p in ports) 67 68 if self.connected is None: 69 text = 'No port information. Did you run find_all_ports()?' 70 elif self.connected == []: 71 text = 'No port detected on the Chameleon board.' 72 else: 73 text = ('Detected %d connected port(s): %s. \t' 74 % (len(self.connected), ports_to_str(self.connected))) 75 76 if self.failed: 77 text += ('DUT failed to detect Chameleon ports: %s' 78 % ports_to_str(self.failed)) 79 80 return text 81 82 83class ChameleonInputFinder(ChameleonPortFinder): 84 """ 85 Responsible for finding all input ports connected to the chameleon board. 86 87 """ 88 89 def find_all_ports(self): 90 """ 91 @returns a named tuple ChameleonPorts() containing a list of connected 92 input ports as the first element and failed ports as second 93 element. 94 95 """ 96 self.connected = self.chameleon_board.get_all_inputs() 97 self.failed = [] 98 99 return ChameleonPorts(self.connected, self.failed) 100 101 102class ChameleonOutputFinder(ChameleonPortFinder): 103 """ 104 Responsible for finding all output ports connected to the chameleon board. 105 106 """ 107 108 def find_all_ports(self): 109 """ 110 @returns a named tuple ChameleonPorts() containing a list of connected 111 output ports as the first element and failed ports as the 112 second element. 113 114 """ 115 self.connected = self.chameleon_board.get_all_outputs() 116 self.failed = [] 117 118 return ChameleonPorts(self.connected, self.failed) 119 120 121class ChameleonVideoInputFinder(ChameleonInputFinder): 122 """ 123 Responsible for finding all video inputs connected to the chameleon board. 124 125 It also verifies if these ports are connected to DUT. 126 127 """ 128 129 REPLUG_DELAY_SEC = 1 130 131 def __init__(self, chameleon_board, display_facade): 132 """ 133 @param chameleon_board: a ChameleonBoard object representing the 134 Chameleon board whose ports we are interested 135 in finding. 136 @param display_facade: a display facade object, to access the DUT 137 display functionality, either locally or 138 remotely. 139 140 """ 141 super(ChameleonVideoInputFinder, self).__init__(chameleon_board) 142 self.display_facade = display_facade 143 self._TIMEOUT_VIDEO_STABLE_PROBE = 10 144 145 146 def _yield_all_ports(self, failed_ports=None, raise_error=False): 147 """ 148 Yields all connected video ports and ensures every of them plugged. 149 150 @param failed_ports: A list to append the failed port or None. 151 @param raise_error: True to raise TestFail if no connected video port. 152 @yields every connected ChameleonVideoInput which is ensured plugged 153 before yielding. 154 155 @raises TestFail if raise_error is True and no connected video port. 156 157 """ 158 yielded = False 159 all_ports = super(ChameleonVideoInputFinder, self).find_all_ports() 160 161 # unplug all ports 162 for port in all_ports.connected: 163 if port.has_video_support(): 164 chameleon.ChameleonVideoInput(port).unplug() 165 166 for port in all_ports.connected: 167 # Skip the non-video port. 168 if not port.has_video_support(): 169 continue 170 171 video_port = chameleon.ChameleonVideoInput(port) 172 connector_type = video_port.get_connector_type() 173 # Plug the port to make it visible. 174 video_port.plug() 175 try: 176 # DUT takes some time to respond. Wait until the video signal 177 # to stabilize and wait for the connector change. 178 video_stable = video_port.wait_video_input_stable( 179 self._TIMEOUT_VIDEO_STABLE_PROBE) 180 output = utils.wait_for_value_changed( 181 self.display_facade.get_external_connector_name, 182 old_value=False) 183 184 if not output: 185 logging.warn('Maybe flaky that no display detected. Retry.') 186 video_port.unplug() 187 time.sleep(self.REPLUG_DELAY_SEC) 188 video_port.plug() 189 video_stable = video_port.wait_video_input_stable( 190 self._TIMEOUT_VIDEO_STABLE_PROBE) 191 output = utils.wait_for_value_changed( 192 self.display_facade.get_external_connector_name, 193 old_value=False) 194 195 logging.info('CrOS detected external connector: %r', output) 196 197 if output: 198 yield video_port 199 yielded = True 200 else: 201 if failed_ports is not None: 202 failed_ports.append(video_port) 203 logging.error('CrOS failed to see any external display') 204 if not video_stable: 205 logging.warn('Chameleon timed out waiting CrOS video') 206 finally: 207 # Unplug the port not to interfere with other tests. 208 video_port.unplug() 209 210 if raise_error and not yielded: 211 raise error.TestFail('No connected video port found between CrOS ' 212 'and Chameleon.') 213 214 215 def iterate_all_ports(self): 216 """ 217 Iterates all connected video ports and ensures every of them plugged. 218 219 It is used via a for statement, like the following: 220 221 finder = ChameleonVideoInputFinder(chameleon_board, display_facade) 222 for chameleon_port in finder.iterate_all_ports() 223 # chameleon_port is automatically plugged before this line. 224 do_some_test_on(chameleon_port) 225 # chameleon_port is automatically unplugged after this line. 226 227 @yields every connected ChameleonVideoInput which is ensured plugged 228 before yeilding. 229 230 @raises TestFail if no connected video port. 231 232 """ 233 return self._yield_all_ports(raise_error=True) 234 235 236 def find_all_ports(self): 237 """ 238 @returns a named tuple ChameleonPorts() containing a list of connected 239 video inputs as the first element and failed ports as second 240 element. 241 242 """ 243 dut_failed_ports = [] 244 connected_ports = list(self._yield_all_ports(dut_failed_ports)) 245 self.connected = connected_ports 246 self.failed = dut_failed_ports 247 248 return ChameleonPorts(connected_ports, dut_failed_ports) 249 250 251class ChameleonAudioInputFinder(ChameleonInputFinder): 252 """ 253 Responsible for finding all audio inputs connected to the chameleon board. 254 255 It does not verify if these ports are connected to DUT. 256 257 """ 258 259 def find_all_ports(self): 260 """ 261 @returns a named tuple ChameleonPorts() containing a list of connected 262 audio inputs as the first element and failed ports as second 263 element. 264 265 """ 266 all_ports = super(ChameleonAudioInputFinder, self).find_all_ports() 267 self.connected = [chameleon.ChameleonAudioInput(port) 268 for port in all_ports.connected 269 if port.has_audio_support()] 270 self.failed = [] 271 272 return ChameleonPorts(self.connected, self.failed) 273 274 275class ChameleonAudioOutputFinder(ChameleonOutputFinder): 276 """ 277 Responsible for finding all audio outputs connected to the chameleon board. 278 279 It does not verify if these ports are connected to DUT. 280 281 """ 282 283 def find_all_ports(self): 284 """ 285 @returns a named tuple ChameleonPorts() containing a list of connected 286 audio outputs as the first element and failed ports as second 287 element. 288 289 """ 290 all_ports = super(ChameleonAudioOutputFinder, self).find_all_ports() 291 self.connected = [chameleon.ChameleonAudioOutput(port) 292 for port in all_ports.connected 293 if port.has_audio_support()] 294 self.failed = [] 295 296 return ChameleonPorts(self.connected, self.failed) 297