Home | History | Annotate | Download | only in power_BacklightControl
      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