Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """ | 5 """ |
| 6 Provides a variety of device interactions based on adb. | 6 Provides a variety of device interactions based on adb. |
| 7 | 7 |
| 8 Eventually, this will be based on adb_wrapper. | 8 Eventually, this will be based on adb_wrapper. |
| 9 """ | 9 """ |
| 10 # pylint: disable=W0613 | 10 # pylint: disable=W0613 |
| 11 | 11 |
| 12 import multiprocessing | |
| 13 import os | |
| 14 import sys | |
| 15 | |
| 16 import pylib.android_commands | 12 import pylib.android_commands |
| 17 from pylib.device import adb_wrapper | 13 from pylib.device import adb_wrapper |
| 18 from pylib.device import decorators | 14 from pylib.device import decorators |
| 19 | 15 from pylib.device import device_errors |
| 20 CHROME_SRC_DIR = os.path.abspath( | 16 from pylib.utils import reraiser_thread |
| 21 os.path.join(os.path.dirname(__file__), '..', '..', '..', '..')) | 17 from pylib.utils import watchdog_timer |
| 22 sys.path.append(os.path.join( | |
| 23 CHROME_SRC_DIR, 'third_party', 'android_testrunner')) | |
| 24 import errors | |
| 25 | 18 |
| 26 _DEFAULT_TIMEOUT = 30 | 19 _DEFAULT_TIMEOUT = 30 |
| 27 _DEFAULT_RETRIES = 3 | 20 _DEFAULT_RETRIES = 3 |
| 28 | 21 |
| 29 | 22 |
| 30 # multiprocessing map_async requires a top-level function for pickle library. | |
| 31 def RebootDeviceSafe(device): | |
| 32 """Reboot a device, wait for it to start, and squelch timeout exceptions.""" | |
| 33 try: | |
| 34 DeviceUtils(device).old_interface.Reboot(True) | |
| 35 except errors.DeviceUnresponsiveError as e: | |
| 36 return e | |
| 37 | |
| 38 | |
| 39 def RebootDevices(): | |
| 40 """Reboot all attached and online devices.""" | |
| 41 devices = pylib.android_commands.GetAttachedDevices() | |
| 42 print 'Rebooting: %s' % devices | |
| 43 if devices: | |
| 44 pool = multiprocessing.Pool(len(devices)) | |
| 45 results = pool.map_async(RebootDeviceSafe, devices).get(99999) | |
| 46 | |
| 47 for device, result in zip(devices, results): | |
| 48 if result: | |
| 49 print '%s failed to startup.' % device | |
| 50 | |
| 51 if any(results): | |
| 52 print 'RebootDevices() Warning: %s' % results | |
| 53 else: | |
| 54 print 'Reboots complete.' | |
| 55 | |
| 56 | |
| 57 @decorators.WithExplicitTimeoutAndRetries( | 23 @decorators.WithExplicitTimeoutAndRetries( |
| 58 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) | 24 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) |
| 59 def GetAVDs(): | 25 def GetAVDs(): |
| 60 """ Returns a list of Android Virtual Devices. | 26 """ Returns a list of Android Virtual Devices. |
| 61 | 27 |
| 62 Returns: | 28 Returns: |
| 63 A list containing the configured AVDs. | 29 A list containing the configured AVDs. |
| 64 """ | 30 """ |
| 65 return pylib.android_commands.GetAVDs() | 31 return pylib.android_commands.GetAVDs() |
| 66 | 32 |
| 67 | 33 |
| 68 @decorators.WithExplicitTimeoutAndRetries( | 34 @decorators.WithExplicitTimeoutAndRetries( |
| 69 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) | 35 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) |
| 70 def RestartServer(): | 36 def RestartServer(): |
| 71 """ Restarts the adb server. | 37 """ Restarts the adb server. |
| 72 | 38 |
| 73 Raises: | 39 Raises: |
| 74 CommandFailedError if we fail to kill or restart the server. | 40 CommandFailedError if we fail to kill or restart the server. |
| 75 """ | 41 """ |
| 76 pylib.android_commands.AndroidCommands().RestartAdbServer() | 42 pylib.android_commands.AndroidCommands().RestartAdbServer() |
| 77 | 43 |
| 78 | 44 |
| 45 class _ParallelExecutor(object): | |
|
frankf
2014/05/16 17:47:26
this should be in its own module with tests
frankf
2014/05/16 17:47:26
Also think about how to organize various modes of
jbudorick
2014/05/16 17:52:50
I debated about the separate module thing. Opted n
craigdh
2014/05/16 21:59:35
This is probably worth sticking in utils/, probabl
jbudorick
2014/05/21 16:42:48
Done.
| |
| 46 """ Allows parallel execution across a provided set of devices. | |
|
frankf
2014/05/16 17:47:26
"""First line of docstinrg.
Additional comments
"
jbudorick
2014/05/16 17:52:50
In general -- what if there are no additional comm
frankf
2014/05/16 18:41:27
The former for single line docstring.
On 2014/05
| |
| 47 """ | |
| 48 | |
| 49 def __init__(self, device_list=None): | |
| 50 if not device_list: | |
| 51 self._devices = [ | |
| 52 DeviceUtils(s) for s in | |
| 53 pylib.android_commands.GetAttachedDevices()] | |
| 54 else: | |
| 55 device_list_type = self._typeOfContents(device_list) | |
| 56 if issubclass(device_list_type, (basestring, | |
| 57 pylib.android_commands.AndroidCommands, | |
| 58 adb_wrapper.AdbWrapper)): | |
| 59 self._devices = [DeviceUtils(d) for d in device_list] | |
| 60 else: | |
| 61 self._devices = device_list | |
| 62 if not len(self._devices): | |
| 63 # TODO(jbudorick) Should this be legal? | |
| 64 raise ValueError('No devices connected.') | |
|
craigdh
2014/05/16 21:59:35
I don't think ValueError is the right exception to
jbudorick
2014/05/21 16:42:48
What would you suggest? IndexError? A separate exc
craigdh
2014/05/21 16:50:13
Maybe RuntimeError. Or just do an assert. ValueErr
| |
| 65 self._objs = self._devices | |
| 66 self._emulated_type = self._typeOfContents(self._objs) | |
| 67 | |
| 68 @staticmethod | |
| 69 def _typeOfContents(objs): | |
| 70 assert(all([type(objs[0]) == type(o) for o in objs])) | |
| 71 return type(objs[0]) if len(objs) else type(None) | |
| 72 | |
| 73 def __getattr__(self, name): | |
| 74 """ Emulate getting the |name| attribute of |self|. | |
| 75 | |
| 76 Args: | |
| 77 name: The name of the attribute to retrieve. | |
| 78 """ | |
| 79 r = _ParallelExecutor(self._devices) | |
| 80 r._objs = [getattr(o, name) for o in self._objs] | |
| 81 | |
| 82 # pylint: disable=W0212 | |
| 83 emulated_type = self._typeOfContents(r._objs) | |
| 84 if all([callable(o) for o in r._objs]): | |
|
craigdh
2014/05/16 21:59:35
prefer generator expressions over list comprehensi
jbudorick
2014/05/21 16:42:48
Done.
| |
| 85 r._emulated_type = self._emulated_type | |
| 86 else: | |
| 87 r._emulated_type = emulated_type | |
| 88 # pylint: enable=W0212 | |
| 89 return r | |
| 90 | |
| 91 def __getitem__(self, index): | |
| 92 """ Emulate getting the value of |self| at |index|. | |
| 93 """ | |
| 94 r = _ParallelExecutor(self._devices) | |
| 95 r._objs = [o[index] for o in self._objs] | |
| 96 return r | |
| 97 | |
| 98 def __call__(self, *args, **kwargs): | |
| 99 """ Emulate calling |self| with |args| and |kwargs|. | |
| 100 """ | |
| 101 if not self._objs: | |
| 102 raise AttributeError('Nothing to call.') | |
| 103 for o in self._objs: | |
| 104 if not callable(o): | |
| 105 raise AttributeError("'%s' is not callable" % o.__name__) | |
| 106 | |
| 107 r = _ParallelExecutor(self._devices) | |
| 108 r._objs = reraiser_thread.ReraiserThreadGroup( | |
| 109 [reraiser_thread.ReraiserThread( | |
| 110 o, args=args, kwargs=kwargs, | |
| 111 name='%s.%s' % (str(d), o.__name__)) | |
| 112 for d, o in zip(self._devices, self._objs)]) | |
| 113 r._objs.StartAll() # pylint: disable=W0212 | |
| 114 return r | |
| 115 | |
| 116 def get(self, timeout): | |
| 117 """ Get the return values from the most recent call. | |
| 118 | |
| 119 Args: | |
| 120 timeout: The maximum number of seconds to wait for an individual | |
| 121 result to return. | |
| 122 Returns: | |
| 123 A list of the results, in order of the provided devices. | |
| 124 Raises: | |
| 125 Any exception raised by any of the called functions, or | |
| 126 CommandTimeoutError on timeout. | |
| 127 """ | |
| 128 try: | |
| 129 if isinstance(self._objs, reraiser_thread.ReraiserThreadGroup): | |
| 130 self._objs.JoinAll() | |
| 131 self._objs = self._objs.Get(watchdog_timer.WatchdogTimer(timeout)) | |
| 132 return self._objs | |
| 133 except reraiser_thread.TimeoutError as e: | |
| 134 raise device_errors.CommandTimeoutError(str(e)) | |
| 135 | |
| 136 | |
| 79 class DeviceUtils(object): | 137 class DeviceUtils(object): |
| 80 | 138 |
| 139 parallel = _ParallelExecutor | |
| 140 | |
| 81 def __init__(self, device): | 141 def __init__(self, device): |
| 82 self.old_interface = None | 142 self.old_interface = None |
| 83 if isinstance(device, basestring): | 143 if isinstance(device, basestring): |
| 84 self.old_interface = pylib.android_commands.AndroidCommands(device) | 144 self.old_interface = pylib.android_commands.AndroidCommands(device) |
| 85 elif isinstance(device, adb_wrapper.AdbWrapper): | 145 elif isinstance(device, adb_wrapper.AdbWrapper): |
| 86 self.old_interface = pylib.android_commands.AndroidCommands(str(device)) | 146 self.old_interface = pylib.android_commands.AndroidCommands(str(device)) |
| 87 elif isinstance(device, pylib.android_commands.AndroidCommands): | 147 elif isinstance(device, pylib.android_commands.AndroidCommands): |
| 88 self.old_interface = device | 148 self.old_interface = device |
| 89 elif not device: | 149 elif not device: |
| 90 self.old_interface = pylib.android_commands.AndroidCommands() | 150 self.old_interface = pylib.android_commands.AndroidCommands() |
| 91 | 151 |
| 152 def __str__(self): | |
| 153 return self.old_interface.GetDevice() | |
| 154 | |
| OLD | NEW |