• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2012 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, threading, time
6
7from autotest_lib.client.cros.crash_test import CrashTest
8from autotest_lib.server import autotest, test
9from autotest_lib.client.common_lib import error
10
11_WAIT_DELAY = 15
12_LONG_TIMEOUT = 200
13_SUSPEND_TIME = 30
14_WAKE_PRESS_IN_SEC = 0.2
15_CRASH_PATHS = [CrashTest._SYSTEM_CRASH_DIR.replace("/crash",""),
16                CrashTest._FALLBACK_USER_CRASH_DIR.replace("/crash",""),
17                CrashTest._USER_CRASH_DIRS.replace("/crash","")]
18
19class platform_ExternalUsbPeripherals(test.test):
20    """Uses servo to repeatedly connect/remove USB devices during boot."""
21    version = 1
22
23
24    def getPluggedUsbDevices(self):
25        """Determines the external USB devices plugged
26
27        @returns plugged_list: List of plugged usb devices names
28
29        """
30        lsusb_output = self.host.run('lsusb').stdout.strip()
31        items = lsusb_output.split('\n')
32        plugged_list = []
33        unnamed_device_count = 1
34        for item in items:
35            columns = item.split(' ')
36            if len(columns) == 6 or len(' '.join(columns[6:]).strip()) == 0:
37                logging.debug('Unnamed device located, adding generic name.')
38                name = 'Unnamed device %d' % unnamed_device_count
39                unnamed_device_count += 1
40            else:
41                name = ' '.join(columns[6:]).strip()
42            #Avoid servo components
43            if not name.startswith('Standard Microsystems Corp'):
44                plugged_list.append(name)
45        return plugged_list
46
47
48    def set_hub_power(self, on=True):
49        """Setting USB hub power status
50
51        @param on: To power on the servo-usb hub or not
52
53        """
54        reset = 'off'
55        if not on:
56            reset = 'on'
57        self.host.servo.set('dut_hub1_rst1', reset)
58        self.pluged_status = on
59
60
61    def action_login(self):
62        """Login i.e. runs running client test
63
64        @exception TestFail failed to login within timeout.
65
66        """
67        self.autotest_client.run_test(self.client_autotest,
68                                      exit_without_logout=True)
69
70
71    def wait_for_cmd_output(self, cmd, check, timeout, timeout_msg):
72        """Waits till command output is meta
73
74        @param cmd: executed command
75        @param check: string to be checked for in cmd output
76        @param timeout: max time in sec to wait for output
77        @param timeout_msg: timeout failure message
78
79        @returns True if check is found in command output; False otherwise
80        """
81        start_time = int(time.time())
82        time_delta = 0
83        command = '%s %s' % (cmd, check)
84        logging.debug('Command: %s', command)
85        while(self.host.run(command, ignore_status=True).exit_status != 0):
86            time_delta = int(time.time()) - start_time
87            if time_delta > timeout:
88                self.add_failure('%s - %d sec' % (timeout_msg, timeout))
89                return False
90            time.sleep(0.5)
91        logging.debug('Succeeded in :%d sec', time_delta)
92        return True
93
94
95    def suspend_for_time(self, suspend_time=_SUSPEND_TIME):
96        """Calls the host method suspend with suspend_time argument.
97
98        @param suspend_time: time to suspend the device for.
99
100        """
101        try:
102            self.host.suspend(suspend_time=suspend_time)
103        except error.AutoservSuspendError:
104            pass
105
106
107    def action_suspend(self):
108        """Suspend i.e. powerd_dbus_suspend and wait
109
110        @returns boot_id for the following resume
111        """
112        boot_id = self.host.get_boot_id()
113        thread = threading.Thread(target=self.suspend_for_time)
114        thread.start()
115        self.host.test_wait_for_sleep(_LONG_TIMEOUT)
116        logging.debug('--- Suspended')
117        self.suspend_status = True
118        return boot_id
119
120
121    def action_resume(self, boot_id):
122        """Resume i.e. press power key and wait
123
124        @param boot_id: boot id obtained prior to suspending
125
126        """
127        self.host.servo.power_key(_WAKE_PRESS_IN_SEC)
128        self.host.test_wait_for_resume(boot_id, _LONG_TIMEOUT)
129        logging.debug('--- Resumed')
130        self.suspend_status = False
131
132
133    def crash_not_detected(self, crash_path):
134        """Check for kernel, browser, process crashes
135
136        @param crash_path: Crash files path
137
138        @returns True if there were not crashes; False otherwise
139        """
140        result = True
141        if str(self.host.run('ls %s' % crash_path,
142                              ignore_status=True)).find('crash') != -1:
143            crash_out = self.host.run('ls %s/crash/' % crash_path).stdout
144            crash_files = crash_out.strip().split('\n')
145            for crash_file in crash_files:
146                if crash_file.find('.meta') != -1 and \
147                    crash_file.find('kernel_warning') == -1:
148                    self.add_failure('CRASH DETECTED in %s/crash: %s' %
149                                     (crash_path, crash_file))
150                    result = False
151        return result
152
153
154    def check_plugged_usb_devices(self):
155        """Checks the plugged peripherals match device list.
156
157        @returns True if expected USB peripherals are detected; False otherwise
158        """
159        result = True
160        if self.pluged_status and self.usb_list != None:
161            # Check for mandatory USb devices passed by usb_list flag
162            for usb_name in self.usb_list:
163                found = self.wait_for_cmd_output(
164                    'lsusb | grep -E ', usb_name, _WAIT_DELAY * 4,
165                    'Not detecting %s' % usb_name)
166                result = result and found
167        time.sleep(_WAIT_DELAY)
168        on_now = self.getPluggedUsbDevices()
169        if self.pluged_status:
170            if not self.diff_list.issubset(on_now):
171                missing = str(self.diff_list.difference(on_now))
172                self.add_failure('Missing connected peripheral(s) '
173                                 'when plugged: %s ' % missing)
174                result = False
175        else:
176            present = self.diff_list.intersection(on_now)
177            if len(present) > 0:
178                self.add_failure('Still presented peripheral(s) '
179                                 'when unplugged: %s ' % str(present))
180                result = False
181        return result
182
183
184    def check_usb_peripherals_details(self):
185        """Checks the effect from plugged in USB peripherals.
186
187        @returns True if command line output is matched successfuly; Else False
188        """
189        usb_check_result = True
190        for cmd in self.usb_checks.keys():
191            out_match_list = self.usb_checks.get(cmd)
192            if cmd.startswith('loggedin:'):
193                if not self.login_status:
194                    continue
195                cmd = cmd.replace('loggedin:','')
196            # Run the usb check command
197            for out_match in out_match_list:
198                match_result = self.wait_for_cmd_output(
199                    cmd, out_match, _WAIT_DELAY * 4,
200                    'USB CHECKS DETAILS failed at %s %s:' % (cmd, out_match))
201                usb_check_result = usb_check_result and match_result
202        return usb_check_result
203
204
205    def check_status(self):
206        """Performs checks after each action:
207            - for USB detected devices
208            - for generated crash files
209            - peripherals effect checks on cmd line
210
211        @returns True if all of the iteration checks pass; False otherwise.
212        """
213        result = True
214        if not self.suspend_status:
215            # Detect the USB peripherals
216            result = self.check_plugged_usb_devices()
217            # Check for crash files
218            if self.crash_check:
219                for crash_path in _CRASH_PATHS:
220                    result = result and self.crash_not_detected(crash_path)
221            if self.pluged_status and (self.usb_checks != None):
222                # Check for plugged USB devices details
223                result = result and self.check_usb_peripherals_details()
224        return result
225
226
227    def remove_crash_data(self):
228        """Delete crash meta files if present"""
229        for crash_path in _CRASH_PATHS:
230            if not self.crash_not_detected(crash_path):
231                self.host.run('rm -rf %s/crash' % crash_path,
232                              ignore_status=True)
233
234
235    def add_failure(self, reason):
236        """ Adds a failure reason to list of failures to be reported at end
237
238        @param reason: failure reason to record
239
240        """
241        if self.action_step is not None:
242            self.fail_reasons.append('%s FAILS - %s' %
243                                     (self.action_step, reason))
244
245
246
247    def cleanup(self):
248        """Disconnect servo hub"""
249        self.set_hub_power(False)
250        self.host.servo.set('usb_mux_sel3', 'servo_sees_usbkey')
251
252
253    def run_once(self, host, client_autotest, action_sequence, repeat,
254                 usb_list=None, usb_checks=None, crash_check=False):
255        self.client_autotest = client_autotest
256        self.host = host
257        self.autotest_client = autotest.Autotest(self.host)
258        self.usb_list = usb_list
259        self.usb_checks = usb_checks
260        self.crash_check = crash_check
261
262        self.suspend_status = False
263        self.login_status = False
264        self.fail_reasons = list()
265        self.action_step = None
266
267        self.host.servo.switch_usbkey('dut')
268        self.host.servo.set('usb_mux_sel3', 'dut_sees_usbkey')
269        time.sleep(_WAIT_DELAY)
270
271        # Collect USB peripherals when unplugged
272        self.set_hub_power(False)
273        time.sleep(_WAIT_DELAY)
274        off_list = self.getPluggedUsbDevices()
275
276        # Collect USB peripherals when plugged
277        self.set_hub_power(True)
278        time.sleep(_WAIT_DELAY * 2)
279        on_list = self.getPluggedUsbDevices()
280
281        self.diff_list = set(on_list).difference(set(off_list))
282        if len(self.diff_list) == 0:
283            # Fail if no devices detected after
284            raise error.TestError('No connected devices were detected. Make '
285                                  'sure the devices are connected to USB_KEY '
286                                  'and DUT_HUB1_USB on the servo board.')
287        logging.debug('Connected devices list: %s', self.diff_list)
288
289        board = host.get_board().split(':')[1]
290        action_sequence = action_sequence.upper()
291        actions = action_sequence.split(',')
292        boot_id = 0
293        self.remove_crash_data()
294
295        for iteration in xrange(1, repeat + 1):
296            step = 0
297            for action in actions:
298                step += 1
299                action = action.strip()
300                self.action_step = 'STEP %d.%d. %s' % (iteration, step, action)
301                logging.info(self.action_step)
302
303                if action == 'RESUME':
304                    self.action_resume(boot_id)
305                    time.sleep(_WAIT_DELAY)
306                elif action == 'UNPLUG':
307                    self.set_hub_power(False)
308                elif action == 'PLUG':
309                    self.set_hub_power(True)
310                elif self.suspend_status == False:
311                    if action.startswith('LOGIN'):
312                        if self.login_status:
313                            logging.debug('Skipping login. Already logged in.')
314                            continue
315                        else:
316                            self.action_login()
317                            self.login_status = True
318                    elif action == 'REBOOT':
319                        self.host.reboot()
320                        time.sleep(_WAIT_DELAY * 3)
321                        self.login_status = False
322                    elif action == 'SUSPEND':
323                        boot_id = self.action_suspend()
324                else:
325                    logging.info('WRONG ACTION: %s .', self.action_step)
326
327                self.check_status()
328
329            if self.fail_reasons:
330                raise error.TestFail('Failures reported: %s' %
331                                     str(self.fail_reasons))
332