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