• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2012 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 json, logging, threading, time, traceback
6
7from autotest_lib.client.common_lib import error
8from autotest_lib.server import autotest, test
9from autotest_lib.server.cros.faft.config.config import Config as FAFTConfig
10
11_RETRY_SUSPEND_ATTEMPTS = 1
12_RETRY_SUSPEND_MS = 10000
13_SUSPEND_WAIT_SECONDS = 30
14_BOOT_WAIT_SECONDS = 100
15
16
17class power_SuspendShutdown(test.test):
18    """Test power manager fallback to power-off if suspend fails."""
19    version = 1
20
21    def initialize(self, host):
22        """
23        Initial settings before running test.
24
25        @param host: Host/DUT object to run test on.
26
27        """
28        # save original boot id
29        self.orig_boot_id = host.get_boot_id()
30        self.host = host
31
32        # override /sys/power/state via bind mount
33        logging.info('binding /dev/full to /sys/power/state')
34        host.run('mount --bind /dev/full /sys/power/state')
35
36        # override suspend retry attempts via bind mount
37        logging.info('settings retry_suspend_attempts to %s',
38                     _RETRY_SUSPEND_ATTEMPTS)
39        host.run('echo %s > /tmp/retry_suspend_attempts;'
40                 ' mount --bind /tmp/retry_suspend_attempts'
41                 ' /usr/share/power_manager/retry_suspend_attempts'
42                 % _RETRY_SUSPEND_ATTEMPTS)
43
44        # override suspend retry interval via bind mount
45        logging.info('settings retry_suspend_ms to %s',
46                     _RETRY_SUSPEND_MS)
47        host.run('echo %s > /tmp/retry_suspend_ms;'
48                 ' mount --bind /tmp/retry_suspend_ms'
49                 ' /usr/share/power_manager/retry_suspend_ms'
50                 % _RETRY_SUSPEND_MS)
51
52        # restart powerd to pick up new retry settings
53        logging.info('restarting powerd')
54        host.run('restart powerd')
55        time.sleep(2)
56
57
58    def platform_check(self, platform_name):
59        """
60        Raises error if device does not have a lid.
61
62        @param platform_name: Name of the platform
63
64        """
65        client_attr = FAFTConfig(platform_name)
66
67        if not client_attr.has_lid:
68            raise error.TestError(
69                    'This test does nothing on devices without a lid.')
70
71        if client_attr.chrome_ec and not 'lid' in client_attr.ec_capability:
72            raise error.TestNAError("TEST IT MANUALLY! Chrome EC can't control "
73                    "lid on the device %s" % client_attr.platform)
74
75
76    def login_into_dut(self, client_autotest, thread_started_evt,
77                       exit_without_logout=True):
78        """
79        Runs the Desktopui_Simple login client test in a seperate thread. The
80        Desktopui_Simple client test will exit without logout.
81
82        @param client_autotest: Client autotest name to login into DUT
83
84        @param thread_started_evt: Thread attribute to start the thread
85
86        @param exit_without_logout: if flag is set thread exists without logout.
87                                    if not set, thread will wait fot logout
88                                    event.
89
90        """
91        logging.info('Login into client started')
92        thread_started_evt.set()
93        try:
94            self.autotest_client.run_test(client_autotest,
95                                          exit_without_logout=
96                                          exit_without_logout)
97        except:
98            logging.info('DUT login process failed')
99
100
101    def create_thread(self, client_autotest, exit_without_logout):
102        """
103        Created seperate thread for client test
104
105        @param client_autotest: Client autotest name to login into DUT
106
107        @param exit_without_logout: if flag is set thread exists without logout.
108                                    if not set, thread will wait fot logout
109                                    event.
110        @return t: thread object
111
112        """
113        thread_started_evt = threading.Event()
114        logging.info('Launching Desktopui_simplelogin thread')
115        try:
116            t = threading.Thread(target=self.login_into_dut,
117                                 args=(client_autotest,
118                                 thread_started_evt, exit_without_logout))
119        except:
120            raise error.TestError('Thread creation failed')
121        t.start()
122        thread_started_evt.wait()
123        logging.info('Login thread started')
124        return t
125
126
127    def logged_in(self):
128        """
129        Checks if the host has a logged in user.
130
131        @param host: Host/DUT object
132
133        @return True if a user is logged in on the device.
134
135        """
136        host = self.host
137        try:
138            out = host.run('cryptohome --action=status').stdout.strip()
139        except:
140            return False
141        try:
142            status = json.loads(out)
143        except ValueError:
144            logging.info('Cryptohome did not return a value, retrying.')
145            return False
146
147        return any((mount['mounted'] for mount in status['mounts']))
148
149
150    def run_once(self, client_autotest):
151        """
152        Run the acutal test on device.
153
154        @param client_autotest: Client autotest name to login into DUT
155
156        @param host: Host/DUT object
157
158        """
159        # check platform is capable of running the test
160        host = self.host
161        platform = host.run_output('mosys platform name')
162        self.platform_check(platform)
163        self.autotest_client = autotest.Autotest(host)
164        logging.info('platform is %s', platform)
165        exit_without_logout = True
166        t = self.create_thread(client_autotest, exit_without_logout)
167        t.join()
168
169        # Waiting for the login thread to finish
170        max_wait_time = 15
171        for check_count in range(int(max_wait_time)):
172            if check_count == max_wait_time:
173                raise error.TestError('Login thread is still'
174                                      'alive after %s seconds' % max_wait_time)
175            if t.is_alive():
176                time.sleep(1)
177            else:
178                logging.info('Login thread successfully finished')
179                break
180
181        # close the lid while logged_in to initiate suspend
182        logging.info('closing lid')
183        host.servo.lid_close()
184
185        # wait for power manager to give up and shut down
186        logging.info('waiting for power off')
187        host.wait_down(timeout=_SUSPEND_WAIT_SECONDS,
188                       old_boot_id=self.orig_boot_id)
189
190        # ensure host is now off
191        if host.is_up():
192            raise error.TestFail('DUT still up with lid closed')
193        else:
194            logging.info('good, host is now off')
195
196        # restart host
197        host.servo.lid_open()
198        host.wait_up(timeout=_BOOT_WAIT_SECONDS)
199
200
201    def cleanup(self):
202        """Clean up the mounts and restore the settings."""
203        # reopen lid - might still be closed due to failure
204        host = self.host
205        logging.info('reopening lid')
206        host.servo.lid_open()
207
208        # try to clean up the mess we've made if shutdown failed
209        if host.get_boot_id() == self.orig_boot_id:
210            # clean up mounts
211            logging.info('cleaning up bind mounts')
212            host.run('umount /sys/power/state'
213                     ' /usr/share/power_manager/retry_suspend_attempts'
214                     ' /usr/share/power_manager/retry_suspend_ms',
215                     ignore_status=True)
216
217            # restart powerd to pick up old retry settings
218            host.run('restart powerd')
219
220        # Reboot Device to logout and cleanup
221        logging.info('Server: reboot client')
222        try:
223            self.host.reboot()
224        except error.AutoservRebootError as e:
225            raise error.TestFail('%s.\nTest failed with error %s' % (
226                    traceback.format_exc(), str(e)))
227