OLD | NEW |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Run specific test on specific environment.""" | 5 """Run specific test on specific environment.""" |
6 | 6 |
| 7 import json |
7 import logging | 8 import logging |
8 import os | 9 import os |
9 import sys | 10 import sys |
10 import tempfile | 11 import tempfile |
11 import time | 12 import time |
12 import zipfile | 13 import zipfile |
13 | 14 |
14 from pylib import constants | 15 from pylib import constants |
15 from pylib.base import test_run | 16 from pylib.base import test_run |
16 from pylib.remote.device import appurify_sanitized | 17 from pylib.remote.device import appurify_sanitized |
17 from pylib.remote.device import remote_device_helper | 18 from pylib.remote.device import remote_device_helper |
18 from pylib.utils import zip_utils | 19 from pylib.utils import zip_utils |
19 | 20 |
20 class RemoteDeviceTestRun(test_run.TestRun): | 21 class RemoteDeviceTestRun(test_run.TestRun): |
21 """Run gtests and uirobot tests on a remote device.""" | 22 """Run gtests and uirobot tests on a remote device.""" |
22 | 23 |
| 24 _TEST_RUN_KEY = 'test_run' |
| 25 _TEST_RUN_ID_KEY = 'test_run_id' |
| 26 |
23 WAIT_TIME = 5 | 27 WAIT_TIME = 5 |
24 COMPLETE = 'complete' | 28 COMPLETE = 'complete' |
25 HEARTBEAT_INTERVAL = 300 | 29 HEARTBEAT_INTERVAL = 300 |
26 | 30 |
27 def __init__(self, env, test_instance): | 31 def __init__(self, env, test_instance): |
28 """Constructor. | 32 """Constructor. |
29 | 33 |
30 Args: | 34 Args: |
31 env: Environment the tests will run in. | 35 env: Environment the tests will run in. |
32 test_instance: The test that will be run. | 36 test_instance: The test that will be run. |
33 """ | 37 """ |
34 super(RemoteDeviceTestRun, self).__init__(env, test_instance) | 38 super(RemoteDeviceTestRun, self).__init__(env, test_instance) |
35 self._env = env | 39 self._env = env |
36 self._test_instance = test_instance | 40 self._test_instance = test_instance |
37 self._app_id = '' | 41 self._app_id = '' |
38 self._test_id = '' | 42 self._test_id = '' |
39 self._results = '' | 43 self._results = '' |
40 self._test_run_id = '' | 44 self._test_run_id = '' |
41 | 45 |
42 #override | 46 #override |
| 47 def SetUp(self): |
| 48 """Set up a test run.""" |
| 49 if self._env.trigger: |
| 50 self._TriggerSetUp() |
| 51 elif self._env.collect: |
| 52 assert isinstance(self._env.trigger, basestring), ( |
| 53 'File for storing test_run_id must be a string.') |
| 54 with open(self._env.collect, 'r') as persisted_data_file: |
| 55 persisted_data = json.loads(persisted_data_file.read()) |
| 56 self._env.LoadFrom(persisted_data) |
| 57 self.LoadFrom(persisted_data) |
| 58 |
| 59 def _TriggerSetUp(self): |
| 60 """Set up the triggering of a test run.""" |
| 61 raise NotImplementedError |
| 62 |
| 63 #override |
43 def RunTests(self): | 64 def RunTests(self): |
44 """Run the test.""" | 65 """Run the test.""" |
45 if self._env.trigger: | 66 if self._env.trigger: |
46 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, | 67 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, |
47 logging.WARNING): | 68 logging.WARNING): |
48 test_start_res = appurify_sanitized.api.tests_run( | 69 test_start_res = appurify_sanitized.api.tests_run( |
49 self._env.token, self._env.device, self._app_id, self._test_id) | 70 self._env.token, self._env.device_type_id, self._app_id, |
| 71 self._test_id) |
50 remote_device_helper.TestHttpResponse( | 72 remote_device_helper.TestHttpResponse( |
51 test_start_res, 'Unable to run test.') | 73 test_start_res, 'Unable to run test.') |
52 self._test_run_id = test_start_res.json()['response']['test_run_id'] | 74 self._test_run_id = test_start_res.json()['response']['test_run_id'] |
53 logging.info('Test run id: %s' % self._test_run_id) | 75 logging.info('Test run id: %s' % self._test_run_id) |
54 if not self._env.collect: | |
55 assert isinstance(self._env.trigger, basestring), ( | |
56 'File for storing test_run_id must be a string.') | |
57 with open(self._env.trigger, 'w') as test_run_id_file: | |
58 test_run_id_file.write(self._test_run_id) | |
59 | 76 |
60 if self._env.collect: | 77 if self._env.collect: |
61 if not self._env.trigger: | |
62 assert isinstance(self._env.trigger, basestring), ( | |
63 'File for storing test_run_id must be a string.') | |
64 with open(self._env.collect, 'r') as test_run_id_file: | |
65 self._test_run_id = test_run_id_file.read().strip() | |
66 current_status = '' | 78 current_status = '' |
67 timeout_counter = 0 | 79 timeout_counter = 0 |
68 heartbeat_counter = 0 | 80 heartbeat_counter = 0 |
69 while self._GetTestStatus(self._test_run_id) != self.COMPLETE: | 81 while self._GetTestStatus(self._test_run_id) != self.COMPLETE: |
70 if self._results['detailed_status'] != current_status: | 82 if self._results['detailed_status'] != current_status: |
71 logging.info('Test status: %s', self._results['detailed_status']) | 83 logging.info('Test status: %s', self._results['detailed_status']) |
72 current_status = self._results['detailed_status'] | 84 current_status = self._results['detailed_status'] |
73 timeout_counter = 0 | 85 timeout_counter = 0 |
74 heartbeat_counter = 0 | 86 heartbeat_counter = 0 |
75 if heartbeat_counter > self.HEARTBEAT_INTERVAL: | 87 if heartbeat_counter > self.HEARTBEAT_INTERVAL: |
76 logging.info('Test status: %s', self._results['detailed_status']) | 88 logging.info('Test status: %s', self._results['detailed_status']) |
77 heartbeat_counter = 0 | 89 heartbeat_counter = 0 |
78 | 90 |
79 timeout = self._env.timeouts.get( | 91 timeout = self._env.timeouts.get( |
80 current_status, self._env.timeouts['unknown']) | 92 current_status, self._env.timeouts['unknown']) |
81 if timeout_counter > timeout: | 93 if timeout_counter > timeout: |
82 raise remote_device_helper.RemoteDeviceError( | 94 raise remote_device_helper.RemoteDeviceError( |
83 'Timeout while in %s state for %s seconds' | 95 'Timeout while in %s state for %s seconds' |
84 % (current_status, timeout)) | 96 % (current_status, timeout)) |
85 time.sleep(self.WAIT_TIME) | 97 time.sleep(self.WAIT_TIME) |
86 timeout_counter += self.WAIT_TIME | 98 timeout_counter += self.WAIT_TIME |
87 heartbeat_counter += self.WAIT_TIME | 99 heartbeat_counter += self.WAIT_TIME |
88 self._DownloadTestResults(self._env.results_path) | 100 self._DownloadTestResults(self._env.results_path) |
89 return self._ParseTestResults() | 101 return self._ParseTestResults() |
90 | 102 |
91 #override | 103 #override |
92 def TearDown(self): | 104 def TearDown(self): |
93 """Tear down the test run.""" | 105 """Tear down the test run.""" |
94 if (self._env.collect | 106 if self._env.collect: |
95 and self._GetTestStatus(self._test_run_id) != self.COMPLETE): | 107 self._CollectTearDown() |
| 108 elif self._env.trigger: |
| 109 assert isinstance(self._env.trigger, basestring), ( |
| 110 'File for storing test_run_id must be a string.') |
| 111 with open(self._env.trigger, 'w') as persisted_data_file: |
| 112 persisted_data = {} |
| 113 self.DumpTo(persisted_data) |
| 114 self._env.DumpTo(persisted_data) |
| 115 persisted_data_file.write(json.dumps(persisted_data)) |
| 116 |
| 117 def _CollectTearDown(self): |
| 118 if self._GetTestStatus(self._test_run_id) != self.COMPLETE: |
96 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, | 119 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, |
97 logging.WARNING): | 120 logging.WARNING): |
98 test_abort_res = appurify_sanitized.api.tests_abort( | 121 test_abort_res = appurify_sanitized.api.tests_abort( |
99 self._env.token, self._test_run_id, reason='Test runner exiting.') | 122 self._env.token, self._test_run_id, reason='Test runner exiting.') |
100 remote_device_helper.TestHttpResponse(test_abort_res, | 123 remote_device_helper.TestHttpResponse(test_abort_res, |
101 'Unable to abort test.') | 124 'Unable to abort test.') |
102 | 125 |
103 def __enter__(self): | 126 def __enter__(self): |
104 """Set up the test run when used as a context manager.""" | 127 """Set up the test run when used as a context manager.""" |
105 self.SetUp() | 128 self.SetUp() |
106 return self | 129 return self |
107 | 130 |
108 def __exit__(self, exc_type, exc_val, exc_tb): | 131 def __exit__(self, exc_type, exc_val, exc_tb): |
109 """Tear down the test run when used as a context manager.""" | 132 """Tear down the test run when used as a context manager.""" |
110 self.TearDown() | 133 self.TearDown() |
111 | 134 |
112 #override | 135 def DumpTo(self, persisted_data): |
113 def SetUp(self): | 136 test_run_data = { |
114 """Set up a test run.""" | 137 self._TEST_RUN_ID_KEY: self._test_run_id, |
115 if self._env.trigger: | 138 } |
116 self._TriggerSetUp() | 139 persisted_data[self._TEST_RUN_KEY] = test_run_data |
117 | 140 |
118 def _TriggerSetUp(self): | 141 def LoadFrom(self, persisted_data): |
119 """Set up the triggering of a test run.""" | 142 test_run_data = persisted_data[self._TEST_RUN_KEY] |
120 raise NotImplementedError | 143 self._test_run_id = test_run_data[self._TEST_RUN_ID_KEY] |
121 | 144 |
122 def _ParseTestResults(self): | 145 def _ParseTestResults(self): |
123 raise NotImplementedError | 146 raise NotImplementedError |
124 | 147 |
125 def _GetTestByName(self, test_name): | 148 def _GetTestByName(self, test_name): |
126 """Gets test_id for specific test. | 149 """Gets test_id for specific test. |
127 | 150 |
128 Args: | 151 Args: |
129 test_name: Test to find the ID of. | 152 test_name: Test to find the ID of. |
130 """ | 153 """ |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
163 | 186 |
164 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, | 187 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, |
165 logging.WARNING): | 188 logging.WARNING): |
166 test_check_res = appurify_sanitized.api.tests_check_result( | 189 test_check_res = appurify_sanitized.api.tests_check_result( |
167 self._env.token, test_run_id) | 190 self._env.token, test_run_id) |
168 remote_device_helper.TestHttpResponse(test_check_res, | 191 remote_device_helper.TestHttpResponse(test_check_res, |
169 'Unable to get test status.') | 192 'Unable to get test status.') |
170 self._results = test_check_res.json()['response'] | 193 self._results = test_check_res.json()['response'] |
171 return self._results['status'] | 194 return self._results['status'] |
172 | 195 |
173 def _AmInstrumentTestSetup(self, app_path, test_path, runner_package): | 196 def _AmInstrumentTestSetup(self, app_path, test_path, runner_package, |
| 197 environment_variables): |
174 config = {'runner': runner_package} | 198 config = {'runner': runner_package} |
| 199 if environment_variables: |
| 200 config['environment_vars'] = ','.join( |
| 201 '%s=%s' % (k, v) for k, v in environment_variables.iteritems()) |
175 | 202 |
176 self._app_id = self._UploadAppToDevice(app_path) | 203 self._app_id = self._UploadAppToDevice(app_path) |
177 | 204 |
178 data_deps = self._test_instance.GetDataDependencies() | 205 data_deps = self._test_instance.GetDataDependencies() |
179 if data_deps: | 206 if data_deps: |
180 with tempfile.NamedTemporaryFile(suffix='.zip') as test_with_deps: | 207 with tempfile.NamedTemporaryFile(suffix='.zip') as test_with_deps: |
181 sdcard_files = [] | 208 sdcard_files = [] |
182 host_test = os.path.basename(test_path) | 209 host_test = os.path.basename(test_path) |
183 with zipfile.ZipFile(test_with_deps.name, 'w') as zip_file: | 210 with zipfile.ZipFile(test_with_deps.name, 'w') as zip_file: |
184 zip_file.write(test_path, host_test, zipfile.ZIP_DEFLATED) | 211 zip_file.write(test_path, host_test, zipfile.ZIP_DEFLATED) |
185 for h, _ in data_deps: | 212 for h, _ in data_deps: |
186 zip_utils.WriteToZipFile(zip_file, h, '.') | |
187 if os.path.isdir(h): | 213 if os.path.isdir(h): |
| 214 zip_utils.WriteToZipFile(zip_file, h, '.') |
188 sdcard_files.extend(os.listdir(h)) | 215 sdcard_files.extend(os.listdir(h)) |
189 else: | 216 else: |
190 sdcard_files.extend(h) | 217 zip_utils.WriteToZipFile(zip_file, h, os.path.basename(h)) |
| 218 sdcard_files.append(os.path.basename(h)) |
191 config['sdcard_files'] = ','.join(sdcard_files) | 219 config['sdcard_files'] = ','.join(sdcard_files) |
192 config['host_test'] = host_test | 220 config['host_test'] = host_test |
193 self._test_id = self._UploadTestToDevice( | 221 self._test_id = self._UploadTestToDevice( |
194 'robotium', test_with_deps.name) | 222 'robotium', test_with_deps.name) |
195 else: | 223 else: |
196 self._test_id = self._UploadTestToDevice('robotium', test_path) | 224 self._test_id = self._UploadTestToDevice('robotium', test_path) |
197 | 225 |
198 logging.info('Setting config: %s' % config) | 226 logging.info('Setting config: %s' % config) |
199 self._SetTestConfig('robotium', config) | 227 self._SetTestConfig('robotium', config) |
200 | 228 |
(...skipping 24 matching lines...) Expand all Loading... |
225 'Unable to upload %s.' % test_path) | 253 'Unable to upload %s.' % test_path) |
226 return upload_results.json()['response']['test_id'] | 254 return upload_results.json()['response']['test_id'] |
227 | 255 |
228 def _SetTestConfig(self, runner_type, body): | 256 def _SetTestConfig(self, runner_type, body): |
229 """Generates and uploads config file for test. | 257 """Generates and uploads config file for test. |
230 Args: | 258 Args: |
231 extras: Extra arguments to set in the config file. | 259 extras: Extra arguments to set in the config file. |
232 """ | 260 """ |
233 logging.info('Generating config file for test.') | 261 logging.info('Generating config file for test.') |
234 with tempfile.TemporaryFile() as config: | 262 with tempfile.TemporaryFile() as config: |
235 config_data = ['[appurify]', '[%s]' % runner_type] | 263 config_data = [ |
| 264 '[appurify]', |
| 265 'pcap=0', |
| 266 'profiler=0', |
| 267 'videocapture=0', |
| 268 '[%s]' % runner_type |
| 269 ] |
236 config_data.extend('%s=%s' % (k, v) for k, v in body.iteritems()) | 270 config_data.extend('%s=%s' % (k, v) for k, v in body.iteritems()) |
237 config.write(''.join('%s\n' % l for l in config_data)) | 271 config.write(''.join('%s\n' % l for l in config_data)) |
238 config.flush() | 272 config.flush() |
239 config.seek(0) | 273 config.seek(0) |
240 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, | 274 with appurify_sanitized.SanitizeLogging(self._env.verbose_count, |
241 logging.WARNING): | 275 logging.WARNING): |
242 config_response = appurify_sanitized.api.config_upload( | 276 config_response = appurify_sanitized.api.config_upload( |
243 self._env.token, config, self._test_id) | 277 self._env.token, config, self._test_id) |
244 remote_device_helper.TestHttpResponse( | 278 remote_device_helper.TestHttpResponse( |
245 config_response, 'Unable to upload test config.') | 279 config_response, 'Unable to upload test config.') |
OLD | NEW |