Home | History | Annotate | Download | only in chameleon
      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 
      5 import logging
      6 import time
      7 from collections import namedtuple
      8 
      9 from autotest_lib.client.bin import utils
     10 from autotest_lib.client.common_lib import error
     11 from autotest_lib.client.cros.chameleon import chameleon
     12 
     13 ChameleonPorts = namedtuple('ChameleonPorts', 'connected failed')
     14 
     15 
     16 class 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 
     83 class 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 
    102 class 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 
    121 class 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 
    251 class 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 
    275 class 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