1# Copyright 2014 The Chromium 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""" 6Exception classes raised by AdbWrapper and DeviceUtils. 7 8The class hierarchy for device exceptions is: 9 10 base_error.BaseError 11 +-- CommandFailedError 12 | +-- AdbCommandFailedError 13 | | +-- AdbShellCommandFailedError 14 | +-- FastbootCommandFailedError 15 | +-- DeviceVersionError 16 | +-- DeviceChargingError 17 +-- CommandTimeoutError 18 +-- DeviceUnreachableError 19 +-- NoDevicesError 20 +-- MultipleDevicesError 21 +-- NoAdbError 22 23""" 24 25from devil import base_error 26from devil.utils import cmd_helper 27from devil.utils import parallelizer 28 29 30class CommandFailedError(base_error.BaseError): 31 """Exception for command failures.""" 32 33 def __init__(self, message, device_serial=None): 34 device_leader = '(device: %s)' % device_serial 35 if device_serial is not None and not message.startswith(device_leader): 36 message = '%s %s' % (device_leader, message) 37 self.device_serial = device_serial 38 super(CommandFailedError, self).__init__(message) 39 40 def __eq__(self, other): 41 return (super(CommandFailedError, self).__eq__(other) 42 and self.device_serial == other.device_serial) 43 44 def __ne__(self, other): 45 return not self == other 46 47 48class _BaseCommandFailedError(CommandFailedError): 49 """Base Exception for adb and fastboot command failures.""" 50 51 def __init__(self, args, output, status=None, device_serial=None, 52 message=None): 53 self.args = args 54 self.output = output 55 self.status = status 56 if not message: 57 adb_cmd = ' '.join(cmd_helper.SingleQuote(arg) for arg in self.args) 58 segments = ['adb %s: failed ' % adb_cmd] 59 if status: 60 segments.append('with exit status %s ' % self.status) 61 if output: 62 segments.append('and output:\n') 63 segments.extend('- %s\n' % line for line in output.splitlines()) 64 else: 65 segments.append('and no output.') 66 message = ''.join(segments) 67 super(_BaseCommandFailedError, self).__init__(message, device_serial) 68 69 def __eq__(self, other): 70 return (super(_BaseCommandFailedError, self).__eq__(other) 71 and self.args == other.args 72 and self.output == other.output 73 and self.status == other.status) 74 75 def __ne__(self, other): 76 return not self == other 77 78 def __reduce__(self): 79 """Support pickling.""" 80 result = [None, None, None, None, None] 81 super_result = super(_BaseCommandFailedError, self).__reduce__() 82 result[:len(super_result)] = super_result 83 84 # Update the args used to reconstruct this exception. 85 result[1] = ( 86 self.args, self.output, self.status, self.device_serial, self.message) 87 return tuple(result) 88 89 90class AdbCommandFailedError(_BaseCommandFailedError): 91 """Exception for adb command failures.""" 92 93 def __init__(self, args, output, status=None, device_serial=None, 94 message=None): 95 super(AdbCommandFailedError, self).__init__( 96 args, output, status=status, message=message, 97 device_serial=device_serial) 98 99 100class FastbootCommandFailedError(_BaseCommandFailedError): 101 """Exception for fastboot command failures.""" 102 103 def __init__(self, args, output, status=None, device_serial=None, 104 message=None): 105 super(FastbootCommandFailedError, self).__init__( 106 args, output, status=status, message=message, 107 device_serial=device_serial) 108 109 110class DeviceVersionError(CommandFailedError): 111 """Exception for device version failures.""" 112 113 def __init__(self, message, device_serial=None): 114 super(DeviceVersionError, self).__init__(message, device_serial) 115 116 117class AdbShellCommandFailedError(AdbCommandFailedError): 118 """Exception for shell command failures run via adb.""" 119 120 def __init__(self, command, output, status, device_serial=None): 121 self.command = command 122 segments = ['shell command run via adb failed on the device:\n', 123 ' command: %s\n' % command] 124 segments.append(' exit status: %s\n' % status) 125 if output: 126 segments.append(' output:\n') 127 if isinstance(output, basestring): 128 output_lines = output.splitlines() 129 else: 130 output_lines = output 131 segments.extend(' - %s\n' % line for line in output_lines) 132 else: 133 segments.append(" output: ''\n") 134 message = ''.join(segments) 135 super(AdbShellCommandFailedError, self).__init__( 136 ['shell', command], output, status, device_serial, message) 137 138 def __reduce__(self): 139 """Support pickling.""" 140 result = [None, None, None, None, None] 141 super_result = super(AdbShellCommandFailedError, self).__reduce__() 142 result[:len(super_result)] = super_result 143 144 # Update the args used to reconstruct this exception. 145 result[1] = (self.command, self.output, self.status, self.device_serial) 146 return tuple(result) 147 148 149class CommandTimeoutError(base_error.BaseError): 150 """Exception for command timeouts.""" 151 def __init__(self, message, is_infra_error=False, output=None): 152 super(CommandTimeoutError, self).__init__(message, is_infra_error) 153 self.output = output 154 155 156class DeviceUnreachableError(base_error.BaseError): 157 """Exception for device unreachable failures.""" 158 pass 159 160 161class NoDevicesError(base_error.BaseError): 162 """Exception for having no devices attached.""" 163 164 def __init__(self, msg=None): 165 super(NoDevicesError, self).__init__( 166 msg or 'No devices attached.', is_infra_error=True) 167 168 169class MultipleDevicesError(base_error.BaseError): 170 """Exception for having multiple attached devices without selecting one.""" 171 172 def __init__(self, devices): 173 parallel_devices = parallelizer.Parallelizer(devices) 174 descriptions = parallel_devices.pMap( 175 lambda d: d.build_description).pGet(None) 176 msg = ('More than one device available. Use -d/--device to select a device ' 177 'by serial.\n\nAvailable devices:\n') 178 for d, desc in zip(devices, descriptions): 179 msg += ' %s (%s)\n' % (d, desc) 180 181 super(MultipleDevicesError, self).__init__(msg, is_infra_error=True) 182 183 184class NoAdbError(base_error.BaseError): 185 """Exception for being unable to find ADB.""" 186 187 def __init__(self, msg=None): 188 super(NoAdbError, self).__init__( 189 msg or 'Unable to find adb.', is_infra_error=True) 190 191 192class DeviceChargingError(CommandFailedError): 193 """Exception for device charging errors.""" 194 195 def __init__(self, message, device_serial=None): 196 super(DeviceChargingError, self).__init__(message, device_serial) 197