Home | History | Annotate | Download | only in platform_ExternalUsbPeripherals
      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 
      5 import logging, threading, time
      6 
      7 from autotest_lib.client.cros.crash_test import CrashTest
      8 from autotest_lib.server import autotest, test
      9 from 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 
     19 class 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