# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import json, logging, threading, time, traceback from autotest_lib.client.common_lib import error from autotest_lib.server import autotest, test from autotest_lib.server.cros.faft.config.config import Config as FAFTConfig _RETRY_SUSPEND_ATTEMPTS = 1 _RETRY_SUSPEND_MS = 10000 _SUSPEND_WAIT_SECONDS = 30 _BOOT_WAIT_SECONDS = 100 class power_SuspendShutdown(test.test): """Test power manager fallback to power-off if suspend fails.""" version = 1 def initialize(self, host): """ Initial settings before running test. @param host: Host/DUT object to run test on. """ # save original boot id self.orig_boot_id = host.get_boot_id() self.host = host # override /sys/power/state via bind mount logging.info('binding /dev/full to /sys/power/state') host.run('mount --bind /dev/full /sys/power/state') # override suspend retry attempts via bind mount logging.info('settings retry_suspend_attempts to %s', _RETRY_SUSPEND_ATTEMPTS) host.run('echo %s > /tmp/retry_suspend_attempts;' ' mount --bind /tmp/retry_suspend_attempts' ' /usr/share/power_manager/retry_suspend_attempts' % _RETRY_SUSPEND_ATTEMPTS) # override suspend retry interval via bind mount logging.info('settings retry_suspend_ms to %s', _RETRY_SUSPEND_MS) host.run('echo %s > /tmp/retry_suspend_ms;' ' mount --bind /tmp/retry_suspend_ms' ' /usr/share/power_manager/retry_suspend_ms' % _RETRY_SUSPEND_MS) # restart powerd to pick up new retry settings logging.info('restarting powerd') host.run('restart powerd') time.sleep(2) def platform_check(self, platform_name): """ Raises error if device does not have a lid. @param platform_name: Name of the platform """ client_attr = FAFTConfig(platform_name) if not client_attr.has_lid: raise error.TestError( 'This test does nothing on devices without a lid.') if client_attr.chrome_ec and not 'lid' in client_attr.ec_capability: raise error.TestNAError("TEST IT MANUALLY! Chrome EC can't control " "lid on the device %s" % client_attr.platform) def login_into_dut(self, client_autotest, thread_started_evt, exit_without_logout=True): """ Runs the Desktopui_Simple login client test in a seperate thread. The Desktopui_Simple client test will exit without logout. @param client_autotest: Client autotest name to login into DUT @param thread_started_evt: Thread attribute to start the thread @param exit_without_logout: if flag is set thread exists without logout. if not set, thread will wait fot logout event. """ logging.info('Login into client started') thread_started_evt.set() try: self.autotest_client.run_test(client_autotest, exit_without_logout= exit_without_logout) except: logging.info('DUT login process failed') def create_thread(self, client_autotest, exit_without_logout): """ Created seperate thread for client test @param client_autotest: Client autotest name to login into DUT @param exit_without_logout: if flag is set thread exists without logout. if not set, thread will wait fot logout event. @return t: thread object """ thread_started_evt = threading.Event() logging.info('Launching Desktopui_simplelogin thread') try: t = threading.Thread(target=self.login_into_dut, args=(client_autotest, thread_started_evt, exit_without_logout)) except: raise error.TestError('Thread creation failed') t.start() thread_started_evt.wait() logging.info('Login thread started') return t def logged_in(self): """ Checks if the host has a logged in user. @param host: Host/DUT object @return True if a user is logged in on the device. """ host = self.host try: out = host.run('cryptohome --action=status').stdout.strip() except: return False try: status = json.loads(out) except ValueError: logging.info('Cryptohome did not return a value, retrying.') return False return any((mount['mounted'] for mount in status['mounts'])) def run_once(self, client_autotest): """ Run the acutal test on device. @param client_autotest: Client autotest name to login into DUT @param host: Host/DUT object """ # check platform is capable of running the test host = self.host platform = host.run_output('mosys platform name') self.platform_check(platform) self.autotest_client = autotest.Autotest(host) logging.info('platform is %s', platform) exit_without_logout = True t = self.create_thread(client_autotest, exit_without_logout) t.join() # Waiting for the login thread to finish max_wait_time = 15 for check_count in range(int(max_wait_time)): if check_count == max_wait_time: raise error.TestError('Login thread is still' 'alive after %s seconds' % max_wait_time) if t.is_alive(): time.sleep(1) else: logging.info('Login thread successfully finished') break # close the lid while logged_in to initiate suspend logging.info('closing lid') host.servo.lid_close() # wait for power manager to give up and shut down logging.info('waiting for power off') host.wait_down(timeout=_SUSPEND_WAIT_SECONDS, old_boot_id=self.orig_boot_id) # ensure host is now off if host.is_up(): raise error.TestFail('DUT still up with lid closed') else: logging.info('good, host is now off') # restart host host.servo.lid_open() host.wait_up(timeout=_BOOT_WAIT_SECONDS) def cleanup(self): """Clean up the mounts and restore the settings.""" # reopen lid - might still be closed due to failure host = self.host logging.info('reopening lid') host.servo.lid_open() # try to clean up the mess we've made if shutdown failed if host.get_boot_id() == self.orig_boot_id: # clean up mounts logging.info('cleaning up bind mounts') host.run('umount /sys/power/state' ' /usr/share/power_manager/retry_suspend_attempts' ' /usr/share/power_manager/retry_suspend_ms', ignore_status=True) # restart powerd to pick up old retry settings host.run('restart powerd') # Reboot Device to logout and cleanup logging.info('Server: reboot client') try: self.host.reboot() except error.AutoservRebootError as e: raise error.TestFail('%s.\nTest failed with error %s' % ( traceback.format_exc(), str(e)))