| # Copyright (c) 2010 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 glob, os |
| from autotest_lib.client.bin import test |
| from autotest_lib.client.common_lib import error, utils |
| |
| class power_ProbeDriver(test.test): |
| """Confirms that AC driver is loaded and functioning |
| unless device is AC only.""" |
| version = 1 |
| power_supply_path = '/sys/class/power_supply/*' |
| |
| def run_once(self, test_which='Mains'): |
| # This test doesn't apply to systems that run on AC only. |
| cmd = "mosys psu type" |
| if utils.system_output(cmd, ignore_status=True).strip() == "AC_only": |
| return |
| ac_paths = [] |
| bat_paths = [] |
| # Gather power supplies |
| for path in glob.glob(power_ProbeDriver.power_supply_path): |
| type_path = os.path.join(path, 'type') |
| if not os.path.exists(type_path): |
| continue |
| # With the advent of USB Type-C, mains power might show up as |
| # one of several variants of USB. |
| psu_type = utils.read_one_line(type_path) |
| if any( [psu_type == 'Mains', psu_type == 'USB', |
| psu_type == 'USB_DCP', psu_type == 'USB_CDP', |
| psu_type == 'USB_C', psu_type == 'USB_PD', |
| psu_type == 'USB_PD_DRP'] ): |
| ac_paths.append(path) |
| elif psu_type == 'Battery': |
| bat_paths.append(path) |
| run_dict = { 'Mains': self.run_ac, 'Battery': self.run_bat } |
| run = run_dict.get(test_which) |
| if run: |
| run(ac_paths, bat_paths) |
| else: |
| raise error.TestNAError('Unknown test type: %s' % test_which) |
| |
| def run_ac(self, ac_paths, bat_paths): |
| """Checks AC driver. |
| |
| @param ac_paths: sysfs AC entries |
| @param bat_paths: sysfs battery entries |
| """ |
| if not ac_paths: |
| raise error.TestFail('No line power devices found in %s' % |
| power_ProbeDriver.power_supply_path) |
| |
| if not any([self._online(ac_path) for ac_path in ac_paths]): |
| raise error.TestFail('Line power is not connected') |
| |
| # if there are batteries, test fails if one of them is discharging |
| # note: any([]) == False, so we don't have to test len(bat_paths) > 0 |
| if any(self._is_discharging(bat_path, ac_paths) |
| for bat_path in bat_paths |
| if self._present(bat_path)): |
| raise error.TestFail('One of batteries is discharging') |
| |
| def run_bat(self, ac_paths, bat_paths): |
| """ Checks batteries. |
| |
| @param ac_paths: sysfs AC entries |
| @param bat_paths: sysfs battery entries |
| """ |
| if len(bat_paths) == 0: |
| raise error.TestFail('Find no batteries') |
| |
| presented = [bat_path for bat_path in bat_paths |
| if self._present(bat_path)] |
| if len(presented) == 0: |
| raise error.TestFail('No batteries are presented') |
| |
| if all(not self._is_discharging(bat_path, ac_paths) for bat_path |
| in presented): |
| raise error.TestFail('No batteries are discharging') |
| |
| if any(self._online(ac_path) for ac_path in ac_paths): |
| raise error.TestFail('One of ACs is online') |
| |
| def _online(self, ac_path): |
| online_path = os.path.join(ac_path, 'online') |
| if not os.path.exists(online_path): |
| raise error.TestFail('online path does not exist: %s' % online_path) |
| online = utils.read_one_line(online_path) |
| return online == '1' |
| |
| def _has_property(self, bat_path, field): |
| """ |
| Indicates whether a battery sysfs has the given field. |
| |
| Fields: |
| str bat_path: Battery sysfs path |
| str field: Sysfs field to test for. |
| |
| Return value: |
| bool True if the field exists, False otherwise. |
| """ |
| return os.path.exists(os.path.join(bat_path, field)) |
| |
| def _read_property(self, bat_path, field): |
| """ |
| Reads the contents of a sysfs field for a battery sysfs. |
| |
| Fields: |
| str bat_path: Battery sysfs path |
| str field: Sysfs field to read. |
| |
| Return value: |
| str The contents of the sysfs field. |
| """ |
| property_path = os.path.join(bat_path, field) |
| if not self._has_property(bat_path, field): |
| raise error.TestNAError('Path not found: %s' % property_path) |
| return utils.read_one_line(property_path) |
| |
| def _present(self, bat_path): |
| """ |
| Indicates whether a battery is present, based on sysfs status. |
| |
| Fields: |
| str bat_path: Battery sysfs path |
| |
| Return value: |
| bool True if the battery is present, False otherwise. |
| """ |
| return self._read_property(bat_path, 'present') == '1' |
| |
| def _is_discharging(self, bat_path, ac_paths): |
| """ |
| Indicates whether a battery is discharging, based on sysfs status. |
| |
| Sometimes the sysfs will not show status='Discharging' when actually |
| discharging. So this function looks at both battery sysfs and AC sysfs. |
| If the battery is discharging, there will be no line power and the |
| power/current draw will be nonzero. |
| |
| Fields: |
| str bat_path: Battery sysfs path |
| str[] ac_paths: List of AC sysfs paths |
| |
| Return value: |
| bool True if the battery is discharging, False otherwise. |
| """ |
| if self._read_property(bat_path, 'status') == 'Discharging': |
| return True |
| if all(not self._online(ac_path) for ac_path in ac_paths): |
| if (self._has_property(bat_path, 'power_now') and |
| self._read_property(bat_path, 'power_now') != '0'): |
| return True |
| if (self._has_property(bat_path, 'current_now') and |
| self._read_property(bat_path, 'current_now') != '0'): |
| return True |
| return False |