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 5 import logging, time 6 from autotest_lib.client.bin import test, utils 7 from autotest_lib.client.common_lib import error 8 from autotest_lib.client.cros import power_status, power_utils 9 from autotest_lib.client.cros.graphics import graphics_utils 10 11 12 def get_num_outputs_on(): 13 """ 14 Retrieves the number of connected outputs that are on. 15 @return: integer value of number of connected outputs that are on. 16 """ 17 18 return graphics_utils.get_num_outputs_on(); 19 20 class power_BacklightControl(test.test): 21 version = 1 22 # Minimum number of steps expected between min and max brightness levels. 23 _min_num_steps = 4 24 # Minimum required percentage change in energy rate between transitions 25 # (max -> min, min-> off) 26 _energy_rate_change_threshold_percent = 5 27 28 29 def initialize(self): 30 """Perform necessary initialization prior to test run. 31 32 Private Attributes: 33 _backlight: power_utils.Backlight object 34 """ 35 super(power_BacklightControl, self).initialize() 36 self._backlight = None 37 38 39 def run_once(self): 40 # Require that this test be run on battery with at least 5% charge 41 status = power_status.get_status() 42 status.assert_battery_state(5) 43 44 prefs = { 'has_ambient_light_sensor' : 0, 45 'ignore_external_policy' : 1, 46 'plugged_dim_ms' : 7200000, 47 'plugged_off_ms' : 9000000, 48 'plugged_suspend_ms' : 18000000, 49 'unplugged_dim_ms' : 7200000, 50 'unplugged_off_ms' : 9000000, 51 'unplugged_suspend_ms' : 18000000 } 52 self._pref_change = power_utils.PowerPrefChanger(prefs) 53 54 keyvals = {} 55 num_errors = 0 56 57 # These are the expected ratios of energy rate between max, min, and off 58 # (zero) brightness levels. e.g. when changing from max to min, the 59 # energy rate must become <= (max_energy_rate * max_to_min_factor). 60 max_to_min_factor = \ 61 1.0 - self._energy_rate_change_threshold_percent / 100.0 62 min_to_off_factor = \ 63 1.0 - self._energy_rate_change_threshold_percent / 100.0 64 off_to_max_factor = 1.0 / (max_to_min_factor * min_to_off_factor) 65 66 # Determine the number of outputs that are on. 67 starting_num_outputs_on = get_num_outputs_on() 68 if starting_num_outputs_on == 0: 69 raise error.TestFail('At least one display output must be on.') 70 keyvals['starting_num_outputs_on'] = starting_num_outputs_on 71 72 self._backlight = power_utils.Backlight() 73 keyvals['max_brightness'] = self._backlight.get_max_level() 74 if keyvals['max_brightness'] <= self._min_num_steps: 75 raise error.TestFail('Must have at least %d backlight levels' % 76 (self._min_num_steps + 1)) 77 78 keyvals['initial_brightness'] = self._backlight.get_level() 79 80 self._wait_for_stable_energy_rate() 81 keyvals['initial_power_w'] = self._get_current_energy_rate() 82 83 self._backlight_controller = power_utils.BacklightController() 84 self._backlight_controller.set_brightness_to_max() 85 86 current_brightness = \ 87 utils.wait_for_value(self._backlight.get_level, 88 max_threshold=keyvals['max_brightness']) 89 if current_brightness != keyvals['max_brightness']: 90 num_errors += 1 91 logging.error(('Failed to increase brightness to max, ' + \ 92 'brightness is %d.') % current_brightness) 93 else: 94 self._wait_for_stable_energy_rate() 95 keyvals['max_brightness_power_w'] = self._get_current_energy_rate() 96 97 # Set brightness to minimum without going to zero. 98 # Note that we don't know what the minimum brightness is, so just set 99 # min_threshold=0 to use the timeout to wait for the brightness to 100 # settle. 101 self._backlight_controller.set_brightness_to_min() 102 current_brightness = utils.wait_for_value( 103 self._backlight.get_level, 104 min_threshold=(keyvals['max_brightness'] / 2 - 1)) 105 if current_brightness >= keyvals['max_brightness'] / 2 or \ 106 current_brightness == 0: 107 num_errors += 1 108 logging.error('Brightness is not at minimum non-zero level: %d' % 109 current_brightness) 110 else: 111 self._wait_for_stable_energy_rate() 112 keyvals['min_brightness_power_w'] = self._get_current_energy_rate() 113 114 # Turn off the screen by decreasing brightness one more time with 115 # allow_off=True. 116 self._backlight_controller.decrease_brightness(True) 117 current_brightness = utils.wait_for_value( 118 self._backlight.get_level, min_threshold=0) 119 if current_brightness != 0: 120 num_errors += 1 121 logging.error('Brightness is %d, expecting 0.' % current_brightness) 122 123 # Wait for screen to turn off. 124 num_outputs_on = utils.wait_for_value( 125 get_num_outputs_on, min_threshold=(starting_num_outputs_on - 1)) 126 keyvals['outputs_on_after_screen_off'] = num_outputs_on 127 if num_outputs_on >= starting_num_outputs_on: 128 num_errors += 1 129 logging.error('At least one display must have been turned off. ' + \ 130 'Number of displays on: %s' % num_outputs_on) 131 else: 132 self._wait_for_stable_energy_rate() 133 keyvals['screen_off_power_w'] = self._get_current_energy_rate() 134 135 # Set brightness to max. 136 self._backlight_controller.set_brightness_to_max() 137 current_brightness = utils.wait_for_value( 138 self._backlight.get_level, max_threshold=keyvals['max_brightness']) 139 if current_brightness != keyvals['max_brightness']: 140 num_errors += 1 141 logging.error(('Failed to increase brightness to max, ' + \ 142 'brightness is %d.') % current_brightness) 143 144 # Verify that the same number of outputs are on as before. 145 num_outputs_on = get_num_outputs_on() 146 keyvals['outputs_on_at_end'] = num_outputs_on 147 if num_outputs_on != starting_num_outputs_on: 148 num_errors += 1 149 logging.error(('Number of displays turned on should be same as ' + \ 150 'at start. Number of displays on: %s') % 151 num_outputs_on) 152 153 self._wait_for_stable_energy_rate() 154 keyvals['final_power_w'] = self._get_current_energy_rate() 155 156 # Energy rate must have changed significantly between transitions. 157 if 'max_brightness_power_w' in keyvals and \ 158 'min_brightness_power_w' in keyvals and \ 159 keyvals['min_brightness_power_w'] >= \ 160 keyvals['max_brightness_power_w'] * max_to_min_factor: 161 num_errors += 1 162 logging.error('Power draw did not decrease enough when ' + \ 163 'brightness was decreased from max to min.') 164 165 if 'screen_off_power_w' in keyvals and \ 166 'min_brightness_power_w' in keyvals and \ 167 keyvals['screen_off_power_w'] >= \ 168 keyvals['min_brightness_power_w'] * min_to_off_factor: 169 num_errors += 1 170 logging.error('Power draw did not decrease enough when screen ' + \ 171 'was turned off.') 172 173 if num_outputs_on == starting_num_outputs_on and \ 174 'screen_off_power_w' in keyvals and \ 175 keyvals['final_power_w'] <= \ 176 keyvals['screen_off_power_w'] * off_to_max_factor: 177 num_errors += 1 178 logging.error('Power draw did not increase enough after ' + \ 179 'turning screen on.') 180 181 self.write_perf_keyval(keyvals) 182 183 if num_errors > 0: 184 raise error.TestFail('Test failed with %d errors' % num_errors) 185 186 187 def cleanup(self): 188 if self._backlight: 189 self._backlight.restore() 190 super(power_BacklightControl, self).cleanup() 191 192 193 def _get_current_energy_rate(self): 194 return power_status.get_status().battery[0].energy_rate 195 196 197 def _wait_for_stable_energy_rate(self, 198 max_variation_percent=5, 199 sample_delay_sec=1, 200 window_size=10, 201 timeout_sec=30): 202 """ 203 Waits for the energy rate to stablize. Stability criterion: 204 The last |window_size| samples of energy rate do not deviate from 205 their mean by more than |max_variation_percent|. 206 207 Arguments: 208 max_variation_percent Percentage of allowed deviation from mean 209 energy rate to still be considered stable. 210 sample_delay_sec Time to wait between each reading of the 211 energy rate. 212 window_size Number of energy rate samples required to 213 measure stability. If there are more 214 samples than this amount, use only the last 215 |window_size| values. 216 timeout_sec If stability has not been attained after 217 this long, stop waiting. 218 219 Return value: 220 True if energy rate stabilized before timeout. 221 False if timed out waiting for energy rate to stabilize. 222 """ 223 start_time = time.time() 224 samples = [] 225 max_variation_factor = max_variation_percent / 100.0 226 while time.time() - start_time < timeout_sec: 227 current_rate = self._get_current_energy_rate() 228 229 # Remove the oldest value if the list of energy rate samples is at 230 # the maximum limit |window_size|, before appending a new value. 231 if len(samples) >= window_size: 232 samples = samples[1:] 233 samples.append(current_rate) 234 235 mean = sum(samples) / len(samples) 236 if len(samples) >= window_size and \ 237 max(samples) <= mean * (1 + max_variation_factor) and \ 238 min(samples) >= mean * (1 - max_variation_factor): 239 return True 240 241 time.sleep(sample_delay_sec) 242 243 return False 244