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