blob: 2fa08aa955f9e9888a33be570fb1522e4a344520 [file] [log] [blame]
# 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 logging, time
from autotest_lib.client.bin import test, utils
from autotest_lib.client.common_lib import error
from autotest_lib.client.cros import power_status, power_utils
def get_num_outputs_on():
"""
Retrieves the number of connected outputs that are on, using Xrandr.
Return value: integer value of number of connected outputs that are on.
"""
xrandr_state = utils.get_xrandr_output_state()
output_states = [xrandr_state[name] for name in xrandr_state]
return sum([1 if is_enabled else 0 for is_enabled in output_states])
class power_BacklightControl(test.test):
version = 1
# Minimum number of steps expected between min and max brightness levels.
_min_num_steps = 4
# Minimum required percentage change in energy rate between transitions
# (max -> min, min-> off)
_energy_rate_change_threshold_percent = 5
def initialize(self):
"""Perform necessary initialization prior to test run.
Private Attributes:
_backlight: power_utils.Backlight object
"""
super(power_BacklightControl, self).initialize()
self._backlight = None
def run_once(self):
# Require that this test be run on battery with at least 5% charge
status = power_status.get_status()
status.assert_battery_state(5)
prefs = { 'disable_als' : 1,
'ignore_external_policy' : 1,
'plugged_dim_ms' : 7200000,
'plugged_off_ms' : 9000000,
'plugged_suspend_ms' : 18000000,
'unplugged_dim_ms' : 7200000,
'unplugged_off_ms' : 9000000,
'unplugged_suspend_ms' : 18000000 }
self._pref_change = power_utils.PowerPrefChanger(prefs)
keyvals = {}
num_errors = 0
# These are the expected ratios of energy rate between max, min, and off
# (zero) brightness levels. e.g. when changing from max to min, the
# energy rate must become <= (max_energy_rate * max_to_min_factor).
max_to_min_factor = \
1.0 - self._energy_rate_change_threshold_percent / 100.0
min_to_off_factor = \
1.0 - self._energy_rate_change_threshold_percent / 100.0
off_to_max_factor = 1.0 / (max_to_min_factor * min_to_off_factor)
# Determine the number of outputs that are on.
starting_num_outputs_on = get_num_outputs_on()
if starting_num_outputs_on == 0:
raise error.TestFail('At least one display output must be on.')
keyvals['starting_num_outputs_on'] = starting_num_outputs_on
self._backlight = power_utils.Backlight()
keyvals['max_brightness'] = self._backlight.get_max_level()
if keyvals['max_brightness'] <= self._min_num_steps:
raise error.TestFail('Must have at least %d backlight levels' %
(self._min_num_steps + 1))
keyvals['initial_brightness'] = self._backlight.get_level()
self._wait_for_stable_energy_rate()
keyvals['initial_power_w'] = self._get_current_energy_rate()
self._backlight_controller = power_utils.BacklightController()
self._backlight_controller.set_brightness_to_max()
current_brightness = \
utils.wait_for_value(self._backlight.get_level,
max_threshold=keyvals['max_brightness'])
if current_brightness != keyvals['max_brightness']:
num_errors += 1
logging.error(('Failed to increase brightness to max, ' + \
'brightness is %d.') % current_brightness)
else:
self._wait_for_stable_energy_rate()
keyvals['max_brightness_power_w'] = self._get_current_energy_rate()
# Set brightness to minimum without going to zero.
# Note that we don't know what the minimum brightness is, so just set
# min_threshold=0 to use the timeout to wait for the brightness to
# settle.
self._backlight_controller.set_brightness_to_min()
current_brightness = utils.wait_for_value(
self._backlight.get_level,
min_threshold=(keyvals['max_brightness'] / 2 - 1))
if current_brightness >= keyvals['max_brightness'] / 2 or \
current_brightness == 0:
num_errors += 1
logging.error('Brightness is not at minimum non-zero level: %d' %
current_brightness)
else:
self._wait_for_stable_energy_rate()
keyvals['min_brightness_power_w'] = self._get_current_energy_rate()
# Turn off the screen by decreasing brightness one more time with
# allow_off=True.
self._backlight_controller.decrease_brightness(True)
current_brightness = utils.wait_for_value(
self._backlight.get_level, min_threshold=0)
if current_brightness != 0:
num_errors += 1
logging.error('Brightness is %d, expecting 0.' % current_brightness)
# Wait for screen to turn off.
num_outputs_on = utils.wait_for_value(
get_num_outputs_on, min_threshold=(starting_num_outputs_on - 1))
keyvals['outputs_on_after_screen_off'] = num_outputs_on
if num_outputs_on >= starting_num_outputs_on:
num_errors += 1
logging.error('At least one display must have been turned off. ' + \
'Number of displays on: %s' % num_outputs_on)
else:
self._wait_for_stable_energy_rate()
keyvals['screen_off_power_w'] = self._get_current_energy_rate()
# Set brightness to max.
self._backlight_controller.set_brightness_to_max()
current_brightness = utils.wait_for_value(
self._backlight.get_level, max_threshold=keyvals['max_brightness'])
if current_brightness != keyvals['max_brightness']:
num_errors += 1
logging.error(('Failed to increase brightness to max, ' + \
'brightness is %d.') % current_brightness)
# Verify that the same number of outputs are on as before.
num_outputs_on = get_num_outputs_on()
keyvals['outputs_on_at_end'] = num_outputs_on
if num_outputs_on != starting_num_outputs_on:
num_errors += 1
logging.error(('Number of displays turned on should be same as ' + \
'at start. Number of displays on: %s') %
num_outputs_on)
self._wait_for_stable_energy_rate()
keyvals['final_power_w'] = self._get_current_energy_rate()
# Energy rate must have changed significantly between transitions.
if 'max_brightness_power_w' in keyvals and \
'min_brightness_power_w' in keyvals and \
keyvals['min_brightness_power_w'] >= \
keyvals['max_brightness_power_w'] * max_to_min_factor:
num_errors += 1
logging.error('Power draw did not decrease enough when ' + \
'brightness was decreased from max to min.')
if 'screen_off_power_w' in keyvals and \
'min_brightness_power_w' in keyvals and \
keyvals['screen_off_power_w'] >= \
keyvals['min_brightness_power_w'] * min_to_off_factor:
num_errors += 1
logging.error('Power draw did not decrease enough when screen ' + \
'was turned off.')
if num_outputs_on == starting_num_outputs_on and \
'screen_off_power_w' in keyvals and \
keyvals['final_power_w'] <= \
keyvals['screen_off_power_w'] * off_to_max_factor:
num_errors += 1
logging.error('Power draw did not increase enough after ' + \
'turning screen on.')
self.write_perf_keyval(keyvals)
if num_errors > 0:
raise error.TestFail('Test failed with %d errors' % num_errors)
def cleanup(self):
if self._backlight:
self._backlight.restore()
super(power_BacklightControl, self).cleanup()
def _get_current_energy_rate(self):
return power_status.get_status().battery[0].energy_rate
def _wait_for_stable_energy_rate(self,
max_variation_percent=5,
sample_delay_sec=1,
window_size=10,
timeout_sec=30):
"""
Waits for the energy rate to stablize. Stability criterion:
The last |window_size| samples of energy rate do not deviate from
their mean by more than |max_variation_percent|.
Arguments:
max_variation_percent Percentage of allowed deviation from mean
energy rate to still be considered stable.
sample_delay_sec Time to wait between each reading of the
energy rate.
window_size Number of energy rate samples required to
measure stability. If there are more
samples than this amount, use only the last
|window_size| values.
timeout_sec If stability has not been attained after
this long, stop waiting.
Return value:
True if energy rate stabilized before timeout.
False if timed out waiting for energy rate to stabilize.
"""
start_time = time.time()
samples = []
max_variation_factor = max_variation_percent / 100.0
while time.time() - start_time < timeout_sec:
current_rate = self._get_current_energy_rate()
# Remove the oldest value if the list of energy rate samples is at
# the maximum limit |window_size|, before appending a new value.
if len(samples) >= window_size:
samples = samples[1:]
samples.append(current_rate)
mean = sum(samples) / len(samples)
if len(samples) >= window_size and \
max(samples) <= mean * (1 + max_variation_factor) and \
min(samples) >= mean * (1 - max_variation_factor):
return True
time.sleep(sample_delay_sec)
return False