• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2010 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 glob, os
6from autotest_lib.client.bin import test
7from autotest_lib.client.common_lib import error, utils
8
9class power_ProbeDriver(test.test):
10    """Confirms that AC driver is loaded and functioning
11    unless device is AC only."""
12    version = 1
13    power_supply_path = '/sys/class/power_supply/*'
14
15    def run_once(self, test_which='Mains'):
16        # This test doesn't apply to systems that run on AC only.
17        cmd = "mosys psu type"
18        if utils.system_output(cmd, ignore_status=True).strip() == "AC_only":
19            return
20        ac_paths  = []
21        bat_paths = []
22        # Gather power supplies
23        for path in glob.glob(power_ProbeDriver.power_supply_path):
24            type_path = os.path.join(path, 'type')
25            if not os.path.exists(type_path):
26                continue
27            # With the advent of USB Type-C, mains power might show up as
28            # one of several variants of USB.
29            psu_type = utils.read_one_line(type_path)
30            if any( [psu_type == 'Mains', psu_type == 'USB',
31                     psu_type == 'USB_DCP', psu_type == 'USB_CDP',
32                     psu_type == 'USB_TYPE_C', psu_type == 'USB_PD',
33                     psu_type == 'USB_PD_DRP'] ):
34                ac_paths.append(path)
35            elif psu_type == 'Battery':
36                bat_paths.append(path)
37        run_dict = { 'Mains': self.run_ac, 'Battery': self.run_bat }
38        run = run_dict.get(test_which)
39        if run:
40            run(ac_paths, bat_paths)
41        else:
42            raise error.TestNAError('Unknown test type: %s' % test_which)
43
44    def run_ac(self, ac_paths, bat_paths):
45        """Checks AC driver.
46
47        @param ac_paths: sysfs AC entries
48        @param bat_paths: sysfs battery entries
49        """
50        if not ac_paths:
51            raise error.TestFail('No line power devices found in %s' %
52                                 power_ProbeDriver.power_supply_path)
53
54        if not any([self._online(ac_path) for ac_path in ac_paths]):
55            raise error.TestFail('Line power is not connected')
56
57        # if there are batteries, test fails if one of them is discharging
58        # note: any([]) == False, so we don't have to test len(bat_paths) > 0
59        if any(self._is_discharging(bat_path, ac_paths)
60               for bat_path in bat_paths
61               if self._present(bat_path)):
62            raise error.TestFail('One of batteries is discharging')
63
64    def run_bat(self, ac_paths, bat_paths):
65        """ Checks batteries.
66
67        @param ac_paths: sysfs AC entries
68        @param bat_paths: sysfs battery entries
69        """
70        if len(bat_paths) == 0:
71            raise error.TestFail('Find no batteries')
72
73        presented = [bat_path for bat_path in bat_paths
74                     if self._present(bat_path)]
75        if len(presented) == 0:
76            raise error.TestFail('No batteries are presented')
77
78        if all(not self._is_discharging(bat_path, ac_paths) for bat_path
79               in presented):
80            raise error.TestFail('No batteries are discharging')
81
82        if any(self._online(ac_path) for ac_path in ac_paths):
83            raise error.TestFail('One of ACs is online')
84
85    def _online(self, ac_path):
86        online_path = os.path.join(ac_path, 'online')
87        if not os.path.exists(online_path):
88            raise error.TestFail('online path does not exist: %s' % online_path)
89        online = utils.read_one_line(online_path)
90        return online == '1'
91
92    def _has_property(self, bat_path, field):
93        """
94        Indicates whether a battery sysfs has the given field.
95
96        Fields:
97        str     bat_path:           Battery sysfs path
98        str     field:              Sysfs field to test for.
99
100        Return value:
101        bool    True if the field exists, False otherwise.
102        """
103        return os.path.exists(os.path.join(bat_path, field))
104
105    def _read_property(self, bat_path, field):
106        """
107        Reads the contents of a sysfs field for a battery sysfs.
108
109        Fields:
110        str     bat_path:           Battery sysfs path
111        str     field:              Sysfs field to read.
112
113        Return value:
114        str     The contents of the sysfs field.
115        """
116        property_path = os.path.join(bat_path, field)
117        if not self._has_property(bat_path, field):
118            raise error.TestNAError('Path not found: %s' % property_path)
119        return utils.read_one_line(property_path)
120
121    def _present(self, bat_path):
122        """
123        Indicates whether a battery is present, based on sysfs status.
124
125        Fields:
126        str     bat_path:           Battery sysfs path
127
128        Return value:
129        bool    True if the battery is present, False otherwise.
130        """
131        return self._read_property(bat_path, 'present') == '1'
132
133    def _is_discharging(self, bat_path, ac_paths):
134        """
135        Indicates whether a battery is discharging, based on sysfs status.
136
137        Sometimes the sysfs will not show status='Discharging' when actually
138        discharging.  So this function looks at both battery sysfs and AC sysfs.
139        If the battery is discharging, there will be no line power and the
140        power/current draw will be nonzero.
141
142        Fields:
143        str     bat_path:           Battery sysfs path
144        str[]   ac_paths:           List of AC sysfs paths
145
146        Return value:
147        bool    True if the battery is discharging, False otherwise.
148        """
149        if self._read_property(bat_path, 'status') == 'Discharging':
150            return True
151        if all(not self._online(ac_path) for ac_path in ac_paths):
152            if (self._has_property(bat_path, 'power_now') and
153                self._read_property(bat_path, 'power_now') != '0'):
154                return True
155            if (self._has_property(bat_path, 'current_now') and
156                self._read_property(bat_path, 'current_now') != '0'):
157                return True
158        return False
159