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"""This test remotely emulates noisy HPD line when connecting to an external
6display in extended mode using the Chameleon board."""
7
8import logging
9import time
10
11from autotest_lib.client.bin import utils
12from autotest_lib.client.common_lib import error
13from autotest_lib.client.cros.chameleon import chameleon_port_finder
14from autotest_lib.client.cros.chameleon import chameleon_screen_test
15from autotest_lib.server import test
16from autotest_lib.server.cros.multimedia import remote_facade_factory
17
18
19class display_HotPlugNoisy(test.test):
20    """Noisy display HPD test.
21
22    This test talks to a Chameleon board and a DUT to set up, run, and verify
23    DUT behavior in response to noisy HPD line.
24    """
25    version = 1
26    PLUG_CONFIGS = [
27        # (plugged_before_noise, plugged_after_noise)
28
29        (False, False),
30        (False, True),
31        (True, False),
32        (True, True),
33    ]
34
35    # pulse segments in msec that end with plugged state
36    PULSES_PLUGGED = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
37    # pulse segments in msec that end with unplugged state
38    PULSES_UNPLUGGED = PULSES_PLUGGED + [2048]
39
40    REPLUG_DELAY_SEC = 1
41
42
43    def run_once(self, host, test_mirrored=False):
44        if test_mirrored and not host.get_board_type() == 'CHROMEBOOK':
45            raise error.TestNAError('DUT is not Chromebook. Test Skipped')
46
47        factory = remote_facade_factory.RemoteFacadeFactory(host)
48        display_facade = factory.create_display_facade()
49        chameleon_board = host.chameleon
50
51        chameleon_board.setup_and_reset(self.outputdir)
52        finder = chameleon_port_finder.ChameleonVideoInputFinder(
53                chameleon_board, display_facade)
54
55        errors = []
56        warns = []
57        for chameleon_port in finder.iterate_all_ports():
58            screen_test = chameleon_screen_test.ChameleonScreenTest(
59                    host, chameleon_port, display_facade, self.outputdir)
60
61            logging.info('See the display on Chameleon: port %d (%s)',
62                         chameleon_port.get_connector_id(),
63                         chameleon_port.get_connector_type())
64
65            logging.info('Set mirrored: %s', test_mirrored)
66            display_facade.set_mirrored(test_mirrored)
67
68            # Keep the original connector name, for later comparison.
69            expected_connector = display_facade.get_external_connector_name()
70            resolution = display_facade.get_external_resolution()
71            logging.info('See the display on DUT: %s %r',
72                         expected_connector, resolution)
73
74            for (plugged_before_noise,
75                 plugged_after_noise) in self.PLUG_CONFIGS:
76                logging.info('TESTING THE CASE: %s > noise > %s',
77                             'plug' if plugged_before_noise else 'unplug',
78                             'plug' if plugged_after_noise else 'unplug')
79
80                chameleon_port.set_plug(plugged_before_noise)
81
82                if screen_test.check_external_display_connected(
83                        expected_connector if plugged_before_noise else False,
84                        errors):
85                    # Skip the following test if an unexpected display detected.
86                    continue
87
88                chameleon_port.fire_mixed_hpd_pulses(
89                        self.PULSES_PLUGGED if plugged_after_noise
90                                            else self.PULSES_UNPLUGGED)
91
92                if plugged_after_noise:
93                    chameleon_port.wait_video_input_stable()
94                    if test_mirrored:
95                        # Wait for resolution change to make sure the resolution
96                        # is stable before moving on. This is to deal with the
97                        # case where DUT may respond slowly after the noise.
98                        # If the resolution doesn't change, then we are
99                        # confident that it is stable. Otherwise, a slow
100                        # response is caught.
101                        r = display_facade.get_internal_resolution()
102                        utils.wait_for_value_changed(
103                                display_facade.get_internal_resolution,
104                                old_value=r)
105
106                    err = screen_test.check_external_display_connected(
107                            expected_connector)
108
109                    if not err:
110                        err = screen_test.test_screen_with_image(
111                                resolution, test_mirrored)
112                    if err:
113                        # When something goes wrong after the noise, a normal
114                        # user would try to re-plug the cable to recover.
115                        # We emulate this behavior below and report error if
116                        # the problem persists.
117                        logging.warn('Possibly flaky: %s', err)
118                        warns.append('Possibly flaky: %s' % err)
119                        logging.info('Replug and retry the screen test...')
120                        chameleon_port.unplug()
121                        time.sleep(self.REPLUG_DELAY_SEC)
122                        chameleon_port.plug()
123                        chameleon_port.wait_video_input_stable()
124                        screen_test.test_screen_with_image(
125                                resolution, test_mirrored, errors)
126                else:
127                    screen_test.check_external_display_connected(False, errors)
128                    time.sleep(1)
129
130        if errors:
131            raise error.TestFail('; '.join(set(errors)))
132        elif warns:
133            raise error.TestWarn('; '.join(set(warns)))
134