Chromium Code Reviews| Index: build/android/pylib/device/device_utils.py |
| diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py |
| index 1440961aa5ec8f31b0a8dc88aab4a945a5dec3a5..c76c7965c37488614e74bb09b2c294b89e54ff2f 100644 |
| --- a/build/android/pylib/device/device_utils.py |
| +++ b/build/android/pylib/device/device_utils.py |
| @@ -9,51 +9,17 @@ Eventually, this will be based on adb_wrapper. |
| """ |
| # pylint: disable=W0613 |
| -import multiprocessing |
| -import os |
| -import sys |
| - |
| import pylib.android_commands |
| from pylib.device import adb_wrapper |
| from pylib.device import decorators |
| - |
| -CHROME_SRC_DIR = os.path.abspath( |
| - os.path.join(os.path.dirname(__file__), '..', '..', '..', '..')) |
| -sys.path.append(os.path.join( |
| - CHROME_SRC_DIR, 'third_party', 'android_testrunner')) |
| -import errors |
| +from pylib.device import device_errors |
| +from pylib.utils import reraiser_thread |
| +from pylib.utils import watchdog_timer |
| _DEFAULT_TIMEOUT = 30 |
| _DEFAULT_RETRIES = 3 |
| -# multiprocessing map_async requires a top-level function for pickle library. |
| -def RebootDeviceSafe(device): |
| - """Reboot a device, wait for it to start, and squelch timeout exceptions.""" |
| - try: |
| - DeviceUtils(device).old_interface.Reboot(True) |
| - except errors.DeviceUnresponsiveError as e: |
| - return e |
| - |
| - |
| -def RebootDevices(): |
| - """Reboot all attached and online devices.""" |
| - devices = pylib.android_commands.GetAttachedDevices() |
| - print 'Rebooting: %s' % devices |
| - if devices: |
| - pool = multiprocessing.Pool(len(devices)) |
| - results = pool.map_async(RebootDeviceSafe, devices).get(99999) |
| - |
| - for device, result in zip(devices, results): |
| - if result: |
| - print '%s failed to startup.' % device |
| - |
| - if any(results): |
| - print 'RebootDevices() Warning: %s' % results |
| - else: |
| - print 'Reboots complete.' |
| - |
| - |
| @decorators.WithExplicitTimeoutAndRetries( |
| _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) |
| def GetAVDs(): |
| @@ -76,8 +42,102 @@ def RestartServer(): |
| pylib.android_commands.AndroidCommands().RestartAdbServer() |
| +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.
|
| + """ 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
|
| + """ |
| + |
| + def __init__(self, device_list=None): |
| + if not device_list: |
| + self._devices = [ |
| + DeviceUtils(s) for s in |
| + pylib.android_commands.GetAttachedDevices()] |
| + else: |
| + device_list_type = self._typeOfContents(device_list) |
| + if issubclass(device_list_type, (basestring, |
| + pylib.android_commands.AndroidCommands, |
| + adb_wrapper.AdbWrapper)): |
| + self._devices = [DeviceUtils(d) for d in device_list] |
| + else: |
| + self._devices = device_list |
| + if not len(self._devices): |
| + # TODO(jbudorick) Should this be legal? |
| + 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
|
| + self._objs = self._devices |
| + self._emulated_type = self._typeOfContents(self._objs) |
| + |
| + @staticmethod |
| + def _typeOfContents(objs): |
| + assert(all([type(objs[0]) == type(o) for o in objs])) |
| + return type(objs[0]) if len(objs) else type(None) |
| + |
| + def __getattr__(self, name): |
| + """ Emulate getting the |name| attribute of |self|. |
| + |
| + Args: |
| + name: The name of the attribute to retrieve. |
| + """ |
| + r = _ParallelExecutor(self._devices) |
| + r._objs = [getattr(o, name) for o in self._objs] |
| + |
| + # pylint: disable=W0212 |
| + emulated_type = self._typeOfContents(r._objs) |
| + 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.
|
| + r._emulated_type = self._emulated_type |
| + else: |
| + r._emulated_type = emulated_type |
| + # pylint: enable=W0212 |
| + return r |
| + |
| + def __getitem__(self, index): |
| + """ Emulate getting the value of |self| at |index|. |
| + """ |
| + r = _ParallelExecutor(self._devices) |
| + r._objs = [o[index] for o in self._objs] |
| + return r |
| + |
| + def __call__(self, *args, **kwargs): |
| + """ Emulate calling |self| with |args| and |kwargs|. |
| + """ |
| + if not self._objs: |
| + raise AttributeError('Nothing to call.') |
| + for o in self._objs: |
| + if not callable(o): |
| + raise AttributeError("'%s' is not callable" % o.__name__) |
| + |
| + r = _ParallelExecutor(self._devices) |
| + r._objs = reraiser_thread.ReraiserThreadGroup( |
| + [reraiser_thread.ReraiserThread( |
| + o, args=args, kwargs=kwargs, |
| + name='%s.%s' % (str(d), o.__name__)) |
| + for d, o in zip(self._devices, self._objs)]) |
| + r._objs.StartAll() # pylint: disable=W0212 |
| + return r |
| + |
| + def get(self, timeout): |
| + """ Get the return values from the most recent call. |
| + |
| + Args: |
| + timeout: The maximum number of seconds to wait for an individual |
| + result to return. |
| + Returns: |
| + A list of the results, in order of the provided devices. |
| + Raises: |
| + Any exception raised by any of the called functions, or |
| + CommandTimeoutError on timeout. |
| + """ |
| + try: |
| + if isinstance(self._objs, reraiser_thread.ReraiserThreadGroup): |
| + self._objs.JoinAll() |
| + self._objs = self._objs.Get(watchdog_timer.WatchdogTimer(timeout)) |
| + return self._objs |
| + except reraiser_thread.TimeoutError as e: |
| + raise device_errors.CommandTimeoutError(str(e)) |
| + |
| + |
| class DeviceUtils(object): |
| + parallel = _ParallelExecutor |
| + |
| def __init__(self, device): |
| self.old_interface = None |
| if isinstance(device, basestring): |
| @@ -89,3 +149,6 @@ class DeviceUtils(object): |
| elif not device: |
| self.old_interface = pylib.android_commands.AndroidCommands() |
| + def __str__(self): |
| + return self.old_interface.GetDevice() |
| + |