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