1# Copyright (c) 2014 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, re, time
6from autotest_lib.server import autotest, test
7from autotest_lib.client.common_lib import error
8
9_CHARGING = 'CHARGING'
10_DISCHARGING = 'DISCHARGING'
11_WAIT_SECS_AFTER_SWITCH = 5
12_LONG_TIMEOUT = 120
13_CLIENT_LOGIN = 'desktopui_SimpleLogin'
14_WAKE_PRESS_IN_SEC = 0.2
15_SUSPEND_TIME = 10
16
17class platform_PowerStatusStress(test.test):
18    """Uses RPM and servo to test the power_supply_info output. """
19    version = 1
20
21
22    def action_login(self):
23        """Login i.e. runs running client test
24
25        @exception TestFail failed to login within timeout.
26
27        """
28        self.autotest_client.run_test(_CLIENT_LOGIN,
29                                      exit_without_logout=True)
30
31
32    def wait_to_suspend(self, suspend_timeout = _LONG_TIMEOUT):
33        """Wait for DUT to suspend.
34
35        @param suspend_timeout: Time in seconds to wait to disconnect
36
37        @exception TestFail if fail to suspend/disconnect in time
38
39        """
40        if not self.host.ping_wait_down(timeout=suspend_timeout):
41            raise error.TestFail("Unable to suspend in %s sec" %
42                                 suspend_timeout)
43
44
45    def wait_to_come_up(self, resume_timeout = _LONG_TIMEOUT):
46        """Wait for DUT to resume.
47
48        @param resume_timeout: Time in seconds to wait to come up
49
50        @exception TestFail if fail to come_up in time
51
52        """
53        if not self.host.wait_up(timeout=resume_timeout):
54            raise error.TestFail("Unable to resume in %s sec" %
55                                 resume_timeout)
56
57
58    def do_suspend_resume(self):
59        """ Suspends the DUT through powerd_dbus_suspend
60        """
61        #Suspend
62        logging.debug('Suspending...')
63        if self.has_lid:
64            self.host.servo.lid_close()
65            self.wait_to_suspend()
66            time.sleep(_SUSPEND_TIME)
67        else:
68            self.host.suspend(suspend_time=_SUSPEND_TIME)
69
70        #Resume
71        logging.debug('Resuming...')
72        if self.has_lid:
73            self.host.servo.lid_open()
74        else:
75            self.host.servo.power_key(_WAKE_PRESS_IN_SEC)
76        self.wait_to_come_up()
77
78
79    def cleanup(self):
80        """ Finish as powered on and lid open"""
81        self.host.power_on()
82        self.host.servo.lid_open()
83
84
85    def switch_power_and_verify(self, powered_on, expected):
86        """ Main action on switching the power state, and verifying status
87
88        @param powered_on: a boolean ON if True, OFF else
89        @param expected: touple of cmd and values to verify
90
91        @exception TestFail  if line_power or battery state do not match
92        """
93        bat_state = _CHARGING if powered_on else _DISCHARGING,
94        logging.info('Switching status to %s ', bat_state)
95        if powered_on:
96            self.host.power_on()
97        else:
98            self.host.power_off()
99        time.sleep(_WAIT_SECS_AFTER_SWITCH)
100
101        # Get power_supply_info output
102        psi_output = self.host.run('power_supply_info').stdout.strip()
103        psi_output = psi_output.replace('\n', '')
104
105        exp_psi_online, exp_psi_enum_type, exp_psi_bat_state = expected
106
107        is_psi_online = re.match(r'.+online:\s+%s.+' % exp_psi_online,
108                                 psi_output) is not None
109        is_psi_enum_type = re.match(r'.+enum type:\s+%s.+' % exp_psi_enum_type,
110                                    psi_output) is not None
111        is_psi_bat_state = re.match(r'.+state:\s+%s.+' % exp_psi_bat_state,
112                                    psi_output) is not None
113
114        if not all([is_psi_online, is_psi_enum_type, is_psi_bat_state]):
115            raise error.TestFail('Bad %s state!' % bat_state)
116
117
118    def run_once(self, host, loop_count):
119        self.host = host
120        dut_type = host.get_board_type()
121        if dut_type != 'CHROMEBOOK':
122            raise error.TestNAError(
123                    'This test is not supported on %s' %  dut_type)
124        self.autotest_client = autotest.Autotest(self.host)
125
126        # Start as powered on
127        if self.host.has_power():
128            self.host.power_on()
129        else:
130            raise error.TestFail('No RPM is setup to device')
131
132        # Check if DUT has lid.
133        self.has_lid = True
134        if self.host.servo.get('lid_open') == 'not_applicable':
135            self.has_lid = False
136        else:
137            # Check if lid_open control is good.
138            self.host.servo.lid_open()
139            if self.host.servo.get('lid_open') != 'yes':
140                raise error.TestError('BAD lid_open control. Reset servo!')
141
142        # Login to device
143        self.action_login()
144
145        pdu_connected = True
146        for i in xrange(loop_count):
147            logging.info('--- Iteration %d', (i + 1))
148
149            # Suspend/resume
150            self.do_suspend_resume()
151
152            # Discharging state
153            expected = ('no', 'Disconnected', 'Discharging')
154            self.switch_power_and_verify(False, expected)
155
156            # Suspend/resume
157            self.do_suspend_resume()
158
159            # Charging state - it could be any of the three below
160            expected = ('yes', 'AC', '(Charging|Fully charged|Discharging)')
161            self.switch_power_and_verify(True, expected)
162