xref: /aosp_15_r20/external/autotest/server/site_tests/display_LidCloseOpen/display_LidCloseOpen.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Lint as: python2, python3
2# Copyright 2014 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""This is a display lid close and open test using the Chameleon board."""
7
8import logging, time
9
10from autotest_lib.client.common_lib import error
11from autotest_lib.client.cros.chameleon import chameleon_port_finder
12from autotest_lib.client.cros.chameleon import chameleon_screen_test
13from autotest_lib.server import test
14from autotest_lib.server.cros.multimedia import remote_facade_factory
15
16class display_LidCloseOpen(test.test):
17    """External Display Lid Close/Open test. """
18    version = 1
19
20    # Time to check if device is suspended
21    TIMEOUT_SUSPEND_CHECK = 5
22    # Allowed timeout for the transition of suspend.
23    TIMEOUT_SUSPEND_TRANSITION = 30
24    # Allowed timeout for the transition of resume.
25    TIMEOUT_RESUME_TRANSITION = 60
26    # Time to allow for table video input
27    WAIT_TIME_STABLE_VIDEO_INPUT = 10
28    # Time to allow lid transition to take effect
29    WAIT_TIME_LID_TRANSITION = 5
30    # Time to allow display port plug transition to take effect
31    WAIT_TIME_PLUG_TRANSITION = 5
32    # Some boards do not play well with servo - crosbug.com/p/27591
33    INCOMPATIBLE_SERVO_BOARDS = ['daisy', 'falco', 'auron_paine']
34
35
36    def close_lid(self):
37        """Close lid through servo"""
38        logging.info('CLOSING LID...')
39        self.host.servo.lid_close()
40        time.sleep(self.WAIT_TIME_LID_TRANSITION)
41
42
43    def open_lid(self):
44        """Open lid through servo"""
45        logging.info('OPENING LID...')
46        self.host.servo.lid_open()
47        time.sleep(self.WAIT_TIME_LID_TRANSITION)
48
49
50    def check_primary_display_on_internal_screen(self):
51        """Checks primary display is on onboard/internal screen"""
52        if not self.display_facade.is_display_primary(internal=True):
53            raise error.TestFail('Primary display is not on internal screen')
54
55
56    def check_primary_display_on_external_screen(self):
57        """Checks primary display is on external screen"""
58        if not self.display_facade.is_display_primary(internal=False):
59            raise error.TestFail('Primary display is not on external screen')
60
61
62    def check_mode(self):
63        """Checks the display mode is as expected"""
64        if self.display_facade.is_mirrored_enabled() is not self.test_mirrored:
65            raise error.TestFail('Display mode %s is not preserved!' %
66                                 ('mirrored' if self.test_mirrored
67                                     else 'extended'))
68
69
70    def check_docked(self):
71        """Checks DUT is docked"""
72        # Device does not suspend
73        if self.host.ping_wait_down(timeout=self.TIMEOUT_SUSPEND_TRANSITION):
74            raise error.TestFail('Device suspends when docked!')
75        # Verify Chameleon displays main screen
76        self.check_primary_display_on_external_screen()
77        logging.info('DUT IS DOCKED!')
78        return self.chameleon_port.wait_video_input_stable(
79            timeout=self.WAIT_TIME_STABLE_VIDEO_INPUT)
80
81
82    def check_still_suspended(self):
83        """Checks DUT is (still) suspended"""
84        if not self.host.ping_wait_down(timeout=self.TIMEOUT_SUSPEND_CHECK):
85            raise error.TestFail('Device does not stay suspended!')
86        logging.info('DUT STILL SUSPENDED')
87
88
89    def check_external_display(self):
90        """Display status check"""
91        # Check connector
92        if self.screen_test.check_external_display_connected(
93                self.connector_used, self.errors) is None:
94            # Check mode is same as beginning of the test
95            self.check_mode()
96            # Check test image
97            resolution = self.chameleon_port.get_resolution()
98            self.screen_test.test_screen_with_image(
99                    resolution, self.test_mirrored, self.errors)
100
101
102    def run_once(self, host, plug_status, test_mirrored=False):
103
104        # Check for chromebook type devices
105        if not host.get_board_type() == 'CHROMEBOOK':
106            raise error.TestNAError('DUT is not Chromebook. Test Skipped')
107
108        # Check for incompatible with servo chromebooks
109        board_name = host.get_board().split(':')[1]
110        if board_name in self.INCOMPATIBLE_SERVO_BOARDS:
111            raise error.TestNAError(
112                    'DUT is incompatible with servo. Skipping test.')
113
114        self.host = host
115        self.test_mirrored = test_mirrored
116        self.errors = list()
117
118        # Check the servo object
119        if self.host.servo is None:
120            raise error.TestError('Invalid servo object found on the host.')
121
122        factory = remote_facade_factory.RemoteFacadeFactory(host)
123        display_facade = factory.create_display_facade()
124        chameleon_board = host.chameleon
125
126        chameleon_board.setup_and_reset(self.outputdir)
127        finder = chameleon_port_finder.ChameleonVideoInputFinder(
128                chameleon_board, display_facade)
129        for chameleon_port in finder.iterate_all_ports():
130            self.run_test_on_port(chameleon_port, display_facade, plug_status)
131
132
133    def run_test_on_port(self, chameleon_port, display_facade, plug_status):
134        """Run the test on the given Chameleon port.
135
136        @param chameleon_port: a ChameleonPorts object.
137        @param display_facade: a display facade object.
138        @param plug_status: the plugged status before_close, after_close,
139           and before_open
140        """
141        self.chameleon_port = chameleon_port
142        self.display_facade = display_facade
143        self.screen_test = chameleon_screen_test.ChameleonScreenTest(
144                self.host, chameleon_port, display_facade, self.outputdir)
145
146        # Get connector type used (HDMI,DP,...)
147        self.connector_used = self.display_facade.get_external_connector_name()
148        # Set main display mode for the test
149        self.display_facade.set_mirrored(self.test_mirrored)
150
151        for (plugged_before_close,
152             plugged_after_close,
153             plugged_before_open) in plug_status:
154            logging.info('TEST CASE: %s > CLOSE_LID > %s > %s > OPEN_LID',
155                'PLUG' if plugged_before_close else 'UNPLUG',
156                'PLUG' if plugged_after_close else 'UNPLUG',
157                'PLUG' if plugged_before_open else 'UNPLUG')
158
159            is_suspended = False
160            boot_id = self.host.get_boot_id()
161
162            # Plug before close
163            self.chameleon_port.set_plug(plugged_before_close)
164            self.chameleon_port.wait_video_input_stable(
165                    timeout=self.WAIT_TIME_STABLE_VIDEO_INPUT)
166
167            # Close lid and check
168            self.close_lid()
169            if plugged_before_close:
170                self.check_docked()
171            else:
172                self.host.test_wait_for_sleep(self.TIMEOUT_SUSPEND_TRANSITION)
173                is_suspended = True
174
175            # Plug after close and check
176            if plugged_after_close is not plugged_before_close:
177                self.chameleon_port.set_plug(plugged_after_close)
178                self.chameleon_port.wait_video_input_stable(
179                        timeout=self.WAIT_TIME_STABLE_VIDEO_INPUT)
180                if not plugged_before_close:
181                    self.check_still_suspended()
182                else:
183                    self.host.test_wait_for_sleep(
184                        self.TIMEOUT_SUSPEND_TRANSITION)
185                    is_suspended = True
186
187            # Plug before open and check
188            if plugged_before_open is not plugged_after_close:
189                self.chameleon_port.set_plug(plugged_before_open)
190                self.chameleon_port.wait_video_input_stable(
191                        timeout=self.WAIT_TIME_STABLE_VIDEO_INPUT)
192                if not plugged_before_close or not plugged_after_close:
193                    self.check_still_suspended()
194                else:
195                    self.host.test_wait_for_sleep(
196                        self.TIMEOUT_SUSPEND_TRANSITION)
197                    is_suspended = True
198
199            # Open lid and check
200            self.open_lid()
201            if is_suspended:
202                self.host.test_wait_for_resume(boot_id,
203                                               self.TIMEOUT_RESUME_TRANSITION)
204                is_suspended = False
205
206            # Check internal screen switch to primary display
207            self.check_primary_display_on_internal_screen()
208
209            # Plug monitor if not plugged, such that we can test the screen.
210            if not plugged_before_open:
211                self.chameleon_port.set_plug(True)
212                self.chameleon_port.wait_video_input_stable(
213                        timeout=self.WAIT_TIME_STABLE_VIDEO_INPUT)
214
215            # Check status
216            self.check_external_display()
217
218            if self.errors:
219                raise error.TestFail('; '.join(set(self.errors)))
220
221    def cleanup(self):
222        """Test cleanup"""
223        # Keep device in lid open sate.
224        self.open_lid()
225