1# Lint as: python2, python3 2# Copyright (c) 2012 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 6import logging, os, tempfile, threading 7from autotest_lib.client.bin import test, utils 8from autotest_lib.client.common_lib import error 9from autotest_lib.client.common_lib.cros import chrome 10 11POWER_MANAGER_SETTINGS = { 12 'plugged_dim_ms': 1000, 13 'plugged_off_ms': 5000, 14 'plugged_suspend_ms': 10000, 15 'unplugged_dim_ms': 1000, 16 'unplugged_off_ms': 5000, 17 'unplugged_suspend_ms': 10000, 18 'disable_idle_suspend': 0, 19 'ignore_external_policy': 1, 20} 21 22SUSPEND_TIMEOUT_MS = 30000 23 24 25class power_IdleSuspend(test.test): 26 """ 27 Verify power manager tries to suspend while idle. 28 29 This test does not actually allow the system to suspend. Instead, 30 it replaces /sys/power/state with a pipe and waits until "mem" is 31 written to it. Such a write would normally cause suspend. 32 """ 33 version = 1 34 mounts = () 35 36 def initialize(self): 37 super(power_IdleSuspend, self).initialize() 38 self.mounts = [] 39 40 41 def setup_power_manager(self): 42 """Configures powerd for the test.""" 43 # create directory for temporary settings 44 self.tempdir = tempfile.mkdtemp(prefix='IdleSuspend.') 45 logging.info('using temporary directory %s', self.tempdir) 46 47 # override power manager settings 48 for key, val in list(POWER_MANAGER_SETTINGS.items()): 49 logging.info('overriding %s to %s', key, val) 50 tmp_path = '%s/%s' % (self.tempdir, key) 51 mount_path = '/usr/share/power_manager/%s' % key 52 utils.write_one_line(tmp_path, str(val)) 53 utils.run('mount --bind %s %s' % (tmp_path, mount_path)) 54 self.mounts.append(mount_path) 55 56 # override /sys/power/state with fifo 57 fifo_path = '%s/sys_power_state' % self.tempdir 58 os.mkfifo(fifo_path) 59 utils.run('mount --bind %s /sys/power/state' % fifo_path) 60 self.mounts.append('/sys/power/state') 61 62 63 def wait_for_suspend(self): 64 """Thread callback to watch for powerd to announce idle transition.""" 65 # block reading new power state from /sys/power/state 66 sys_power_state = open('/sys/power/state') 67 self.new_power_state = sys_power_state.read() 68 logging.info('new power state: %s', self.new_power_state) 69 70 71 def run_once(self): 72 with chrome.Chrome(): 73 # stop power manager before reconfiguring 74 # TODO: Consider checking to see if powerd is running. 75 # If it isn't, the test currently fails here. 76 logging.info('stopping powerd') 77 utils.run('stop powerd') 78 79 # override power manager settings 80 self.setup_power_manager() 81 82 # start thread to wait for suspend 83 self.new_power_state = None 84 thread = threading.Thread(target=self.wait_for_suspend) 85 thread.start() 86 87 # touch OOBE completed file so powerd won't ignore idle state. 88 utils.run('touch /home/chronos/.oobe_completed') 89 90 # restart powerd to pick up new settings 91 logging.info('restarting powerd') 92 utils.run('start powerd') 93 94 # wait for idle suspend 95 thread.join(SUSPEND_TIMEOUT_MS / 1000.) 96 97 if thread.is_alive(): 98 # join timed out - powerd didn't write to /sys/power/state 99 raise error.TestFail('timed out waiting for suspend') 100 101 if self.new_power_state is None: 102 # probably an exception in the thread, check the log 103 raise error.TestError('reader thread crashed') 104 105 if self.new_power_state.strip() not in ['mem', 'freeze']: 106 err_str = 'bad power state written to /sys/power/state' 107 raise error.TestFail(err_str) 108 109 110 def cleanup(self): 111 # restore original power manager settings 112 for mount in self.mounts: 113 logging.info('restoring %s', mount) 114 utils.run('umount -l %s' % mount) 115 116 # restart powerd to pick up original settings 117 logging.info('restarting powerd') 118 utils.run('restart powerd') 119 120 super(power_IdleSuspend, self).cleanup() 121