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        factory = remote_facade_factory.RemoteFacadeFactory(host)
45        display_facade = factory.create_display_facade()
46        chameleon_board = host.chameleon
47
48        chameleon_board.reset()
49        finder = chameleon_port_finder.ChameleonVideoInputFinder(
50                chameleon_board, display_facade)
51
52        errors = []
53        warns = []
54        for chameleon_port in finder.iterate_all_ports():
55            screen_test = chameleon_screen_test.ChameleonScreenTest(
56                    chameleon_port, display_facade, self.outputdir)
57
58            logging.info('See the display on Chameleon: port %d (%s)',
59                         chameleon_port.get_connector_id(),
60                         chameleon_port.get_connector_type())
61
62            logging.info('Set mirrored: %s', test_mirrored)
63            display_facade.set_mirrored(test_mirrored)
64
65            # Keep the original connector name, for later comparison.
66            expected_connector = display_facade.get_external_connector_name()
67            resolution = display_facade.get_external_resolution()
68            logging.info('See the display on DUT: %s %r',
69                         expected_connector, resolution)
70
71            for (plugged_before_noise,
72                 plugged_after_noise) in self.PLUG_CONFIGS:
73                logging.info('TESTING THE CASE: %s > noise > %s',
74                             'plug' if plugged_before_noise else 'unplug',
75                             'plug' if plugged_after_noise else 'unplug')
76
77                chameleon_port.set_plug(plugged_before_noise)
78
79                if screen_test.check_external_display_connected(
80                        expected_connector if plugged_before_noise else False,
81                        errors):
82                    # Skip the following test if an unexpected display detected.
83                    continue
84
85                chameleon_port.fire_mixed_hpd_pulses(
86                        self.PULSES_PLUGGED if plugged_after_noise
87                                            else self.PULSES_UNPLUGGED)
88
89                if plugged_after_noise:
90                    chameleon_port.wait_video_input_stable()
91                    if test_mirrored:
92                        # Wait for resolution change to make sure the resolution
93                        # is stable before moving on. This is to deal with the
94                        # case where DUT may respond slowly after the noise.
95                        # If the resolution doesn't change, then we are
96                        # confident that it is stable. Otherwise, a slow
97                        # response is caught.
98                        r = display_facade.get_internal_resolution()
99                        utils.wait_for_value_changed(
100                                display_facade.get_internal_resolution,
101                                old_value=r)
102
103                    err = screen_test.check_external_display_connected(
104                            expected_connector)
105
106                    if not err:
107                        err = screen_test.test_screen_with_image(
108                                resolution, test_mirrored)
109                    if err:
110                        # When something goes wrong after the noise, a normal
111                        # user would try to re-plug the cable to recover.
112                        # We emulate this behavior below and report error if
113                        # the problem persists.
114                        logging.warn('Possibly flaky: %s', err)
115                        warns.append('Possibly flaky: %s' % err)
116                        logging.info('Replug and retry the screen test...')
117                        chameleon_port.unplug()
118                        time.sleep(self.REPLUG_DELAY_SEC)
119                        chameleon_port.plug()
120                        chameleon_port.wait_video_input_stable()
121                        screen_test.test_screen_with_image(
122                                resolution, test_mirrored, errors)
123                else:
124                    screen_test.check_external_display_connected(False, errors)
125                    time.sleep(1)
126
127        if errors:
128            raise error.TestFail('; '.join(set(errors)))
129        elif warns:
130            raise error.TestWarn('; '.join(set(warns)))
131