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 |