# 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_TYPE_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