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

Side by Side Diff: build/android/pylib/remote/device/remote_device_test_run.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 """Run specific test on specific environment.""" 5 """Run specific test on specific environment."""
6 6
7 import json 7 import json
8 import logging 8 import logging
9 import os 9 import os
10 import random
10 import re 11 import re
11 import shutil 12 import shutil
12 import string 13 import string
13 import tempfile 14 import tempfile
14 import time 15 import time
15 import zipfile 16 import zipfile
16 17
18 from devil.utils import parallelizer
17 from devil.utils import zip_utils 19 from devil.utils import zip_utils
18 from pylib.base import base_test_result 20 from pylib.base import base_test_result
19 from pylib.base import test_run 21 from pylib.base import test_run
20 from pylib.remote.device import appurify_constants
21 from pylib.remote.device import appurify_sanitized 22 from pylib.remote.device import appurify_sanitized
22 from pylib.remote.device import remote_device_helper 23 from pylib.remote.device import remote_device_helper
23 24
24 _DEVICE_OFFLINE_RE = re.compile('error: device not found') 25 _DEVICE_OFFLINE_RE = re.compile('error: device not found')
25 _LONG_MSG_RE = re.compile('longMsg=(.*)$') 26 _LONG_MSG_RE = re.compile('longMsg=(.*)$')
26 _SHORT_MSG_RE = re.compile('shortMsg=(.*)$') 27 _SHORT_MSG_RE = re.compile('shortMsg=(.*)$')
27 28
28 class RemoteDeviceTestRun(test_run.TestRun): 29 class RemoteDeviceTestRun(test_run.TestRun):
29 """Run tests on a remote device.""" 30 """Run tests on a remote device."""
30 31
31 _TEST_RUN_KEY = 'test_run' 32 WAIT_TIME = 10
32 _TEST_RUN_ID_KEY = 'test_run_id'
33
34 WAIT_TIME = 5
35 COMPLETE = 'complete' 33 COMPLETE = 'complete'
36 HEARTBEAT_INTERVAL = 300 34 HEARTBEAT_INTERVAL = 300
37 35
36 _TEST_RUN_KEY = 'test_run'
37 _TEST_RUN_IDS_KEY = 'test_run_ids'
38
38 def __init__(self, env, test_instance): 39 def __init__(self, env, test_instance):
39 """Constructor. 40 """Constructor.
40 41
41 Args: 42 Args:
42 env: Environment the tests will run in. 43 env: Environment the tests will run in.
43 test_instance: The test that will be run. 44 test_instance: The test that will be run.
44 """ 45 """
45 super(RemoteDeviceTestRun, self).__init__(env, test_instance) 46 super(RemoteDeviceTestRun, self).__init__(env, test_instance)
47 self._app_id = None
48 self._appurify_configs = None
49 self._base_tempfile_dir = ''
50 self._device_ids = None
46 self._env = env 51 self._env = env
47 self._test_instance = test_instance 52 self._test_instance = test_instance
48 self._app_id = '' 53 self._test_package = None
49 self._test_id = '' 54 self._shard_framework_configs = None
50 self._results = '' 55 self._test_run_ids = []
51 self._test_run_id = '' 56
52 self._results_temp_dir = None 57 # pylint: disable=no-self-use
58 def _GetAdditionalApks(self):
59 return NotImplementedError
60
61 def _GetAppPath(self):
62 raise NotImplementedError
63
64 def _GetTestPath(self):
65 raise NotImplementedError
66
67 def _GetTestRunnerName(self):
68 raise NotImplementedError
69
70 # pylint: disable=no-self-use
71 def _GetTestFramework(self):
72 raise NotImplementedError
73
74 # pylint: disable=no-self-use
75 def _GetFrameworkConfigs(self):
76 return NotImplementedError
77
78 # pylint: disable=unused-argument
79 def _GetShardEnvironmentVars(self, num_shards):
80 return NotImplementedError
53 81
54 #override 82 #override
55 def SetUp(self): 83 def SetUp(self):
56 """Set up a test run.""" 84 """Set up a test run."""
85 self._base_tempfile_dir = tempfile.mkdtemp()
57 if self._env.trigger: 86 if self._env.trigger:
58 self._TriggerSetUp() 87 self._TriggerSetUp()
59 elif self._env.collect: 88 elif self._env.collect:
60 assert isinstance(self._env.collect, basestring), ( 89 assert isinstance(self._env.collect, basestring), (
61 'File for storing test_run_id must be a string.') 90 'File for storing test_run_id must be a string.')
62 with open(self._env.collect, 'r') as persisted_data_file: 91 with open(self._env.collect, 'r') as persisted_data_file:
63 persisted_data = json.loads(persisted_data_file.read()) 92 persisted_data = json.loads(persisted_data_file.read())
64 self._env.LoadFrom(persisted_data) 93 self._LoadFrom(persisted_data)
65 self.LoadFrom(persisted_data) 94
95 def _ShouldShard(self):
96 raise NotImplementedError
66 97
67 def _TriggerSetUp(self): 98 def _TriggerSetUp(self):
68 """Set up the triggering of a test run.""" 99 """Set up the triggering of a test run."""
69 raise NotImplementedError 100 self._app_id = self._UploadAppToDevice(self._GetAppPath())
70 101
71 #override 102 if self._ShouldShard():
103 num_shards = min(self._env.num_shards, len(self._env.device_ids))
104 # TODO(mikecase): Change to always run with the number of shards passed.
105 # Will need some shards to wait for new devices to become available.
106 if num_shards < self._env.num_shards:
107 logging.warning(
108 'Requested to shard across %s devices, only %s devices availible.',
109 self._env.num_shards, num_shards)
110 else:
111 num_shards = 1
112 logging.critical('Sharding test run across %s device(s).', num_shards)
jbudorick 2015/10/26 22:23:30 This is info, not critical.
mikecase (-- gone --) 2015/10/27 01:27:43 Done.
113 self._device_ids = random.sample(self._env.device_ids, num_shards)
114
115 shard_env_vars = (self._GetShardEnvironmentVars(num_shards)
jbudorick 2015/10/26 22:23:30 I don't think this is structured properly. Most of
mikecase (-- gone --) 2015/10/27 01:27:43 Yeah, but it even gets messier if you want some of
116 or [{} for _ in range(num_shards)])
jbudorick 2015/10/26 22:23:30 :/ this is ugly. Maybe [{}] * num_shards also: wh
mikecase (-- gone --) 2015/10/27 01:27:43 Done.
117 framework_configs = self._GetFrameworkConfigs() or {}
118
119 if self._GetTestPath():
120 self._test_package, test_package_configs = self._PackageTest(
121 test_path=self._GetTestPath(),
122 extra_apks=self._GetAdditionalApks())
123 else:
124 test_package_configs = {}
125 framework_configs.update(test_package_configs)
126
127 self._appurify_configs = {
128 'network': self._env.network_config,
129 'pcap': self._env.pcap_config,
130 'profiler': self._env.profiler_config,
131 'videocapture': self._env.videocapture_config,
132 }
133
134 self._shard_framework_configs = []
135 for env_vars in shard_env_vars:
136 shard_framework_config = dict(framework_configs)
137 shard_framework_config['environment_vars'] = ','.join(
138 '%s=%s' % (k, v) for k, v in env_vars.iteritems())
139 self._shard_framework_configs.append(shard_framework_config)
140
141 def _Trigger(self, device_id, test_id):
142 with appurify_sanitized.SanitizeLogging(
143 verbose_count=self._env.verbose_count,
144 level=logging.WARNING):
145 test_start_response = appurify_sanitized.api.tests_run(
146 access_token=self._env.token,
147 device_type_id=device_id,
148 app_id=self._app_id,
149 test_id=test_id)
150 remote_device_helper.TestHttpResponse(
151 response=test_start_response,
152 error_msg='Unable to run test.')
153 return test_start_response.json()['response']['test_run_id']
154
72 def RunTests(self): 155 def RunTests(self):
73 """Run the test.""" 156 """Run the test."""
157
158 def trigger(trigger_args):
jbudorick 2015/10/26 22:23:30 these should be defined in their respective if blo
159 device_id = trigger_args[0]
160 configs = trigger_args[1]
161 test_id = self._UploadTestToDevice()
162 self._UploadTestConfigToDevice(
163 test_id=test_id,
164 framework_configs=configs,
165 appurify_configs=self._appurify_configs)
166 test_run_id = self._Trigger(
167 device_id=device_id,
168 test_id=test_id)
169 self._test_run_ids.append(test_run_id)
170
171 def collect(test_run_id, parsed_results):
172 download_results_file = tempfile.NamedTemporaryFile(
173 suffix='.zip',
174 dir=self._base_tempfile_dir,
175 delete=False)
176 test_output = self._Collect(test_run_id=test_run_id)
177 self._DownloadTestResults(test_output, download_results_file.name)
178 parsed_results.AddTestRunResults(self._ParseTestResults(
179 test_output=test_output,
180 results_zip=download_results_file.name))
181
74 if self._env.trigger: 182 if self._env.trigger:
75 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, 183 parallelizer.SyncParallelizer(
76 logging.WARNING): 184 zip(self._device_ids, self._shard_framework_configs)).pMap(trigger)
jbudorick 2015/10/26 22:23:30 O_O I... I don't know how I feel about this.
mikecase (-- gone --) 2015/10/27 01:27:43 Ack
77 test_start_res = appurify_sanitized.api.tests_run(
78 self._env.token, self._env.device_type_id, self._app_id,
79 self._test_id)
80 remote_device_helper.TestHttpResponse(
81 test_start_res, 'Unable to run test.')
82 self._test_run_id = test_start_res.json()['response']['test_run_id']
83 logging.info('Test run id: %s', self._test_run_id)
84
85 if self._env.collect: 185 if self._env.collect:
86 current_status = '' 186 parsed_results = base_test_result.TestRunResults()
87 timeout_counter = 0 187 parallelizer.SyncParallelizer(
88 heartbeat_counter = 0 188 self._test_run_ids).pMap(collect, parsed_results)
jbudorick 2015/10/26 22:23:30 or, unsurprisingly, this.
mikecase (-- gone --) 2015/10/27 01:27:43 Ack
89 while self._GetTestStatus(self._test_run_id) != self.COMPLETE: 189 return parsed_results
90 if self._results['detailed_status'] != current_status:
91 logging.info('Test status: %s', self._results['detailed_status'])
92 current_status = self._results['detailed_status']
93 timeout_counter = 0
94 heartbeat_counter = 0
95 if heartbeat_counter > self.HEARTBEAT_INTERVAL:
96 logging.info('Test status: %s', self._results['detailed_status'])
97 heartbeat_counter = 0
98
99 timeout = self._env.timeouts.get(
100 current_status, self._env.timeouts['unknown'])
101 if timeout_counter > timeout:
102 raise remote_device_helper.RemoteDeviceError(
103 'Timeout while in %s state for %s seconds'
104 % (current_status, timeout),
105 is_infra_error=True)
106 time.sleep(self.WAIT_TIME)
107 timeout_counter += self.WAIT_TIME
108 heartbeat_counter += self.WAIT_TIME
109 self._DownloadTestResults(self._env.results_path)
110
111 if self._results['results']['exception']:
112 raise remote_device_helper.RemoteDeviceError(
113 self._results['results']['exception'], is_infra_error=True)
114
115 return self._ParseTestResults()
116 190
117 #override 191 #override
118 def TearDown(self): 192 def TearDown(self):
119 """Tear down the test run.""" 193 """Tear down the test run."""
194 if self._base_tempfile_dir:
195 shutil.rmtree(self._base_tempfile_dir)
196
120 if self._env.collect: 197 if self._env.collect:
121 self._CollectTearDown() 198 self._CollectTearDown()
122 elif self._env.trigger: 199 elif self._env.trigger:
123 assert isinstance(self._env.trigger, basestring), ( 200 assert isinstance(self._env.trigger, basestring), (
124 'File for storing test_run_id must be a string.') 201 'File for storing test_run_id must be a string.')
125 with open(self._env.trigger, 'w') as persisted_data_file: 202 with open(self._env.trigger, 'w') as persisted_data_file:
126 persisted_data = {} 203 persisted_data = {}
127 self.DumpTo(persisted_data) 204 self._DumpTo(persisted_data)
128 self._env.DumpTo(persisted_data)
129 persisted_data_file.write(json.dumps(persisted_data)) 205 persisted_data_file.write(json.dumps(persisted_data))
130 206
207 def _Collect(self, test_run_id):
208 current_status = ''
209 timeout_counter = 0
210 heartbeat_counter = 0
211 results = self._CheckTestResults(test_run_id)
212 while results['status'] != self.COMPLETE:
213 if results['detailed_status'] != current_status:
214 logging.info('Test status: %s', results['detailed_status'])
215 current_status = results['detailed_status']
216 timeout_counter = 0
217 heartbeat_counter = 0
218 if heartbeat_counter > self.HEARTBEAT_INTERVAL:
219 logging.info('Test status: %s', results['detailed_status'])
220 heartbeat_counter = 0
221
222 timeout = self._env.timeouts.get(
223 current_status, self._env.timeouts['unknown'])
224 if timeout_counter > timeout:
225 raise remote_device_helper.RemoteDeviceError(
226 message='Timeout while in %s state for %s seconds'
227 % (current_status, timeout),
228 is_infra_error=True)
229 time.sleep(self.WAIT_TIME)
230 timeout_counter += self.WAIT_TIME
231 heartbeat_counter += self.WAIT_TIME
232 results = self._CheckTestResults(test_run_id)
233 if results['results']['exception']:
234 raise remote_device_helper.RemoteDeviceError(
235 message=results['results']['exception'],
236 is_infra_error=True)
237 return results
238
131 def _CollectTearDown(self): 239 def _CollectTearDown(self):
132 if self._GetTestStatus(self._test_run_id) != self.COMPLETE: 240 def abort_test_run(test_run_id):
rnephew (Reviews Here) 2015/10/26 20:42:18 Is there a reason you can't just pass self._AbortT
mikecase (-- gone --) 2015/10/27 01:27:43 Hmmmmmmmmmm, it seems to work your way. I assumed
133 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, 241 self._AbortTestRun(test_run_id)
134 logging.WARNING): 242 if self._test_run_ids:
135 test_abort_res = appurify_sanitized.api.tests_abort( 243 parallelizer.SyncParallelizer(self._test_run_ids).pMap(abort_test_run)
136 self._env.token, self._test_run_id, reason='Test runner exiting.')
137 remote_device_helper.TestHttpResponse(test_abort_res,
138 'Unable to abort test.')
139 if self._results_temp_dir:
140 shutil.rmtree(self._results_temp_dir)
141 244
142 def __enter__(self): 245 def __enter__(self):
143 """Set up the test run when used as a context manager.""" 246 """Set up the test run when used as a context manager."""
144 self.SetUp() 247 self.SetUp()
145 return self 248 return self
146 249
147 def __exit__(self, exc_type, exc_val, exc_tb): 250 def __exit__(self, exc_type, exc_val, exc_tb):
148 """Tear down the test run when used as a context manager.""" 251 """Tear down the test run when used as a context manager."""
149 self.TearDown() 252 self.TearDown()
150 253
151 def DumpTo(self, persisted_data): 254 def _DumpTo(self, persisted_data):
152 test_run_data = { 255 """Dump test run information to dict."""
153 self._TEST_RUN_ID_KEY: self._test_run_id, 256 test_run_data = {self._TEST_RUN_IDS_KEY: self._test_run_ids}
154 }
155 persisted_data[self._TEST_RUN_KEY] = test_run_data 257 persisted_data[self._TEST_RUN_KEY] = test_run_data
156 258
157 def LoadFrom(self, persisted_data): 259 def _LoadFrom(self, persisted_data):
260 """Load test run information."""
158 test_run_data = persisted_data[self._TEST_RUN_KEY] 261 test_run_data = persisted_data[self._TEST_RUN_KEY]
159 self._test_run_id = test_run_data[self._TEST_RUN_ID_KEY] 262 self._test_run_ids = test_run_data[self._TEST_RUN_IDS_KEY]
160 263
161 def _ParseTestResults(self): 264 def _ParseTestResults(self, test_output, results_zip):
162 raise NotImplementedError 265 raise NotImplementedError
163 266
164 def _GetTestByName(self, test_name): 267 def _DownloadTestResults(self, results, download_path):
165 """Gets test_id for specific test.
166
167 Args:
168 test_name: Test to find the ID of.
169 """
170 with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
171 logging.WARNING):
172 test_list_res = appurify_sanitized.api.tests_list(self._env.token)
173 remote_device_helper.TestHttpResponse(test_list_res,
174 'Unable to get tests list.')
175 for test in test_list_res.json()['response']:
176 if test['test_type'] == test_name:
177 return test['test_id']
178 raise remote_device_helper.RemoteDeviceError(
179 'No test found with name %s' % (test_name))
180
181 def _DownloadTestResults(self, results_path):
182 """Download the test results from remote device service. 268 """Download the test results from remote device service.
183 269
184 Downloads results in temporary location, and then copys results
185 to results_path if results_path is not set to None.
186
187 Args:
188 results_path: Path to download appurify results zipfile.
189
190 Returns: 270 Returns:
191 Path to downloaded file. 271 Path to downloaded file.
192 """ 272 """
273 logging.info('Downloading results to %s.', download_path)
274 with appurify_sanitized.SanitizeLogging(
275 verbose_count=self._env.verbose_count,
276 level=logging.WARNING):
277 appurify_sanitized.utils.wget(
278 url=results['results']['url'],
279 path=download_path)
193 280
194 if self._results_temp_dir is None: 281 def _CheckTestResults(self, test_run_id):
195 self._results_temp_dir = tempfile.mkdtemp() 282 """Checks the state of the test.
196 logging.info('Downloading results to %s.', self._results_temp_dir)
197 with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
198 logging.WARNING):
199 appurify_sanitized.utils.wget(self._results['results']['url'],
200 self._results_temp_dir + '/results')
201 if results_path:
202 logging.info('Copying results to %s', results_path)
203 if not os.path.exists(os.path.dirname(results_path)):
204 os.makedirs(os.path.dirname(results_path))
205 shutil.copy(self._results_temp_dir + '/results', results_path)
206 return self._results_temp_dir + '/results'
207
208 def _GetTestStatus(self, test_run_id):
209 """Checks the state of the test, and sets self._results
210 283
211 Args: 284 Args:
212 test_run_id: Id of test on on remote service. 285 test_run_id: Id of test on on remote service.
213 """ 286 """
287 with appurify_sanitized.SanitizeLogging(
288 verbose_count=self._env.verbose_count,
289 level=logging.WARNING):
290 test_check_response = appurify_sanitized.api.tests_check_result(
291 access_token=self._env.token,
292 test_run_id=test_run_id)
293 remote_device_helper.TestHttpResponse(
294 response=test_check_response,
295 error_msg='Unable to get test status.')
296 return test_check_response.json()['response']
214 297
215 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, 298 def _PackageTest(self, test_path, extra_apks):
216 logging.WARNING): 299 """Packages the test and dependencies into a zip.
217 test_check_res = appurify_sanitized.api.tests_check_result(
218 self._env.token, test_run_id)
219 remote_device_helper.TestHttpResponse(test_check_res,
220 'Unable to get test status.')
221 self._results = test_check_res.json()['response']
222 return self._results['status']
223 300
224 def _AmInstrumentTestSetup(self, app_path, test_path, runner_package, 301 Returns:
225 environment_variables, extra_apks=None): 302 A tuple of the path to the packaged test and a dictionary containing
226 config = {'runner': runner_package} 303 configs about how the test was packaged.
227 if environment_variables: 304 """
228 config['environment_vars'] = ','.join( 305 package_configs = {}
229 '%s=%s' % (k, v) for k, v in environment_variables.iteritems())
230
231 self._app_id = self._UploadAppToDevice(app_path)
232
233 data_deps = self._test_instance.GetDataDependencies() 306 data_deps = self._test_instance.GetDataDependencies()
234 if data_deps: 307 if data_deps:
235 with tempfile.NamedTemporaryFile(suffix='.zip') as test_with_deps: 308 test_package = tempfile.NamedTemporaryFile(
236 sdcard_files = [] 309 suffix='.zip',
237 additional_apks = [] 310 delete=False,
238 host_test = os.path.basename(test_path) 311 dir=self._base_tempfile_dir)
jbudorick 2015/10/26 22:23:30 oh. This is how you're deleting the temporary file
mikecase (-- gone --) 2015/10/27 01:27:43 Ack. All my temporary files are put into _base_tem
239 with zipfile.ZipFile(test_with_deps.name, 'w') as zip_file: 312 sdcard_files = []
240 zip_file.write(test_path, host_test, zipfile.ZIP_DEFLATED) 313 additional_apks = []
241 for h, _ in data_deps: 314 host_test = os.path.basename(test_path)
242 if os.path.isdir(h): 315 with zipfile.ZipFile(test_package.name, 'w') as zip_file:
243 zip_utils.WriteToZipFile(zip_file, h, '.') 316 zip_file.write(self._GetTestPath(), host_test, zipfile.ZIP_DEFLATED)
244 sdcard_files.extend(os.listdir(h)) 317 for h, _ in data_deps:
245 else: 318 if os.path.isdir(h):
246 zip_utils.WriteToZipFile(zip_file, h, os.path.basename(h)) 319 zip_utils.WriteToZipFile(zip_file, h, '.')
247 sdcard_files.append(os.path.basename(h)) 320 sdcard_files.extend(os.listdir(h))
248 for a in extra_apks or (): 321 else:
249 zip_utils.WriteToZipFile(zip_file, a, os.path.basename(a)) 322 zip_utils.WriteToZipFile(zip_file, h, os.path.basename(h))
250 additional_apks.append(os.path.basename(a)) 323 sdcard_files.append(os.path.basename(h))
324 for a in extra_apks or ():
325 zip_utils.WriteToZipFile(zip_file, a, os.path.basename(a))
326 additional_apks.append(os.path.basename(a))
251 327
252 config['sdcard_files'] = ','.join(sdcard_files) 328 package_configs['sdcard_files'] = ','.join(sdcard_files)
253 config['host_test'] = host_test 329 package_configs['host_test'] = host_test
254 if additional_apks: 330 if additional_apks:
255 config['additional_apks'] = ','.join(additional_apks) 331 package_configs['additional_apks'] = ','.join(additional_apks)
256 self._test_id = self._UploadTestToDevice( 332 return (test_package.name, package_configs)
257 'robotium', test_with_deps.name, app_id=self._app_id)
258 else: 333 else:
259 self._test_id = self._UploadTestToDevice('robotium', test_path) 334 return (self._GetTestPath(), package_configs)
260
261 logging.info('Setting config: %s', config)
262 appurify_configs = {}
263 if self._env.network_config:
264 appurify_configs['network'] = self._env.network_config
265 self._SetTestConfig('robotium', config, **appurify_configs)
266 335
267 def _UploadAppToDevice(self, app_path): 336 def _UploadAppToDevice(self, app_path):
268 """Upload app to device.""" 337 """Upload app to device."""
269 logging.info('Uploading %s to remote service as %s.', app_path, 338 logging.info('Uploading %s to remote service as %s.', app_path,
270 self._test_instance.suite) 339 self._test_instance.suite)
271 with open(app_path, 'rb') as apk_src: 340 with open(app_path, 'rb') as apk_src:
272 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, 341 with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
273 logging.WARNING): 342 logging.WARNING):
274 upload_results = appurify_sanitized.api.apps_upload( 343 upload_results = appurify_sanitized.api.apps_upload(
275 self._env.token, apk_src, 'raw', name=self._test_instance.suite) 344 self._env.token, apk_src, 'raw', name=self._test_instance.suite)
276 remote_device_helper.TestHttpResponse( 345 remote_device_helper.TestHttpResponse(
277 upload_results, 'Unable to upload %s.' % app_path) 346 upload_results, 'Unable to upload %s.' % app_path)
278 return upload_results.json()['response']['app_id'] 347 return upload_results.json()['response']['app_id']
279 348
280 def _UploadTestToDevice(self, test_type, test_path, app_id=None): 349 def _UploadTestToDevice(self):
281 """Upload test to device 350 """Upload test to device."""
282 Args: 351 logging.info('Shard uploading %s to remote service.', self._test_package)
283 test_type: Type of test that is being uploaded. Ex. uirobot, gtest.. 352 with open(self._test_package, 'rb') as test_src:
284 """ 353 with appurify_sanitized.SanitizeLogging(
285 logging.info('Uploading %s to remote service.', test_path) 354 verbose_count=self._env.verbose_count,
286 with open(test_path, 'rb') as test_src: 355 level=logging.WARNING):
287 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, 356 upload_results_response = appurify_sanitized.api.tests_upload(
288 logging.WARNING): 357 access_token=self._env.token,
jbudorick 2015/10/26 22:23:30 You sure added a lot of kwarg labels. Are they rea
mikecase (-- gone --) 2015/10/27 01:27:43 Added them because it made reading the code far ea
jbudorick 2015/10/27 01:30:32 to be fair, you were also outvoted 2-1 :P
289 upload_results = appurify_sanitized.api.tests_upload( 358 test_source=test_src,
290 self._env.token, test_src, 'raw', test_type, app_id=app_id) 359 test_source_type='raw',
291 remote_device_helper.TestHttpResponse(upload_results, 360 test_type=self._GetTestFramework(),
292 'Unable to upload %s.' % test_path) 361 app_id=self._app_id)
293 return upload_results.json()['response']['test_id'] 362 remote_device_helper.TestHttpResponse(
363 response=upload_results_response,
364 error_msg='Unable to upload %s.' % self._test_package)
365 return upload_results_response.json()['response']['test_id']
294 366
295 def _SetTestConfig(self, runner_type, runner_configs, 367 def _UploadTestConfigToDevice(
296 network=appurify_constants.NETWORK.WIFI_1_BAR, 368 self, test_id, framework_configs, appurify_configs):
297 pcap=0, profiler=0, videocapture=0): 369 """Generates and uploads config file for test."""
298 """Generates and uploads config file for test.
299 Args:
300 runner_configs: Configs specific to the runner you are using.
301 network: Config to specify the network environment the devices running
302 the tests will be in.
303 pcap: Option to set the recording the of network traffic from the device.
304 profiler: Option to set the recording of CPU, memory, and network
305 transfer usage in the tests.
306 videocapture: Option to set video capture during the tests.
307
308 """
309 logging.info('Generating config file for test.') 370 logging.info('Generating config file for test.')
371 config_data = ['[appurify]']
372 config_data.extend(
373 '%s=%s' % (k, v) for k, v in appurify_configs.iteritems())
374 config_data.append('[%s]' % self._GetTestFramework())
375 config_data.extend(
376 '%s=%s' % (k, v) for k, v in framework_configs.iteritems())
310 with tempfile.TemporaryFile() as config: 377 with tempfile.TemporaryFile() as config:
311 config_data = [
312 '[appurify]',
313 'network=%s' % network,
314 'pcap=%s' % pcap,
315 'profiler=%s' % profiler,
316 'videocapture=%s' % videocapture,
317 '[%s]' % runner_type
318 ]
319 config_data.extend(
320 '%s=%s' % (k, v) for k, v in runner_configs.iteritems())
321 config.write(''.join('%s\n' % l for l in config_data)) 378 config.write(''.join('%s\n' % l for l in config_data))
322 config.flush() 379 config.flush()
323 config.seek(0) 380 config.seek(0)
324 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, 381 with appurify_sanitized.SanitizeLogging(
325 logging.WARNING): 382 verbose_count=self._env.verbose_count,
326 config_response = appurify_sanitized.api.config_upload( 383 level=logging.WARNING):
327 self._env.token, config, self._test_id) 384 config_upload_response = appurify_sanitized.api.config_upload(
385 access_token=self._env.token,
386 config_src=config,
387 test_id=test_id)
328 remote_device_helper.TestHttpResponse( 388 remote_device_helper.TestHttpResponse(
329 config_response, 'Unable to upload test config.') 389 response=config_upload_response,
390 error_msg='Unable to upload test config.')
330 391
331 def _LogLogcat(self, level=logging.CRITICAL): 392 def _DetectPlatformErrors(self, results, test_output, results_zip):
393 if not test_output['results']['pass']:
394 crash_msg = None
395 for line in test_output['results']['output'].splitlines():
396 m = _LONG_MSG_RE.search(line)
397 if m:
398 crash_msg = m.group(1)
399 break
400 m = _SHORT_MSG_RE.search(line)
401 if m:
402 crash_msg = m.group(1)
403 if crash_msg:
404 self._LogLogcat(results_zip)
405 results.AddResult(base_test_result.BaseTestResult(
406 crash_msg, base_test_result.ResultType.CRASH))
407 elif self._DidDeviceGoOffline(results_zip):
408 self._LogLogcat(results_zip)
409 self._LogAdbTraceLog(results_zip)
410 raise remote_device_helper.RemoteDeviceError(
411 message='Remote service unable to reach device.',
412 is_infra_error=True)
413 else:
414 # Remote service is reporting a failure, but no failure in results obj.
415 if results.DidRunPass():
416 results.AddResult(base_test_result.BaseTestResult(
417 'Remote service detected error.',
418 base_test_result.ResultType.UNKNOWN))
419
420 def _AbortTestRun(self, test_run_id):
421 if self._CheckTestResults(test_run_id)['status'] != self.COMPLETE:
422 with appurify_sanitized.SanitizeLogging(
423 verbose_count=self._env.verbose_count,
424 level=logging.WARNING):
425 test_abort_response = appurify_sanitized.api.tests_abort(
426 access_token=self._env.token,
427 test_run_id=test_run_id,
428 reason='Test runner exiting.')
429 remote_device_helper.TestHttpResponse(
430 response=test_abort_response,
431 error_msg='Unable to abort test.')
432
433 # pylint: disable=no-self-use
434 def _LogLogcat(self, results_zip, level=logging.CRITICAL):
jbudorick 2015/10/26 22:23:30 methods that don't use self should usually be move
mikecase (-- gone --) 2015/10/27 01:27:43 Will do. Was kinda unsure since only this class re
332 """Prints out logcat downloaded from remote service. 435 """Prints out logcat downloaded from remote service.
333 Args: 436 Args:
437 results_zip: path to zipfile containing the results files.
334 level: logging level to print at. 438 level: logging level to print at.
335 439
336 Raises: 440 Raises:
337 KeyError: If appurify_results/logcat.txt file cannot be found in 441 KeyError: If appurify_results/logcat.txt file cannot be found in
338 downloaded zip. 442 downloaded zip.
339 """ 443 """
340 zip_file = self._DownloadTestResults(None) 444 with zipfile.ZipFile(results_zip) as z:
341 with zipfile.ZipFile(zip_file) as z:
342 try: 445 try:
343 logcat = z.read('appurify_results/logcat.txt') 446 logcat = z.read('appurify_results/logcat.txt')
344 printable_logcat = ''.join(c for c in logcat if c in string.printable) 447 printable_logcat = ''.join(c for c in logcat if c in string.printable)
345 for line in printable_logcat.splitlines(): 448 for line in printable_logcat.splitlines():
346 logging.log(level, line) 449 logging.log(level, line)
347 except KeyError: 450 except KeyError:
348 logging.error('No logcat found.') 451 logging.error('No logcat found.')
349 452
350 def _LogAdbTraceLog(self): 453 # pylint: disable=no-self-use
351 zip_file = self._DownloadTestResults(None) 454 def _LogAdbTraceLog(self, results_zip):
352 with zipfile.ZipFile(zip_file) as z: 455 with zipfile.ZipFile(results_zip) as z:
353 adb_trace_log = z.read('adb_trace.log') 456 adb_trace_log = z.read('adb_trace.log')
354 for line in adb_trace_log.splitlines(): 457 for line in adb_trace_log.splitlines():
355 logging.critical(line) 458 logging.critical(line)
356 459
357 def _DidDeviceGoOffline(self): 460 # pylint: disable=no-self-use
358 zip_file = self._DownloadTestResults(None) 461 def _DidDeviceGoOffline(self, results_zip):
359 with zipfile.ZipFile(zip_file) as z: 462 with zipfile.ZipFile(results_zip) as z:
360 adb_trace_log = z.read('adb_trace.log') 463 adb_trace_log = z.read('adb_trace.log')
361 if any(_DEVICE_OFFLINE_RE.search(l) for l in adb_trace_log.splitlines()): 464 if any(_DEVICE_OFFLINE_RE.search(l) for l in adb_trace_log.splitlines()):
362 return True 465 return True
363 return False 466 return False
364
365 def _DetectPlatformErrors(self, results):
366 if not self._results['results']['pass']:
367 crash_msg = None
368 for line in self._results['results']['output'].splitlines():
369 m = _LONG_MSG_RE.search(line)
370 if m:
371 crash_msg = m.group(1)
372 break
373 m = _SHORT_MSG_RE.search(line)
374 if m:
375 crash_msg = m.group(1)
376 if crash_msg:
377 self._LogLogcat()
378 results.AddResult(base_test_result.BaseTestResult(
379 crash_msg, base_test_result.ResultType.CRASH))
380 elif self._DidDeviceGoOffline():
381 self._LogLogcat()
382 self._LogAdbTraceLog()
383 raise remote_device_helper.RemoteDeviceError(
384 'Remote service unable to reach device.', is_infra_error=True)
385 else:
386 # Remote service is reporting a failure, but no failure in results obj.
387 if results.DidRunPass():
388 results.AddResult(base_test_result.BaseTestResult(
389 'Remote service detected error.',
390 base_test_result.ResultType.UNKNOWN))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698