• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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