1# Copyright (c) 2013 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, numpy, random, time 6 7from autotest_lib.client.bin import test, utils 8from autotest_lib.client.common_lib import error 9from autotest_lib.client.common_lib.cros.network import interface 10from autotest_lib.client.cros.networking import shill_proxy 11from autotest_lib.client.cros.power import power_suspend, sys_power 12 13class power_SuspendStress(test.test): 14 """Class for test.""" 15 version = 1 16 17 def initialize(self, duration, idle=False, init_delay=0, min_suspend=0, 18 min_resume=5, max_resume_window=3, check_connection=True, 19 suspend_iterations=None, suspend_state=''): 20 """ 21 Entry point. 22 23 @param duration: total run time of the test 24 @param idle: use sys_power.idle_suspend method. 25 (use with dummy_IdleSuspend) 26 @param init_delay: wait this many seconds before starting the test to 27 give parallel tests time to get started 28 @param min_suspend: suspend durations will be chosen randomly out of 29 the interval between min_suspend and min_suspend + 3 seconds. 30 @param min_resume: minimal time in seconds between suspends. 31 @param max_resume_window: maximum range to use between suspends. i.e., 32 we will stay awake between min_resume and min_resume + 33 max_resume_window seconds. 34 @param check_connection: If true, we check that the network interface 35 used for testing is up after resume. Otherwsie we reboot. 36 @param suspend_iterations: number of times to attempt suspend. If 37 !=None has precedence over duration. 38 @param suspend_state: Force to suspend to a specific 39 state ("mem" or "freeze"). If the string is empty, suspend 40 state is left to the default pref on the system. 41 """ 42 self._endtime = time.time() 43 if duration: 44 self._endtime += duration 45 self._init_delay = init_delay 46 self._min_suspend = min_suspend 47 self._min_resume = min_resume 48 self._max_resume_window = max_resume_window 49 self._check_connection = check_connection 50 self._suspend_iterations = suspend_iterations 51 self._suspend_state = suspend_state 52 self._method = sys_power.idle_suspend if idle else sys_power.suspend_for 53 54 def _done(self): 55 if self._suspend_iterations != None: 56 self._suspend_iterations -= 1 57 return self._suspend_iterations < 0 58 return time.time() >= self._endtime 59 60 def _get_default_network_interface(self): 61 iface = shill_proxy.ShillProxy().get_default_interface_name() 62 if not iface: 63 return None 64 return interface.Interface(iface) 65 66 def run_once(self): 67 time.sleep(self._init_delay) 68 self._suspender = power_suspend.Suspender( 69 self.resultsdir, method=self._method, 70 suspend_state=self._suspend_state) 71 # Find the interface which is used for most communication. 72 # We assume the interface connects to the gateway and has the lowest 73 # metric. 74 if self._check_connection: 75 iface = utils.poll_for_condition( 76 self._get_default_network_interface, 77 desc='Find default network interface') 78 logging.info('Found default network interface: %s', iface.name) 79 80 while not self._done(): 81 time.sleep(self._min_resume + 82 random.randint(0, self._max_resume_window)) 83 # Check the network interface to the caller is still available 84 if self._check_connection: 85 # Give a 10 second window for the network to come back. 86 try: 87 utils.poll_for_condition(iface.is_link_operational, 88 desc='Link is operational') 89 except utils.TimeoutError: 90 logging.error('Link to the server gone, reboot') 91 utils.system('reboot') 92 # Reboot may return; raise a TestFail() to abort too, even 93 # though the server likely won't see this. 94 raise error.TestFail('Link is gone; rebooting') 95 96 self._suspender.suspend(random.randint(0, 3) + self._min_suspend) 97 98 99 def postprocess_iteration(self): 100 if self._suspender.successes: 101 keyvals = {'suspend_iterations': len(self._suspender.successes)} 102 for key in self._suspender.successes[0]: 103 values = [result[key] for result in self._suspender.successes] 104 keyvals[key + '_mean'] = numpy.mean(values) 105 keyvals[key + '_stddev'] = numpy.std(values) 106 keyvals[key + '_min'] = numpy.amin(values) 107 keyvals[key + '_max'] = numpy.amax(values) 108 self.write_perf_keyval(keyvals) 109 if self._suspender.failures: 110 total = len(self._suspender.failures) 111 iterations = len(self._suspender.successes) + total 112 timeout = kernel = firmware = spurious = 0 113 for failure in self._suspender.failures: 114 if type(failure) is sys_power.SuspendTimeout: timeout += 1 115 if type(failure) is sys_power.KernelError: kernel += 1 116 if type(failure) is sys_power.FirmwareError: firmware += 1 117 if type(failure) is sys_power.SpuriousWakeupError: spurious += 1 118 if total == kernel + timeout: 119 raise error.TestWarn('%d non-fatal suspend failures in %d ' 120 'iterations (%d timeouts, %d kernel warnings)' % 121 (total, iterations, timeout, kernel)) 122 if total == 1: 123 # just throw it as is, makes aggregation on dashboards easier 124 raise self._suspender.failures[0] 125 raise error.TestFail('%d suspend failures in %d iterations (%d ' 126 'timeouts, %d kernel warnings, %d firmware errors, %d ' 127 'spurious wakeups)' % 128 (total, iterations, timeout, kernel, firmware, spurious)) 129 130 131 def cleanup(self): 132 """ 133 Clean this up before we wait ages for all the log copying to finish... 134 """ 135 self._suspender.finalize() 136