• 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
7
8from autotest_lib.client.bin import utils
9from autotest_lib.client.common_lib import error as error_lib
10from autotest_lib.client.cros.chameleon import screen_utility_factory
11
12
13class ChameleonScreenTest(object):
14    """Utility to test the screen between Chameleon and CrOS.
15
16    This class contains the screen-related testing operations.
17
18    """
19    # Time in seconds to wait for notation bubbles, including bubbles for
20    # external detection, mirror mode and fullscreen, to disappear.
21    _TEST_IMAGE_STABILIZE_TIME = 10
22
23    def __init__(self, host, chameleon_port, display_facade, output_dir):
24        """Initializes the ScreenUtilityFactory objects."""
25        self._host = host
26        self._display_facade = display_facade
27        factory = screen_utility_factory.ScreenUtilityFactory(
28                chameleon_port, display_facade)
29        self._resolution_comparer = factory.create_resolution_comparer()
30        self._screen_comparer = factory.create_screen_comparer(output_dir)
31        self._mirror_comparer = factory.create_mirror_comparer(output_dir)
32        self._calib_comparer = factory.create_calibration_comparer(output_dir)
33        self._calibration_image_tab_descriptor = None
34
35
36    def test_resolution(self, expected_resolution):
37        """Tests if the resolution of Chameleon matches with the one of CrOS.
38
39        @param expected_resolution: A tuple (width, height) for the expected
40                                    resolution.
41        @return: None if the check passes; otherwise, a string of error message.
42        """
43        return self._resolution_comparer.compare(expected_resolution)
44
45
46    def test_screen(self, expected_resolution, test_mirrored=None,
47                    error_list=None):
48        """Tests if the screen of Chameleon matches with the one of CrOS.
49
50        @param expected_resolution: A tuple (width, height) for the expected
51                                    resolution.
52        @param test_mirrored: True to test mirrored mode. False not to. None
53                              to test mirrored mode iff the current mode is
54                              mirrored.
55        @param error_list: A list to append the error message to or None.
56        @return: None if the check passes; otherwise, a string of error message.
57        """
58        if test_mirrored is None:
59            test_mirrored = self._display_facade.is_mirrored_enabled()
60
61        error = self._resolution_comparer.compare(expected_resolution)
62        if not error:
63            # Do two screen comparisons with and without hiding cursor, to
64            # work-around some devices still showing cursor on CrOS FB.
65            # TODO: Remove this work-around once crosbug/p/34524 got fixed.
66            error = self._screen_comparer.compare()
67            if error:
68                logging.info('Hide cursor and do screen comparison again...')
69                self._display_facade.hide_cursor()
70                error = self._screen_comparer.compare()
71
72            if error:
73                # On some ARM device, the internal FB has some issue and it
74                # won't get fixed in the near future. As this issue don't
75                # affect users, just the tests. So compare it with the
76                # calibration image directly as an workaround.
77                board = self._host.get_board().replace('board:', '')
78                if board in ['kevin']:
79                    # TODO(waihong): In mirrored mode, using calibration image
80                    # to compare is not feasible due to the size difference.
81                    # Skip the error first and think a better way to compare.
82                    if test_mirrored:
83                        raise error_lib.TestNAError('Test this item manually! '
84                                'Missing CrOS feature, not able to automate.')
85                    logging.info('Compare the calibration image directly...')
86                    error = self._calib_comparer.compare()
87
88        if not error and test_mirrored:
89            error = self._mirror_comparer.compare()
90        if error and error_list is not None:
91            error_list.append(error)
92        return error
93
94
95    def load_test_image(self, image_size, test_mirrored=None):
96        """Loads calibration image on the CrOS with logging
97
98        @param image_size: A tuple (width, height) conforms the resolution.
99        @param test_mirrored: True to test mirrored mode. False not to. None
100                              to test mirrored mode iff the current mode is
101                              mirrored.
102        """
103        if test_mirrored is None:
104            test_mirrored = self._display_facade.is_mirrored_enabled()
105        self._calibration_image_tab_descriptor = \
106            self._display_facade.load_calibration_image(image_size)
107        if not test_mirrored:
108            self._display_facade.move_to_display(
109                    self._display_facade.get_first_external_display_id())
110        self._display_facade.set_fullscreen(True)
111        logging.info('Waiting for calibration image to stabilize...')
112        time.sleep(self._TEST_IMAGE_STABILIZE_TIME)
113
114
115    def unload_test_image(self):
116        """Closes the tab in browser to unload the fullscreen test image."""
117        self._display_facade.close_tab(self._calibration_image_tab_descriptor)
118
119
120    def test_screen_with_image(self, expected_resolution, test_mirrored=None,
121                               error_list=None, chameleon_supported=True,
122                               retry_count=2):
123        """Tests the screen with image loaded.
124
125        @param expected_resolution: A tuple (width, height) for the expected
126                                    resolution.
127        @param test_mirrored: True to test mirrored mode. False not to. None
128                              to test mirrored mode iff the current mode is
129                              mirrored.
130        @param error_list: A list to append the error message to or None.
131        @param retry_count: A count to retry the screen test.
132        @param chameleon_supported: Whether resolution is supported by
133                                    chameleon. The DP RX doesn't support
134                                    4K resolution. The max supported resolution
135                                    is 2560x1600. See crbug/585900.
136        @return: None if the check passes; otherwise, a string of error message.
137        """
138        if test_mirrored is None:
139            test_mirrored = self._display_facade.is_mirrored_enabled()
140
141        if test_mirrored:
142            test_image_size = self._display_facade.get_internal_resolution()
143        else:
144            # DUT needs time to respond to the plug event
145            test_image_size = utils.wait_for_value_changed(
146                    self._display_facade.get_external_resolution,
147                    old_value=None)
148        error = None
149        if test_image_size != expected_resolution:
150            error = ('Screen size %s is not as expected %s!'
151                     % (str(test_image_size), str(expected_resolution)))
152            if test_mirrored:
153                # For the case of mirroring, depending on hardware vs
154                # software mirroring, screen size can be different.
155                logging.info('Warning: %s', error)
156                error = None
157            else:
158                error_list.append(error)
159
160        # Avoid browser tab focus issue. See crbug/1035014.
161        self._display_facade.set_fullscreen(True)
162        if chameleon_supported:
163            error = self._resolution_comparer.compare(expected_resolution)
164            if not error:
165                while retry_count:
166                    retry_count = retry_count - 1
167                    try:
168                        self.load_test_image(test_image_size)
169                        error = self.test_screen(expected_resolution, test_mirrored)
170                        if error is None:
171                            return error
172                        elif retry_count > 0:
173                            logging.info('Retry screen comparison again...')
174                    finally:
175                        self.unload_test_image()
176
177            if error and error_list is not None:
178                error_list.append(error)
179        return error
180
181
182    def check_external_display_connected(self, expected_display,
183                                         error_list=None):
184        """Checks the given external display connected.
185
186        @param expected_display: Name of the expected display or False
187                if no external display is expected.
188        @param error_list: A list to append the error message to or None.
189        @return: None if the check passes; otherwise, a string of error message.
190        """
191        error = None
192        if not self._display_facade.wait_external_display_connected(
193                expected_display):
194            error = 'Waited for display %s but timed out' % expected_display
195
196        if error and error_list is not None:
197            logging.error(error)
198            error_list.append(error)
199        return error
200