• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2020 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
5import logging
6import time
7
8from autotest_lib.client.common_lib import error
9from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
10
11
12class firmware_ECChargingState(FirmwareTest):
13    """
14    Type-C servo-v4 based EC charging state test.
15    """
16    version = 1
17
18    # The delay to wait for the AC state to update.
19    AC_STATE_UPDATE_DELAY = 3
20
21    # We wait for up to 3 hrs for the battery to report fully charged.
22    FULL_CHARGE_TIMEOUT = 60 * 60 * 3
23
24    # The period to check battery state while charging.
25    CHECK_BATT_STATE_WAIT = 60
26
27    def initialize(self, host, cmdline_args):
28        super(firmware_ECChargingState, self).initialize(host, cmdline_args)
29        if not self.check_ec_capability(['battery', 'charging']):
30            raise error.TestNAError("Nothing needs to be tested on this DUT")
31        if self.servo.get_servo_version() != 'servo_v4_with_ccd_cr50':
32            raise error.TestNAError("This test can only be run with servo-v4 "
33                    "+ CCD. If you don't have a Type-C servo-v4, please run "
34                    "the test manually.")
35        if host.is_ac_connected() != True:
36            raise error.TestFail("This test must be run with AC power.")
37        self.switcher.setup_mode('normal')
38        self.ec.send_command("chan save")
39        self.ec.send_command("chan 0")
40        self.set_dut_low_power_idle_delay(20)
41
42    def cleanup(self):
43        try:
44            self.ec.send_command("chan restore")
45            self.restore_dut_low_power_idle_delay()
46        except Exception as e:
47            logging.error("Caught exception: %s", str(e))
48        super(firmware_ECChargingState, self).cleanup()
49
50    def check_ac_state(self):
51        """Check if AC is plugged."""
52        ac_state = int(self.ec.send_command_get_output("chgstate",
53            ["ac\s*=\s*(0|1)\s*"])[0][1])
54        if ac_state == 1:
55            return 'on'
56        elif ac_state == 0:
57            return 'off'
58        else:
59            return 'unknown'
60
61    def get_battery_level(self):
62        """Get battery charge percentage."""
63        batt_level = int(self.ec.send_command_get_output("battery",
64                ["Charge:\s+(\d+)\s+"])[0][1])
65        return batt_level
66
67    def run_once(self, host):
68        """Execute the main body of the test."""
69
70        if host.is_ac_connected() != True:
71            raise error.TestFail("This test must be run with AC power.")
72
73        logging.info("Suspend, unplug AC, and then wake up the device.")
74        self.suspend()
75        self.switcher.wait_for_client_offline()
76
77        # Call set_servo_v4_role_to_snk() instead of directly setting
78        # servo_v4 role to snk, so servo_v4_role can be recovered to
79        # default src in cleanup().
80        self.set_servo_v4_role_to_snk()
81        time.sleep(self.AC_STATE_UPDATE_DELAY)
82
83        # Verify servo v4 is sinking power.
84        if self.check_ac_state() != 'off':
85            raise error.TestFail("Fail to unplug AC.")
86
87        self.servo.power_normal_press()
88        self.switcher.wait_for_client()
89
90        batt_state = host.get_battery_state()
91        if batt_state != 'Discharging':
92            raise error.TestFail("Wrong battery state. Expected: "
93                    "Discharging, got: %s." % batt_state)
94
95        logging.info("Suspend, plug AC, and then wake up the device.")
96        self.suspend()
97        self.switcher.wait_for_client_offline()
98        self.servo.set_servo_v4_role('src')
99        time.sleep(self.AC_STATE_UPDATE_DELAY)
100
101        # Verify servo v4 is sourcing power.
102        if self.check_ac_state() != 'on':
103            raise error.TestFail("Fail to plug AC.")
104
105        self.servo.power_normal_press()
106        self.switcher.wait_for_client()
107
108        batt_state = host.get_battery_state()
109        if batt_state != 'Charging' and batt_state != 'Fully charged':
110            raise error.TestFail("Wrong battery state. Expected: "
111                    "Charging/Fully charged, got: %s." % batt_state)
112
113        logging.info("Keep charging until the battery reports fully charged.")
114        deadline = time.time() + self.FULL_CHARGE_TIMEOUT
115        while time.time() < deadline:
116            batt_state = host.get_battery_state()
117            if batt_state == 'Fully charged':
118                logging.info("The battery reports fully charged.")
119                return
120            elif batt_state == 'Charging':
121                logging.info("Wait for the battery to be fully charged. "
122                        "The current battery level is %d%%.",
123                        self.get_battery_level())
124            else:
125                raise error.TestFail("The battery state is %s. "
126                        "Is AC unplugged?", batt_state)
127            time.sleep(self.CHECK_BATT_STATE_WAIT)
128
129        raise error.TestFail("The battery does not report fully charged "
130                "before timeout is reached. The final battery level is %d%%.",
131                self.get_battery_level())
132