Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(849)

Side by Side Diff: build/android/pylib/remote/device/remote_device_environment.py

Issue 1415533007: [Android] Add sharding for AMP instrumentation tests. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 """Environment setup and teardown for remote devices.""" 5 """Environment setup and teardown for remote devices."""
6 6
7 import distutils.version 7 import distutils.version
8 import json 8 import json
9 import logging 9 import logging
10 import os 10 import os
11 import random
12 import sys 11 import sys
13 12
14 from devil.utils import reraiser_thread 13 from devil.utils import reraiser_thread
15 from devil.utils import timeout_retry 14 from devil.utils import timeout_retry
16 from pylib.base import environment 15 from pylib.base import environment
16 from pylib.remote.device import appurify_constants
17 from pylib.remote.device import appurify_sanitized 17 from pylib.remote.device import appurify_sanitized
18 from pylib.remote.device import remote_device_helper 18 from pylib.remote.device import remote_device_helper
19 19
20 class RemoteDeviceEnvironment(environment.Environment): 20 class RemoteDeviceEnvironment(environment.Environment):
21 """An environment for running on remote devices.""" 21 """An environment for running on remote devices."""
22 22
23 _ENV_KEY = 'env'
24 _DEVICE_KEY = 'device'
25 _DEFAULT_RETRIES = 0 23 _DEFAULT_RETRIES = 0
24 _DEFAULT_SHARD_COUNT = 6
jbudorick 2015/10/26 22:23:30 // chosen by random die roll
26 25
27 def __init__(self, args, error_func): 26 def __init__(self, args, error_func):
28 """Constructor. 27 """Constructor.
29 28
30 Args: 29 Args:
31 args: Command line arguments. 30 args: Command line arguments.
32 error_func: error to show when using bad command line arguments. 31 error_func: error to show when using bad command line arguments.
33 """ 32 """
34 super(RemoteDeviceEnvironment, self).__init__() 33 super(RemoteDeviceEnvironment, self).__init__()
35 self._access_token = None 34 self._access_token = None
36 self._device = None 35 self._devices = []
37 self._device_type = args.device_type 36 self._device_type = args.device_type
38 self._verbose_count = args.verbose_count 37 self._verbose_count = args.verbose_count
39 self._timeouts = { 38 self._timeouts = {
40 'queueing': 60 * 10, 39 'queueing': 60 * 10,
41 'installing': 60 * 10, 40 'installing': 60 * 10,
42 'in-progress': 60 * 30, 41 'in-progress': 60 * 30,
43 'unknown': 60 * 5 42 'unknown': 60 * 5
44 } 43 }
45 # Example config file: 44 # Example config file:
46 # { 45 # {
(...skipping 19 matching lines...) Expand all
66 device_json = {} 65 device_json = {}
67 66
68 self._api_address = device_json.get('api_address', None) 67 self._api_address = device_json.get('api_address', None)
69 self._api_key = device_json.get('api_key', None) 68 self._api_key = device_json.get('api_key', None)
70 self._api_port = device_json.get('api_port', None) 69 self._api_port = device_json.get('api_port', None)
71 self._api_protocol = device_json.get('api_protocol', None) 70 self._api_protocol = device_json.get('api_protocol', None)
72 self._api_secret = device_json.get('api_secret', None) 71 self._api_secret = device_json.get('api_secret', None)
73 self._device_oem = device_json.get('device_oem', None) 72 self._device_oem = device_json.get('device_oem', None)
74 self._device_type = device_json.get('device_type', 'Android') 73 self._device_type = device_json.get('device_type', 'Android')
75 self._network_config = device_json.get('network_config', None) 74 self._network_config = device_json.get('network_config', None)
75 self._num_shards = device_json.get('num_shards', None)
76 self._pcap_config = device_json.get('pcap_config', 0)
77 self._profiler_config = device_json.get('profiler_config', 0)
76 self._remote_device = device_json.get('remote_device', None) 78 self._remote_device = device_json.get('remote_device', None)
77 self._remote_device_minimum_os = device_json.get( 79 self._remote_device_minimum_os = device_json.get(
78 'remote_device_minimum_os', None) 80 'remote_device_minimum_os', None)
79 self._remote_device_os = device_json.get('remote_device_os', None) 81 self._remote_device_os = device_json.get('remote_device_os', None)
80 self._remote_device_timeout = device_json.get( 82 self._remote_device_timeout = device_json.get(
81 'remote_device_timeout', None) 83 'remote_device_timeout', None)
82 self._results_path = device_json.get('results_path', None)
83 self._runner_package = device_json.get('runner_package', None) 84 self._runner_package = device_json.get('runner_package', None)
84 self._runner_type = device_json.get('runner_type', None) 85 self._test_framework = device_json.get('test_framework', None)
85 self._timeouts.update(device_json.get('timeouts', {})) 86 self._timeouts.update(device_json.get('timeouts', {}))
87 self._videocapture_config = device_json.get('videocapture_config', 0)
86 88
87 def command_line_override( 89 def command_line_override(
88 file_value, cmd_line_value, desc, print_value=True): 90 file_value, cmd_line_value, desc, print_value=True):
89 if cmd_line_value: 91 if cmd_line_value:
90 if file_value and file_value != cmd_line_value: 92 if file_value and file_value != cmd_line_value:
91 if print_value: 93 if print_value:
92 logging.info('Overriding %s from %s to %s', 94 logging.info('Overriding %s from %s to %s',
93 desc, file_value, cmd_line_value) 95 desc, file_value, cmd_line_value)
94 else: 96 else:
95 logging.info('overriding %s', desc) 97 logging.info('overriding %s', desc)
96 return cmd_line_value 98 return cmd_line_value
97 return file_value 99 return file_value
98 100
99 self._api_address = command_line_override( 101 self._api_address = command_line_override(
100 self._api_address, args.api_address, 'api_address') 102 self._api_address, args.api_address, 'api_address')
101 self._api_port = command_line_override( 103 self._api_port = command_line_override(
102 self._api_port, args.api_port, 'api_port') 104 self._api_port, args.api_port, 'api_port')
103 self._api_protocol = command_line_override( 105 self._api_protocol = command_line_override(
104 self._api_protocol, args.api_protocol, 'api_protocol') 106 self._api_protocol, args.api_protocol, 'api_protocol')
105 self._device_oem = command_line_override( 107 self._device_oem = command_line_override(
106 self._device_oem, args.device_oem, 'device_oem') 108 self._device_oem, args.device_oem, 'device_oem')
107 self._device_type = command_line_override( 109 self._device_type = command_line_override(
108 self._device_type, args.device_type, 'device_type') 110 self._device_type, args.device_type, 'device_type')
109 self._network_config = command_line_override( 111 self._network_config = command_line_override(
110 self._network_config, args.network_config, 'network_config') 112 self._network_config, args.network_config, 'network_config')
113 self._num_shards = command_line_override(
114 self._num_shards, args.num_shards, 'num_shards')
111 self._remote_device = command_line_override( 115 self._remote_device = command_line_override(
112 self._remote_device, args.remote_device, 'remote_device') 116 self._remote_device, args.remote_device, 'remote_device')
113 self._remote_device_minimum_os = command_line_override( 117 self._remote_device_minimum_os = command_line_override(
114 self._remote_device_minimum_os, args.remote_device_minimum_os, 118 self._remote_device_minimum_os, args.remote_device_minimum_os,
115 'remote_device_minimum_os') 119 'remote_device_minimum_os')
116 self._remote_device_os = command_line_override( 120 self._remote_device_os = command_line_override(
117 self._remote_device_os, args.remote_device_os, 'remote_device_os') 121 self._remote_device_os, args.remote_device_os, 'remote_device_os')
118 self._remote_device_timeout = command_line_override( 122 self._remote_device_timeout = command_line_override(
119 self._remote_device_timeout, args.remote_device_timeout, 123 self._remote_device_timeout, args.remote_device_timeout,
120 'remote_device_timeout') 124 'remote_device_timeout')
121 self._results_path = command_line_override(
122 self._results_path, args.results_path, 'results_path')
123 self._runner_package = command_line_override( 125 self._runner_package = command_line_override(
124 self._runner_package, args.runner_package, 'runner_package') 126 self._runner_package, args.runner_package, 'runner_package')
125 self._runner_type = command_line_override( 127 self._test_framework = command_line_override(
126 self._runner_type, args.runner_type, 'runner_type') 128 self._test_framework, args.test_framework, 'test_framework')
127 129
128 if args.api_key_file: 130 if args.api_key_file:
129 with open(args.api_key_file) as api_key_file: 131 with open(args.api_key_file) as api_key_file:
130 temp_key = api_key_file.read().strip() 132 temp_key = api_key_file.read().strip()
131 self._api_key = command_line_override( 133 self._api_key = command_line_override(
132 self._api_key, temp_key, 'api_key', print_value=False) 134 self._api_key, temp_key, 'api_key', print_value=False)
133 self._api_key = command_line_override( 135 self._api_key = command_line_override(
134 self._api_key, args.api_key, 'api_key', print_value=False) 136 self._api_key, args.api_key, 'api_key', print_value=False)
135 137
136 if args.api_secret_file: 138 if args.api_secret_file:
(...skipping 23 matching lines...) Expand all
160 logging.info('Api address: %s', self._api_address) 162 logging.info('Api address: %s', self._api_address)
161 logging.info('Api port: %s', self._api_port) 163 logging.info('Api port: %s', self._api_port)
162 logging.info('Api protocol: %s', self._api_protocol) 164 logging.info('Api protocol: %s', self._api_protocol)
163 logging.info('Remote device: %s', self._remote_device) 165 logging.info('Remote device: %s', self._remote_device)
164 logging.info('Remote device minimum OS: %s', 166 logging.info('Remote device minimum OS: %s',
165 self._remote_device_minimum_os) 167 self._remote_device_minimum_os)
166 logging.info('Remote device OS: %s', self._remote_device_os) 168 logging.info('Remote device OS: %s', self._remote_device_os)
167 logging.info('Remote device OEM: %s', self._device_oem) 169 logging.info('Remote device OEM: %s', self._device_oem)
168 logging.info('Remote device type: %s', self._device_type) 170 logging.info('Remote device type: %s', self._device_type)
169 logging.info('Remote device timout: %s', self._remote_device_timeout) 171 logging.info('Remote device timout: %s', self._remote_device_timeout)
170 logging.info('Results Path: %s', self._results_path)
171 logging.info('Runner package: %s', self._runner_package) 172 logging.info('Runner package: %s', self._runner_package)
172 logging.info('Runner type: %s', self._runner_type) 173 logging.info('Test framework: %s', self._test_framework)
173 logging.info('Timeouts: %s', self._timeouts) 174 logging.info('Timeouts: %s', self._timeouts)
174 175
175 if not args.trigger and not args.collect: 176 if not args.trigger and not args.collect:
176 self._trigger = True 177 self._trigger = True
177 self._collect = True 178 self._collect = True
178 else: 179 else:
179 self._trigger = args.trigger 180 self._trigger = args.trigger
180 self._collect = args.collect 181 self._collect = args.collect
181 182
182 def SetUp(self): 183 def SetUp(self):
183 """Set up the test environment.""" 184 """Set up the test environment."""
184 os.environ['APPURIFY_API_PROTO'] = self._api_protocol 185 os.environ['APPURIFY_API_PROTO'] = self._api_protocol
185 os.environ['APPURIFY_API_HOST'] = self._api_address 186 os.environ['APPURIFY_API_HOST'] = self._api_address
186 os.environ['APPURIFY_API_PORT'] = self._api_port 187 os.environ['APPURIFY_API_PORT'] = self._api_port
187 os.environ['APPURIFY_STATUS_BASE_URL'] = 'none' 188 os.environ['APPURIFY_STATUS_BASE_URL'] = 'none'
188 self._GetAccessToken() 189 self._GetAccessToken()
189 if self._trigger: 190 if self._trigger:
190 self._SelectDevice() 191 self._SelectDevices()
191 192
192 def TearDown(self): 193 def TearDown(self):
193 """Teardown the test environment.""" 194 """Teardown the test environment."""
194 self._RevokeAccessToken() 195 self._RevokeAccessToken()
195 196
196 def __enter__(self): 197 def __enter__(self):
197 """Set up the test run when used as a context manager.""" 198 """Set up the test run when used as a context manager."""
198 try: 199 try:
199 self.SetUp() 200 self.SetUp()
200 return self 201 return self
201 except: 202 except:
202 self.__exit__(*sys.exc_info()) 203 self.__exit__(*sys.exc_info())
203 raise 204 raise
204 205
205 def __exit__(self, exc_type, exc_val, exc_tb): 206 def __exit__(self, exc_type, exc_val, exc_tb):
206 """Tears down the test run when used as a context manager.""" 207 """Tears down the test run when used as a context manager."""
207 self.TearDown() 208 self.TearDown()
208 209
209 def DumpTo(self, persisted_data):
210 env_data = {
211 self._DEVICE_KEY: self._device,
212 }
213 persisted_data[self._ENV_KEY] = env_data
214
215 def LoadFrom(self, persisted_data):
216 env_data = persisted_data[self._ENV_KEY]
217 self._device = env_data[self._DEVICE_KEY]
218
219 def _GetAccessToken(self): 210 def _GetAccessToken(self):
220 """Generates access token for remote device service.""" 211 """Generates access token for remote device service."""
221 logging.info('Generating remote service access token') 212 logging.info('Generating remote service access token')
222 with appurify_sanitized.SanitizeLogging(self._verbose_count, 213 with appurify_sanitized.SanitizeLogging(
223 logging.WARNING): 214 verbose_count=self._verbose_count,
224 access_token_results = appurify_sanitized.api.access_token_generate( 215 level=logging.WARNING):
225 self._api_key, self._api_secret) 216 access_token_result = appurify_sanitized.api.access_token_generate(
226 remote_device_helper.TestHttpResponse(access_token_results, 217 api_key=self._api_key,
227 'Unable to generate access token.') 218 api_secret=self._api_secret)
228 self._access_token = access_token_results.json()['response']['access_token'] 219 remote_device_helper.TestHttpResponse(
220 response=access_token_result,
221 error_msg='Unable to generate access token.')
222 self._access_token = access_token_result.json()['response']['access_token']
229 223
230 def _RevokeAccessToken(self): 224 def _RevokeAccessToken(self):
231 """Destroys access token for remote device service.""" 225 """Destroys access token for remote device service."""
232 logging.info('Revoking remote service access token') 226 logging.info('Revoking remote service access token')
233 with appurify_sanitized.SanitizeLogging(self._verbose_count, 227 with appurify_sanitized.SanitizeLogging(
234 logging.WARNING): 228 verbose_count=self._verbose_count,
229 level=logging.WARNING):
235 revoke_token_results = appurify_sanitized.api.access_token_revoke( 230 revoke_token_results = appurify_sanitized.api.access_token_revoke(
236 self._access_token) 231 access_token=self._access_token)
237 remote_device_helper.TestHttpResponse(revoke_token_results, 232 remote_device_helper.TestHttpResponse(
238 'Unable to revoke access token.') 233 response=revoke_token_results,
234 error_msg='Unable to revoke access token.')
239 235
240 def _SelectDevice(self): 236 def _SelectDevices(self):
241 if self._remote_device_timeout: 237 if self._remote_device_timeout:
242 try: 238 try:
243 timeout_retry.Run(self._FindDeviceWithTimeout, 239 timeout_retry.Run(self._FindDevicesWithTimeout,
244 self._remote_device_timeout, self._DEFAULT_RETRIES) 240 self._remote_device_timeout, self._DEFAULT_RETRIES)
245 except reraiser_thread.TimeoutError: 241 except reraiser_thread.TimeoutError:
246 self._NoDeviceFound() 242 self._NoDeviceFound()
247 else: 243 else:
248 if not self._FindDevice(): 244 if not self._FindDevices():
249 self._NoDeviceFound() 245 self._NoDeviceFound()
250 246
251 def _FindDevice(self): 247 def _FindDevices(self):
252 """Find which device to use.""" 248 """Find which devices to use.
253 logging.info('Finding device to run tests on.') 249
250 Returns:
251 False if no devices were found and True otherwise.
252 """
253 logging.info('Finding devices to run tests on.')
254 device_list = self._GetDeviceList() 254 device_list = self._GetDeviceList()
255 random.shuffle(device_list)
256 for device in device_list: 255 for device in device_list:
257 if device['os_name'] != self._device_type: 256 if device['os_name'] != self._device_type:
258 continue 257 continue
259 if self._remote_device and device['name'] not in self._remote_device: 258 if self._remote_device and device['name'] not in self._remote_device:
260 continue 259 continue
261 if (self._remote_device_os 260 if (self._remote_device_os
262 and device['os_version'] not in self._remote_device_os): 261 and device['os_version'] not in self._remote_device_os):
263 continue 262 continue
264 if self._device_oem and device['brand'] not in self._device_oem: 263 if self._device_oem and device['brand'] not in self._device_oem:
265 continue 264 continue
266 if (self._remote_device_minimum_os 265 if (self._remote_device_minimum_os
267 and distutils.version.LooseVersion(device['os_version']) 266 and distutils.version.LooseVersion(device['os_version'])
268 < distutils.version.LooseVersion(self._remote_device_minimum_os)): 267 < distutils.version.LooseVersion(self._remote_device_minimum_os)):
269 continue 268 continue
270 if device['has_available_device']: 269 if device['has_available_device']:
271 logging.info('Found device: %s %s', 270 logging.info('Found %s available %s %s',
271 device['available_devices_count'],
272 device['name'], device['os_version']) 272 device['name'], device['os_version'])
273 self._device = device 273 self._devices.extend(
jbudorick 2015/10/26 22:23:30 I don't understand why you're adding N instances o
mikecase (-- gone --) 2015/10/27 01:27:43 I just want self._devices to be a list of all avai
274 return True 274 [device for _ in range(device['available_devices_count'])])
275 return False 275 return True if self._devices else False
276 276
277 def _FindDeviceWithTimeout(self): 277 def _FindDevicesWithTimeout(self):
278 """Find which device to use with timeout.""" 278 """Find which device to use with timeout."""
279 timeout_retry.WaitFor(self._FindDevice, wait_period=1) 279 timeout_retry.WaitFor(self._FindDevices, wait_period=1)
280 280
281 def _PrintAvailableDevices(self, device_list): 281 def _PrintAvailableDevices(self, device_list):
282 def compare_devices(a, b): 282 def compare_devices(a, b):
283 for key in ('os_version', 'name'): 283 for key in ('os_version', 'name'):
284 c = cmp(a[key], b[key]) 284 c = cmp(a[key], b[key])
285 if c: 285 if c:
286 return c 286 return c
287 return 0 287 return 0
288 288
289 logging.critical('Available %s Devices:', self._device_type) 289 logging.critical('Available %s Devices:', self._device_type)
290 logging.critical( 290 logging.critical(
291 ' %s %s %s %s %s', 291 ' %s %s %s %s %s',
292 'OS'.ljust(10), 292 'OS'.ljust(10),
293 'Device Name'.ljust(30), 293 'Device Name'.ljust(30),
294 'Available'.ljust(10), 294 'Available'.ljust(10),
295 'Busy'.ljust(10), 295 'Busy'.ljust(10),
296 'All'.ljust(10)) 296 'All'.ljust(10))
297 devices = (d for d in device_list if d['os_name'] == self._device_type) 297 devices = (d for d in device_list if d['os_name'] == self._device_type)
298 for d in sorted(devices, compare_devices): 298 for d in sorted(devices, compare_devices):
299 logging.critical( 299 logging.critical(
300 ' %s %s %s %s %s', 300 ' %s %s %s %s %s',
301 d['os_version'].ljust(10), 301 d['os_version'].ljust(10),
302 d['name'].ljust(30), 302 d['name'].ljust(30),
303 str(d['available_devices_count']).ljust(10), 303 str(d['available_devices_count']).ljust(10),
304 str(d['busy_devices_count']).ljust(10), 304 str(d['busy_devices_count']).ljust(10),
305 str(d['all_devices_count']).ljust(10)) 305 str(d['all_devices_count']).ljust(10))
306 306
307 def _GetDeviceList(self): 307 def _GetDeviceList(self):
308 with appurify_sanitized.SanitizeLogging(self._verbose_count, 308 with appurify_sanitized.SanitizeLogging(
309 logging.WARNING): 309 verbose_count=self._verbose_count,
310 dev_list_res = appurify_sanitized.api.devices_list(self._access_token) 310 level=logging.WARNING):
311 remote_device_helper.TestHttpResponse(dev_list_res, 311 device_list_response = appurify_sanitized.api.devices_list(
312 'Unable to generate access token.') 312 access_token=self._access_token)
313 return dev_list_res.json()['response'] 313 remote_device_helper.TestHttpResponse(
314 response=device_list_response,
315 error_msg='Unable to generate access token.')
316 return device_list_response.json()['response']
314 317
315 def _NoDeviceFound(self): 318 def _NoDeviceFound(self):
316 self._PrintAvailableDevices(self._GetDeviceList()) 319 self._PrintAvailableDevices(self._GetDeviceList())
317 raise remote_device_helper.RemoteDeviceError( 320 raise remote_device_helper.RemoteDeviceError(
318 'No device found.', is_infra_error=True) 321 'No device found.', is_infra_error=True)
319 322
320 @property 323 @property
321 def collect(self): 324 def collect(self):
322 return self._collect 325 return self._collect
323 326
324 @property 327 @property
325 def device_type_id(self): 328 def device_type(self):
326 return self._device['device_type_id'] 329 return self._device_type
327 330
328 @property 331 @property
329 def network_config(self): 332 def device_ids(self):
330 return self._network_config 333 return [d['device_type_id'] for d in self._devices]
331 334
332 @property 335 @property
333 def results_path(self): 336 def num_shards(self):
334 return self._results_path 337 return self._num_shards or self._DEFAULT_SHARD_COUNT
335 338
336 @property 339 @property
337 def runner_package(self): 340 def runner_package(self):
338 return self._runner_package 341 return self._runner_package
339 342
340 @property 343 @property
341 def runner_type(self): 344 def test_framework(self):
342 return self._runner_type 345 return self._test_framework
343 346
344 @property 347 @property
345 def timeouts(self): 348 def timeouts(self):
346 return self._timeouts 349 return self._timeouts
347 350
348 @property 351 @property
349 def token(self): 352 def token(self):
350 return self._access_token 353 return self._access_token
351 354
352 @property 355 @property
353 def trigger(self): 356 def trigger(self):
354 return self._trigger 357 return self._trigger
355 358
356 @property 359 @property
357 def verbose_count(self): 360 def verbose_count(self):
358 return self._verbose_count 361 return self._verbose_count
359 362
360 @property 363 @property
361 def device_type(self): 364 def network_config(self):
rnephew (Reviews Here) 2015/10/26 20:42:18 Nit: Alphabetical order on properties.
mikecase (-- gone --) 2015/10/27 01:27:43 Done.
362 return self._device_type 365 """Config to specify the network environment."""
366 return self._network_config or appurify_constants.NETWORK.WIFI_1_BAR
367
368 @property
369 def pcap_config(self):
370 """Config to record the of network traffic from the device."""
371 return self._pcap_config
372
373 @property
374 def profiler_config(self):
375 """Config to record CPU, memory, and network transfer usage."""
376 return self._profiler_config
377
378 @property
379 def videocapture_config(self):
380 """Config to set video capture during the tests."""
381 return self._videocapture_config
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698