244 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			244 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
| # 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.graphics import graphics_utils
 | |
| from autotest_lib.client.cros.power import power_status, power_utils
 | |
| 
 | |
| 
 | |
| def get_num_outputs_on():
 | |
|     """
 | |
|     Retrieves the number of connected outputs that are on.
 | |
|     @return: integer value of number of connected outputs that are on.
 | |
|     """
 | |
| 
 | |
|     return graphics_utils.get_num_outputs_on();
 | |
| 
 | |
| 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 = { 'has_ambient_light_sensor' : 0,
 | |
|                   '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.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
 |