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

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