| # 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. |
| |
| import logging |
| import time |
| from collections import namedtuple |
| from contextlib import contextmanager |
| |
| from autotest_lib.client.bin import utils |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.cros.chameleon import chameleon |
| |
| ChameleonPorts = namedtuple('ChameleonPorts', 'connected failed') |
| |
| |
| class ChameleonPortFinder(object): |
| """ |
| Responsible for finding all ports connected to the chameleon board. |
| |
| It does not verify if these ports are connected to DUT. |
| |
| """ |
| |
| def __init__(self, chameleon_board): |
| """ |
| @param chameleon_board: a ChameleonBoard object representing the |
| Chameleon board whose ports we are interested |
| in finding. |
| |
| """ |
| self.chameleon_board = chameleon_board |
| self.connected = None |
| self.failed = None |
| |
| |
| def find_all_ports(self): |
| """ |
| @returns a named tuple ChameleonPorts() containing a list of connected |
| ports as the first element and failed ports as second element. |
| |
| """ |
| self.connected = self.chameleon_board.get_all_ports() |
| self.failed = [] |
| |
| return ChameleonPorts(self.connected, self.failed) |
| |
| |
| def find_port(self, interface): |
| """ |
| @param interface: string, the interface. e.g: HDMI, DP, VGA |
| @returns a ChameleonPort object if port is found, else None. |
| |
| """ |
| connected_ports = self.find_all_ports().connected |
| |
| for port in connected_ports: |
| if port.get_connector_type().lower() == interface.lower(): |
| return port |
| |
| return None |
| |
| |
| def __str__(self): |
| ports_to_str = lambda ports: ', '.join( |
| '%s(%d)' % (p.get_connector_type(), p.get_connector_id()) |
| for p in ports) |
| |
| if self.connected is None: |
| text = 'No port information. Did you run find_all_ports()?' |
| elif self.connected == []: |
| text = 'No port detected on the Chameleon board.' |
| else: |
| text = ('Detected %d connected port(s): %s. \t' |
| % (len(self.connected), ports_to_str(self.connected))) |
| |
| if self.failed: |
| text += ('DUT failed to detect Chameleon ports: %s' |
| % ports_to_str(self.failed)) |
| |
| return text |
| |
| |
| class ChameleonInputFinder(ChameleonPortFinder): |
| """ |
| Responsible for finding all input ports connected to the chameleon board. |
| |
| """ |
| |
| def find_all_ports(self): |
| """ |
| @returns a named tuple ChameleonPorts() containing a list of connected |
| input ports as the first element and failed ports as second |
| element. |
| |
| """ |
| self.connected = self.chameleon_board.get_all_inputs() |
| self.failed = [] |
| |
| return ChameleonPorts(self.connected, self.failed) |
| |
| |
| class ChameleonOutputFinder(ChameleonPortFinder): |
| """ |
| Responsible for finding all output ports connected to the chameleon board. |
| |
| """ |
| |
| def find_all_ports(self): |
| """ |
| @returns a named tuple ChameleonPorts() containing a list of connected |
| output ports as the first element and failed ports as the |
| second element. |
| |
| """ |
| self.connected = self.chameleon_board.get_all_outputs() |
| self.failed = [] |
| |
| return ChameleonPorts(self.connected, self.failed) |
| |
| |
| class ChameleonVideoInputFinder(ChameleonInputFinder): |
| """ |
| Responsible for finding all video inputs connected to the chameleon board. |
| |
| It also verifies if these ports are connected to DUT. |
| |
| """ |
| |
| REPLUG_DELAY_SEC = 1 |
| |
| def __init__(self, chameleon_board, display_facade): |
| """ |
| @param chameleon_board: a ChameleonBoard object representing the |
| Chameleon board whose ports we are interested |
| in finding. |
| @param display_facade: a display facade object, to access the DUT |
| display functionality, either locally or |
| remotely. |
| |
| """ |
| super(ChameleonVideoInputFinder, self).__init__(chameleon_board) |
| self.display_facade = display_facade |
| self._TIMEOUT_VIDEO_STABLE_PROBE = 10 |
| |
| |
| def _yield_all_ports(self, failed_ports=None, raise_error=False): |
| """ |
| Yields all connected video ports and ensures every of them plugged. |
| |
| @param failed_ports: A list to append the failed port or None. |
| @param raise_error: True to raise TestFail if no connected video port. |
| @yields every connected ChameleonVideoInput which is ensured plugged |
| before yielding. |
| |
| @raises TestFail if raise_error is True and no connected video port. |
| |
| """ |
| yielded = False |
| all_ports = super(ChameleonVideoInputFinder, self).find_all_ports() |
| |
| # unplug all ports |
| for port in all_ports.connected: |
| if port.has_video_support(): |
| chameleon.ChameleonVideoInput(port).unplug() |
| self.display_facade.reset_connector_if_applicable( |
| port.get_connector_type()) |
| |
| for port in all_ports.connected: |
| # Skip the non-video port. |
| if not port.has_video_support(): |
| continue |
| |
| video_port = chameleon.ChameleonVideoInput(port) |
| # Plug the port to make it visible. |
| video_port.plug() |
| try: |
| # DUT takes some time to respond. Wait until the video signal |
| # to stabilize and wait for the connector change. |
| video_stable = video_port.wait_video_input_stable( |
| self._TIMEOUT_VIDEO_STABLE_PROBE) |
| output = utils.wait_for_value_changed( |
| self.display_facade.get_external_connector_name, |
| old_value=False) |
| |
| if not output: |
| logging.warning('Maybe flaky that no display detected. Retry.') |
| video_port.unplug() |
| time.sleep(self.REPLUG_DELAY_SEC) |
| video_port.plug() |
| video_stable = video_port.wait_video_input_stable( |
| self._TIMEOUT_VIDEO_STABLE_PROBE) |
| output = utils.wait_for_value_changed( |
| self.display_facade.get_external_connector_name, |
| old_value=False) |
| |
| logging.info('CrOS detected external connector: %r', output) |
| |
| if output: |
| yield video_port |
| yielded = True |
| else: |
| if failed_ports is not None: |
| failed_ports.append(video_port) |
| logging.error('CrOS failed to see any external display') |
| if not video_stable: |
| logging.warning('Chameleon timed out waiting CrOS video') |
| finally: |
| # Unplug the port not to interfere with other tests. |
| video_port.unplug() |
| |
| if raise_error and not yielded: |
| raise error.TestFail('No connected video port found between CrOS ' |
| 'and Chameleon.') |
| |
| |
| def iterate_all_ports(self): |
| """ |
| Iterates all connected video ports and ensures every of them plugged. |
| |
| It is used via a for statement, like the following: |
| |
| finder = ChameleonVideoInputFinder(chameleon_board, display_facade) |
| for chameleon_port in finder.iterate_all_ports() |
| # chameleon_port is automatically plugged before this line. |
| do_some_test_on(chameleon_port) |
| # chameleon_port is automatically unplugged after this line. |
| |
| @yields every connected ChameleonVideoInput which is ensured plugged |
| before yeilding. |
| |
| @raises TestFail if no connected video port. |
| |
| """ |
| return self._yield_all_ports(raise_error=True) |
| |
| |
| @contextmanager |
| def use_first_port(self): |
| """ |
| Use the first connected video port and ensures it plugged. |
| |
| It is used via a with statement, like the following: |
| |
| finder = ChameleonVideoInputFinder(chameleon_board, display_facade) |
| with finder.use_first_port() as chameleon_port: |
| # chameleon_port is automatically plugged before this line. |
| do_some_test_on(chameleon_port) |
| # chameleon_port is automatically unplugged after this line. |
| |
| @yields the first connected ChameleonVideoInput which is ensured plugged |
| before yeilding. |
| |
| @raises TestFail if no connected video port. |
| |
| """ |
| for port in self._yield_all_ports(raise_error=True): |
| yield port |
| break |
| |
| |
| def find_all_ports(self): |
| """ |
| @returns a named tuple ChameleonPorts() containing a list of connected |
| video inputs as the first element and failed ports as second |
| element. |
| |
| """ |
| dut_failed_ports = [] |
| connected_ports = list(self._yield_all_ports(dut_failed_ports)) |
| self.connected = connected_ports |
| self.failed = dut_failed_ports |
| |
| return ChameleonPorts(connected_ports, dut_failed_ports) |
| |
| |
| class ChameleonAudioInputFinder(ChameleonInputFinder): |
| """ |
| Responsible for finding all audio inputs connected to the chameleon board. |
| |
| It does not verify if these ports are connected to DUT. |
| |
| """ |
| |
| def find_all_ports(self): |
| """ |
| @returns a named tuple ChameleonPorts() containing a list of connected |
| audio inputs as the first element and failed ports as second |
| element. |
| |
| """ |
| all_ports = super(ChameleonAudioInputFinder, self).find_all_ports() |
| self.connected = [chameleon.ChameleonAudioInput(port) |
| for port in all_ports.connected |
| if port.has_audio_support()] |
| self.failed = [] |
| |
| return ChameleonPorts(self.connected, self.failed) |
| |
| |
| class ChameleonAudioOutputFinder(ChameleonOutputFinder): |
| """ |
| Responsible for finding all audio outputs connected to the chameleon board. |
| |
| It does not verify if these ports are connected to DUT. |
| |
| """ |
| |
| def find_all_ports(self): |
| """ |
| @returns a named tuple ChameleonPorts() containing a list of connected |
| audio outputs as the first element and failed ports as second |
| element. |
| |
| """ |
| all_ports = super(ChameleonAudioOutputFinder, self).find_all_ports() |
| self.connected = [chameleon.ChameleonAudioOutput(port) |
| for port in all_ports.connected |
| if port.has_audio_support()] |
| self.failed = [] |
| |
| return ChameleonPorts(self.connected, self.failed) |