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