xref: /aosp_15_r20/external/autotest/client/site_tests/power_BatteryDrain/power_BatteryDrain.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Lint as: python2, python3
2# Copyright 2019 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5import logging
6
7from autotest_lib.client.bin import test
8from autotest_lib.client.common_lib import error
9from autotest_lib.client.common_lib.cros import chrome
10from autotest_lib.client.common_lib import utils
11from autotest_lib.client.cros.power import power_status
12from autotest_lib.client.cros.power import power_utils
13
14class power_BatteryDrain(test.test):
15    """Not a test, but a utility for server tests to drain the battery below
16    a certain threshold within a certain timeframe."""
17    version = 1
18
19    backlight = None
20    keyboard_backlight = None
21
22    tick_count = 0
23
24    url = 'https://crospower.page.link/power_BatteryDrain'
25
26    def cleanup(self):
27        '''Cleanup for a test run'''
28        if self._force_discharge:
29            if not power_utils.charge_control_by_ectool(True):
30                logging.warning('Can not restore from force discharge.')
31        if self.backlight:
32            self.backlight.restore()
33        if self.keyboard_backlight:
34            default_level = self.keyboard_backlight.get_default_level()
35            self.keyboard_backlight.set_level(default_level)
36
37    def run_once(self, drain_to_percent, drain_timeout, force_discharge=False):
38        '''
39        Entry point of this test. The DUT must not be connected to AC.
40
41        It turns the screen and keyboard backlight up as high as possible, and
42        then opens Chrome to a WebGL heavy webpage. I also tried using a
43        dedicated tool for stress-testing the CPU
44        (https://github.com/intel/psst), but that only drew 27 watts on my DUT,
45        compared with 35 watts using the WebGL website. If you find a better
46        way to use a lot of power, please modify this test!
47
48        @param drain_to_percent: Battery percentage to drain to.
49        @param drain_timeout: In seconds.
50        @param force_discharge: Force discharge even with AC plugged in.
51        '''
52        status = power_status.get_status()
53        if not status.battery:
54            raise error.TestNAError('DUT has no battery. Test Skipped')
55
56        self._force_discharge = force_discharge
57        if force_discharge:
58            if not power_utils.charge_control_by_ectool(False):
59                raise error.TestError('Could not run battery force discharge.')
60
61        ac_error = error.TestFail('DUT is on AC power, but should not be')
62        if not force_discharge and status.on_ac():
63            raise ac_error
64
65        self.backlight = power_utils.Backlight()
66        self.backlight.set_percent(100)
67        try:
68            self.keyboard_backlight = power_utils.KbdBacklight()
69            self.keyboard_backlight.set_percent(100)
70        except power_utils.KbdBacklightException as e:
71            logging.info("Assuming no keyboard backlight due to %s", str(e))
72            self.keyboard_backlight = None
73
74        with chrome.Chrome(init_network_controller=True) as cr:
75            tab = cr.browser.tabs.New()
76            tab.Navigate(self.url)
77
78            logging.info(
79                'Waiting {} seconds for battery to drain to {} percent'.format(
80                    drain_timeout, drain_to_percent))
81
82            def is_battery_low_enough():
83                """Check if battery level reach target."""
84                status.refresh()
85                if not force_discharge and status.on_ac():
86                    raise ac_error
87                self.tick_count += 1
88                if self.tick_count % 60 == 0:
89                    logging.info('Battery charge percent: {}'.format(
90                            status.percent_display_charge()))
91                return status.percent_display_charge() <= drain_to_percent
92
93            err = error.TestFail(
94                "Battery did not drain to {} percent in {} seconds".format(
95                    drain_to_percent, drain_timeout))
96            utils.poll_for_condition(is_battery_low_enough,
97                                            exception=err,
98                                            timeout=drain_timeout,
99                                            sleep_interval=1)
100