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

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: Minor change to uirobot SetupTestShards. 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 reraiser_thread
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 = {
49 'network': env.network_config,
50 'pcap': env.pcap_config,
51 'profiler': env.profiler_config,
52 'videocapture': env.videocapture_config,
53 }
54 self._device_ids = None
46 self._env = env 55 self._env = env
47 self._test_instance = test_instance 56 self._test_instance = test_instance
48 self._app_id = '' 57 self._test_ids = []
49 self._test_id = '' 58 self._test_run_ids = []
50 self._results = '' 59
51 self._test_run_id = '' 60 def _GetAppPath(self):
52 self._results_temp_dir = None 61 raise NotImplementedError
62
63 def _GetTestFramework(self):
64 raise NotImplementedError
65
66 def _SetupTestShards(self, num_shards):
67 raise NotImplementedError
53 68
54 #override 69 #override
55 def SetUp(self): 70 def SetUp(self):
56 """Set up a test run.""" 71 """Set up a test run."""
57 if self._env.trigger: 72 if self._env.trigger:
58 self._TriggerSetUp() 73 num_shards = 1
74 if self._ShouldShard():
75 num_shards = min(self._env.num_shards, len(self._env.device_ids))
76 # TODO(mikecase): Change to always run with the number of shards passed.
77 # Will need some shards to wait for new devices to become available.
78 if num_shards < self._env.num_shards:
79 logging.warning(
80 'Requested to shard across %s devices, only %s availible.',
81 self._env.num_shards, num_shards)
82 logging.info('Sharding test run across %s device(s).', num_shards)
83
84 self._test_ids = self._SetupTestShards(num_shards)
85
59 elif self._env.collect: 86 elif self._env.collect:
60 assert isinstance(self._env.collect, basestring), ( 87 assert isinstance(self._env.collect, basestring), (
61 'File for storing test_run_id must be a string.') 88 'File for storing test_run_id must be a string.')
62 with open(self._env.collect, 'r') as persisted_data_file: 89 with open(self._env.collect, 'r') as persisted_data_file:
63 persisted_data = json.loads(persisted_data_file.read()) 90 persisted_data = json.loads(persisted_data_file.read())
64 self._env.LoadFrom(persisted_data) 91 self._LoadFrom(persisted_data)
65 self.LoadFrom(persisted_data)
66 92
67 def _TriggerSetUp(self): 93 # pylint: disable=no-self-use
68 """Set up the triggering of a test run.""" 94 def _ShouldShard(self):
69 raise NotImplementedError 95 return False
70 96
71 #override 97 def _Trigger(self, device_id, test_id):
98 with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
99 logging.WARNING):
100 test_start_response = appurify_sanitized.api.tests_run(
101 access_token=self._env.token, device_type_id=device_id,
102 app_id=self._app_id, test_id=test_id)
103 remote_device_helper.TestHttpResponse(test_start_response,
104 'Unable to run test.')
105 return test_start_response.json()['response']['test_run_id']
106
72 def RunTests(self): 107 def RunTests(self):
73 """Run the test.""" 108 """Run the test."""
109
74 if self._env.trigger: 110 if self._env.trigger:
75 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, 111 def trigger(device_id, test_id):
76 logging.WARNING): 112 test_run_id = self._Trigger(device_id, test_id)
77 test_start_res = appurify_sanitized.api.tests_run( 113 self._test_run_ids.append(test_run_id)
78 self._env.token, self._env.device_type_id, self._app_id, 114
79 self._test_id) 115 self._app_id = self._UploadAppToDevice(self._GetAppPath())
80 remote_device_helper.TestHttpResponse( 116 self._device_ids = random.sample(
81 test_start_res, 'Unable to run test.') 117 self._env.device_ids, len(self._test_ids))
82 self._test_run_id = test_start_res.json()['response']['test_run_id'] 118
83 logging.info('Test run id: %s', self._test_run_id) 119 reraiser_thread.RunAsync(
120 funcs=[trigger]*len(self._test_ids),
121 args=zip(self._device_ids, self._test_ids),
122 names=['trigger test %s' % test_id for test_id in self._test_ids])
84 123
85 if self._env.collect: 124 if self._env.collect:
86 current_status = '' 125 parsed_results = base_test_result.TestRunResults()
87 timeout_counter = 0
88 heartbeat_counter = 0
89 while self._GetTestStatus(self._test_run_id) != self.COMPLETE:
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 126
99 timeout = self._env.timeouts.get( 127 def collect(test_run_id):
100 current_status, self._env.timeouts['unknown']) 128 with tempfile.NamedTemporaryFile(
101 if timeout_counter > timeout: 129 prefix='results-', suffix='.zip') as download_results_file:
102 raise remote_device_helper.RemoteDeviceError( 130 test_output = self._Collect(test_run_id=test_run_id)
103 'Timeout while in %s state for %s seconds' 131 self._DownloadTestResults(test_output, download_results_file.name)
104 % (current_status, timeout), 132 parsed_results.AddTestRunResults(self._ParseTestResults(
105 is_infra_error=True) 133 test_output, download_results_file.name))
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 134
111 if self._results['results']['exception']: 135 reraiser_thread.RunAsync(
112 raise remote_device_helper.RemoteDeviceError( 136 funcs=[collect]*len(self._test_run_ids),
113 self._results['results']['exception'], is_infra_error=True) 137 args=[[run_id] for run_id in self._test_run_ids],
138 names=['collect test %s' % run_id for run_id in self._test_run_ids])
114 139
115 return self._ParseTestResults() 140 return parsed_results
116 141
117 #override 142 #override
118 def TearDown(self): 143 def TearDown(self):
119 """Tear down the test run.""" 144 """Tear down the test run."""
145
120 if self._env.collect: 146 if self._env.collect:
121 self._CollectTearDown() 147 self._CollectTearDown()
122 elif self._env.trigger: 148 elif self._env.trigger:
123 assert isinstance(self._env.trigger, basestring), ( 149 assert isinstance(self._env.trigger, basestring), (
124 'File for storing test_run_id must be a string.') 150 'File for storing test_run_id must be a string.')
125 with open(self._env.trigger, 'w') as persisted_data_file: 151 with open(self._env.trigger, 'w') as persisted_data_file:
126 persisted_data = {} 152 persisted_data = {}
127 self.DumpTo(persisted_data) 153 self._DumpTo(persisted_data)
128 self._env.DumpTo(persisted_data)
129 persisted_data_file.write(json.dumps(persisted_data)) 154 persisted_data_file.write(json.dumps(persisted_data))
130 155
156 def _Collect(self, test_run_id):
157 current_status = ''
158 timeout_counter = 0
159 heartbeat_counter = 0
160 results = self._CheckTestResults(test_run_id)
161 while results['status'] != self.COMPLETE:
162 if results['detailed_status'] != current_status:
163 logging.info('Test status: %s', results['detailed_status'])
164 current_status = results['detailed_status']
165 timeout_counter = 0
166 heartbeat_counter = 0
167 if heartbeat_counter > self.HEARTBEAT_INTERVAL:
168 logging.info('Test status: %s', results['detailed_status'])
169 heartbeat_counter = 0
170
171 timeout = self._env.timeouts.get(
172 current_status, self._env.timeouts['unknown'])
173 if timeout_counter > timeout:
174 raise remote_device_helper.RemoteDeviceError(
175 'Timeout while in %s state for %s seconds'
176 % (current_status, timeout),
177 is_infra_error=True)
178 time.sleep(self.WAIT_TIME)
179 timeout_counter += self.WAIT_TIME
180 heartbeat_counter += self.WAIT_TIME
181 results = self._CheckTestResults(test_run_id)
182 if results['results']['exception']:
183 raise remote_device_helper.RemoteDeviceError(
184 results['results']['exception'],
185 is_infra_error=True)
186 return results
187
131 def _CollectTearDown(self): 188 def _CollectTearDown(self):
132 if self._GetTestStatus(self._test_run_id) != self.COMPLETE: 189 if self._test_run_ids:
133 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, 190 reraiser_thread.RunAsync(
134 logging.WARNING): 191 funcs=[self._AbortTestRun]*len(self._test_run_ids),
135 test_abort_res = appurify_sanitized.api.tests_abort( 192 args=[[run_id] for run_id in self._test_run_ids],
136 self._env.token, self._test_run_id, reason='Test runner exiting.') 193 names=['aborting test %s' % run_id for run_id in self._test_run_ids])
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 194
142 def __enter__(self): 195 def __enter__(self):
143 """Set up the test run when used as a context manager.""" 196 """Set up the test run when used as a context manager."""
144 self.SetUp() 197 self.SetUp()
145 return self 198 return self
146 199
147 def __exit__(self, exc_type, exc_val, exc_tb): 200 def __exit__(self, exc_type, exc_val, exc_tb):
148 """Tear down the test run when used as a context manager.""" 201 """Tear down the test run when used as a context manager."""
149 self.TearDown() 202 self.TearDown()
150 203
151 def DumpTo(self, persisted_data): 204 def _PackageTest(self, dest_path, test_apk_path, data_deps, extra_apks=None):
152 test_run_data = { 205 packaged_data_deps = []
153 self._TEST_RUN_ID_KEY: self._test_run_id, 206 packaged_extra_apks = []
207 packaged_test_path = os.path.basename(test_apk_path)
208
209 with zipfile.ZipFile(dest_path, 'w') as zip_file:
210 zip_file.write(test_apk_path, packaged_test_path, zipfile.ZIP_DEFLATED)
211 for h, _ in data_deps:
212 if os.path.isdir(h):
213 zip_utils.WriteToZipFile(zip_file, h, '.')
214 packaged_data_deps.extend(os.listdir(h))
215 else:
216 zip_utils.WriteToZipFile(zip_file, h, os.path.basename(h))
217 packaged_data_deps.append(os.path.basename(h))
218 for a in extra_apks or ():
219 zip_utils.WriteToZipFile(zip_file, a, os.path.basename(a))
220 packaged_extra_apks.append(os.path.basename(a))
221
222 configs = {
223 'host_test': packaged_test_path,
224 'sdcard_files': ','.join(packaged_data_deps),
154 } 225 }
226 if packaged_extra_apks:
227 configs['additional_apks'] = ','.join(packaged_extra_apks)
228 return configs
229
230 def _DumpTo(self, persisted_data):
231 """Dump test run information to dict."""
232 test_run_data = {self._TEST_RUN_IDS_KEY: self._test_run_ids}
155 persisted_data[self._TEST_RUN_KEY] = test_run_data 233 persisted_data[self._TEST_RUN_KEY] = test_run_data
156 234
157 def LoadFrom(self, persisted_data): 235 def _LoadFrom(self, persisted_data):
236 """Load test run information."""
158 test_run_data = persisted_data[self._TEST_RUN_KEY] 237 test_run_data = persisted_data[self._TEST_RUN_KEY]
159 self._test_run_id = test_run_data[self._TEST_RUN_ID_KEY] 238 self._test_run_ids = test_run_data[self._TEST_RUN_IDS_KEY]
160 239
161 def _ParseTestResults(self): 240 def _ParseTestResults(self, test_output, results_zip):
162 raise NotImplementedError 241 raise NotImplementedError
163 242
164 def _GetTestByName(self, test_name): 243 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. 244 """Download the test results from remote device service.
183 245
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: 246 Returns:
191 Path to downloaded file. 247 Path to downloaded file.
192 """ 248 """
249 logging.info('Downloading results to %s.', download_path)
250 with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
251 logging.WARNING):
252 appurify_sanitized.utils.wget(
253 url=results['results']['url'], path=download_path)
254 if self._env.results_dir:
255 logging.info('Copying results to %s from %s',
256 self._env.results_dir, download_path)
257 if not os.path.exists(os.path.dirname(self._env.results_dir)):
258 os.makedirs(self._env.results_dir)
259 shutil.copy(download_path, self._env.results_dir)
193 260
194 if self._results_temp_dir is None: 261 def _CheckTestResults(self, test_run_id):
195 self._results_temp_dir = tempfile.mkdtemp() 262 """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 263
211 Args: 264 Args:
212 test_run_id: Id of test on on remote service. 265 test_run_id: Id of test on on remote service.
213 """ 266 """
214
215 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, 267 with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
216 logging.WARNING): 268 logging.WARNING):
217 test_check_res = appurify_sanitized.api.tests_check_result( 269 test_check_response = appurify_sanitized.api.tests_check_result(
218 self._env.token, test_run_id) 270 access_token=self._env.token, test_run_id=test_run_id)
219 remote_device_helper.TestHttpResponse(test_check_res, 271 remote_device_helper.TestHttpResponse(test_check_response,
220 'Unable to get test status.') 272 'Unable to get test status.')
221 self._results = test_check_res.json()['response'] 273 return test_check_response.json()['response']
222 return self._results['status']
223
224 def _AmInstrumentTestSetup(self, app_path, test_path, runner_package,
225 environment_variables, extra_apks=None):
226 config = {'runner': runner_package}
227 if environment_variables:
228 config['environment_vars'] = ','.join(
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()
234 if data_deps:
235 with tempfile.NamedTemporaryFile(suffix='.zip') as test_with_deps:
236 sdcard_files = []
237 additional_apks = []
238 host_test = os.path.basename(test_path)
239 with zipfile.ZipFile(test_with_deps.name, 'w') as zip_file:
240 zip_file.write(test_path, host_test, zipfile.ZIP_DEFLATED)
241 for h, _ in data_deps:
242 if os.path.isdir(h):
243 zip_utils.WriteToZipFile(zip_file, h, '.')
244 sdcard_files.extend(os.listdir(h))
245 else:
246 zip_utils.WriteToZipFile(zip_file, h, os.path.basename(h))
247 sdcard_files.append(os.path.basename(h))
248 for a in extra_apks or ():
249 zip_utils.WriteToZipFile(zip_file, a, os.path.basename(a))
250 additional_apks.append(os.path.basename(a))
251
252 config['sdcard_files'] = ','.join(sdcard_files)
253 config['host_test'] = host_test
254 if additional_apks:
255 config['additional_apks'] = ','.join(additional_apks)
256 self._test_id = self._UploadTestToDevice(
257 'robotium', test_with_deps.name, app_id=self._app_id)
258 else:
259 self._test_id = self._UploadTestToDevice('robotium', test_path)
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 274
267 def _UploadAppToDevice(self, app_path): 275 def _UploadAppToDevice(self, app_path):
268 """Upload app to device.""" 276 """Upload app to device."""
269 logging.info('Uploading %s to remote service as %s.', app_path, 277 logging.info('Uploading %s to remote service as %s.', app_path,
270 self._test_instance.suite) 278 self._test_instance.suite)
271 with open(app_path, 'rb') as apk_src: 279 with open(app_path, 'rb') as apk_src:
272 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, 280 with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
273 logging.WARNING): 281 logging.WARNING):
274 upload_results = appurify_sanitized.api.apps_upload( 282 upload_results = appurify_sanitized.api.apps_upload(
275 self._env.token, apk_src, 'raw', name=self._test_instance.suite) 283 self._env.token, apk_src, 'raw', name=self._test_instance.suite)
276 remote_device_helper.TestHttpResponse( 284 remote_device_helper.TestHttpResponse(upload_results,
277 upload_results, 'Unable to upload %s.' % app_path) 285 'Unable to upload %s.' % app_path)
278 return upload_results.json()['response']['app_id'] 286 return upload_results.json()['response']['app_id']
279 287
280 def _UploadTestToDevice(self, test_type, test_path, app_id=None): 288 def _UploadTestToDevice(self, test_path):
281 """Upload test to device 289 """Upload test to device."""
282 Args: 290 logging.info('Shard uploading %s to remote service.', test_path)
283 test_type: Type of test that is being uploaded. Ex. uirobot, gtest..
284 """
285 logging.info('Uploading %s to remote service.', test_path)
286 with open(test_path, 'rb') as test_src: 291 with open(test_path, 'rb') as test_src:
287 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, 292 with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
288 logging.WARNING): 293 logging.WARNING):
289 upload_results = appurify_sanitized.api.tests_upload( 294 upload_results_response = appurify_sanitized.api.tests_upload(
290 self._env.token, test_src, 'raw', test_type, app_id=app_id) 295 access_token=self._env.token, test_source=test_src,
291 remote_device_helper.TestHttpResponse(upload_results, 296 test_source_type='raw', test_type=self._GetTestFramework(),
292 'Unable to upload %s.' % test_path) 297 app_id=self._app_id)
293 return upload_results.json()['response']['test_id'] 298 remote_device_helper.TestHttpResponse(
299 upload_results_response, 'Unable to upload %s.' % test_path)
300 return upload_results_response.json()['response']['test_id']
294 301
295 def _SetTestConfig(self, runner_type, runner_configs, 302 def _UploadTestConfigToDevice(
296 network=appurify_constants.NETWORK.WIFI_1_BAR, 303 self, test_id, framework_configs, appurify_configs):
297 pcap=0, profiler=0, videocapture=0): 304 """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.') 305 logging.info('Generating config file for test.')
306 config_data = ['[appurify]']
307 config_data.extend(
308 '%s=%s' % (k, v) for k, v in appurify_configs.iteritems())
309 config_data.append('[%s]' % self._GetTestFramework())
310 config_data.extend(
311 '%s=%s' % (k, v) for k, v in framework_configs.iteritems())
310 with tempfile.TemporaryFile() as config: 312 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)) 313 config.write(''.join('%s\n' % l for l in config_data))
322 config.flush() 314 config.flush()
323 config.seek(0) 315 config.seek(0)
324 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, 316 with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
325 logging.WARNING): 317 logging.WARNING):
326 config_response = appurify_sanitized.api.config_upload( 318 config_upload_response = appurify_sanitized.api.config_upload(
327 self._env.token, config, self._test_id) 319 access_token=self._env.token, config_src=config, test_id=test_id)
328 remote_device_helper.TestHttpResponse( 320 remote_device_helper.TestHttpResponse(config_upload_response,
329 config_response, 'Unable to upload test config.') 321 'Unable to upload test config.')
330 322
331 def _LogLogcat(self, level=logging.CRITICAL): 323 def _AbortTestRun(self, test_run_id):
332 """Prints out logcat downloaded from remote service. 324 logging.info('Aborting test run %s.', test_run_id)
333 Args: 325 if self._CheckTestResults(test_run_id)['status'] != self.COMPLETE:
334 level: logging level to print at. 326 with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
327 logging.WARNING):
328 test_abort_response = appurify_sanitized.api.tests_abort(
329 access_token=self._env.token, test_run_id=test_run_id,
330 reason='Test runner exiting.')
331 remote_device_helper.TestHttpResponse(test_abort_response,
332 'Unable to abort test.')
335 333
336 Raises: 334 def DetectPlatformErrors(results, test_output, results_zip):
337 KeyError: If appurify_results/logcat.txt file cannot be found in 335 if not test_output['results']['pass']:
338 downloaded zip. 336 crash_msg = None
339 """ 337 for line in test_output['results']['output'].splitlines():
340 zip_file = self._DownloadTestResults(None) 338 m = _LONG_MSG_RE.search(line)
341 with zipfile.ZipFile(zip_file) as z: 339 if m:
342 try: 340 crash_msg = m.group(1)
343 logcat = z.read('appurify_results/logcat.txt') 341 break
344 printable_logcat = ''.join(c for c in logcat if c in string.printable) 342 m = _SHORT_MSG_RE.search(line)
345 for line in printable_logcat.splitlines(): 343 if m:
346 logging.log(level, line) 344 crash_msg = m.group(1)
347 except KeyError: 345 if crash_msg:
348 logging.error('No logcat found.') 346 _LogLogcat(results_zip)
347 results.AddResult(base_test_result.BaseTestResult(
348 crash_msg, base_test_result.ResultType.CRASH))
349 elif _DidDeviceGoOffline(results_zip):
350 _LogLogcat(results_zip)
351 _LogAdbTraceLog(results_zip)
352 raise remote_device_helper.RemoteDeviceError(
353 'Remote service unable to reach device.',
354 is_infra_error=True)
355 else:
356 # Remote service is reporting a failure, but no failure in results obj.
357 if results.DidRunPass():
358 results.AddResult(base_test_result.BaseTestResult(
359 'Remote service detected error.',
360 base_test_result.ResultType.UNKNOWN))
349 361
350 def _LogAdbTraceLog(self): 362 def _LogLogcat(results_zip, level=logging.CRITICAL):
351 zip_file = self._DownloadTestResults(None) 363 """Prints out logcat downloaded from remote service.
352 with zipfile.ZipFile(zip_file) as z: 364 Args:
353 adb_trace_log = z.read('adb_trace.log') 365 results_zip: path to zipfile containing the results files.
354 for line in adb_trace_log.splitlines(): 366 level: logging level to print at.
355 logging.critical(line)
356 367
357 def _DidDeviceGoOffline(self): 368 Raises:
358 zip_file = self._DownloadTestResults(None) 369 KeyError: If appurify_results/logcat.txt file cannot be found in
359 with zipfile.ZipFile(zip_file) as z: 370 downloaded zip.
360 adb_trace_log = z.read('adb_trace.log') 371 """
361 if any(_DEVICE_OFFLINE_RE.search(l) for l in adb_trace_log.splitlines()): 372 with zipfile.ZipFile(results_zip) as z:
362 return True 373 try:
363 return False 374 logcat = z.read('appurify_results/logcat.txt')
375 printable_logcat = ''.join(c for c in logcat if c in string.printable)
376 for line in printable_logcat.splitlines():
377 logging.log(level, line)
378 except KeyError:
379 logging.error('No logcat found.')
364 380
365 def _DetectPlatformErrors(self, results): 381 def _LogAdbTraceLog(results_zip):
366 if not self._results['results']['pass']: 382 with zipfile.ZipFile(results_zip) as z:
367 crash_msg = None 383 adb_trace_log = z.read('adb_trace.log')
368 for line in self._results['results']['output'].splitlines(): 384 for line in adb_trace_log.splitlines():
369 m = _LONG_MSG_RE.search(line) 385 logging.critical(line)
370 if m: 386
371 crash_msg = m.group(1) 387 def _DidDeviceGoOffline(results_zip):
372 break 388 with zipfile.ZipFile(results_zip) as z:
373 m = _SHORT_MSG_RE.search(line) 389 adb_trace_log = z.read('adb_trace.log')
374 if m: 390 if any(_DEVICE_OFFLINE_RE.search(l) for l in adb_trace_log.splitlines()):
375 crash_msg = m.group(1) 391 return True
376 if crash_msg: 392 return False
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