blob: e8642c9cc7d38966aedf0564cc637aa973c8b2db [file] [log] [blame]
Benny Peake9190a132016-11-02 12:57:01 -07001# Copyright 2016 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Benny Peake9190a132016-11-02 12:57:01 -07005import json
6import logging
7import os
8
9from autotest_lib.client.common_lib import error
10from autotest_lib.client.common_lib import global_config
Benny Peake9190a132016-11-02 12:57:01 -070011from autotest_lib.server import adb_utils
12from autotest_lib.server import constants
Benny Peake9190a132016-11-02 12:57:01 -070013from autotest_lib.server.hosts import adb_host
Benny Peake9190a132016-11-02 12:57:01 -070014
15DEFAULT_ACTS_INTERNAL_DIRECTORY = 'tools/test/connectivity/acts'
16
17CONFIG_FOLDER_LOCATION = global_config.global_config.get_config_value(
Joe Brennana0db27c2017-02-27 15:40:00 -080018 'ACTS', 'acts_config_folder', default='')
Benny Peake9190a132016-11-02 12:57:01 -070019
20TEST_DIR_NAME = 'tests'
21FRAMEWORK_DIR_NAME = 'framework'
22SETUP_FILE_NAME = 'setup.py'
23CONFIG_DIR_NAME = 'autotest_config'
24CAMPAIGN_DIR_NAME = 'autotest_campaign'
25LOG_DIR_NAME = 'logs'
26ACTS_EXECUTABLE_IN_FRAMEWORK = 'acts/bin/act.py'
27
28ACTS_TESTPATHS_ENV_KEY = 'ACTS_TESTPATHS'
29ACTS_LOGPATH_ENV_KEY = 'ACTS_LOGPATH'
30ACTS_PYTHONPATH_ENV_KEY = 'PYTHONPATH'
31
32
33def create_acts_package_from_current_artifact(test_station, job_repo_url,
34 target_zip_file):
35 """Creates an acts package from the build branch being used.
36
37 Creates an acts artifact from the build branch being used. This is
38 determined by the job_repo_url passed in.
39
40 @param test_station: The teststation that should be creating the package.
41 @param job_repo_url: The job_repo_url to get the build info from.
42 @param target_zip_file: The zip file to create form the artifact on the
43 test_station.
44
45 @returns An ActsPackage containing all the information about the zipped
46 artifact.
47 """
48 build_info = adb_host.ADBHost.get_build_info_from_build_url(job_repo_url)
49
50 return create_acts_package_from_artifact(
Joe Brennana0db27c2017-02-27 15:40:00 -080051 test_station, build_info['branch'], build_info['target'],
52 build_info['build_id'], job_repo_url, target_zip_file)
Benny Peake9190a132016-11-02 12:57:01 -070053
54
55def create_acts_package_from_artifact(test_station, branch, target, build_id,
Benny Peake285d3942017-06-26 14:32:53 -070056 devserver, target_zip_file):
Benny Peake9190a132016-11-02 12:57:01 -070057 """Creates an acts package from a specified branch.
58
59 Grabs the packaged acts artifact from the branch and places it on the
60 test_station.
61
62 @param test_station: The teststation that should be creating the package.
63 @param branch: The name of the branch where the artifact is to be pulled.
64 @param target: The name of the target where the artifact is to be pulled.
65 @param build_id: The build id to pull the artifact from.
Benny Peake285d3942017-06-26 14:32:53 -070066 @param devserver: The devserver to use.
Benny Peake9190a132016-11-02 12:57:01 -070067 @param target_zip_file: The zip file to create on the teststation.
68
69 @returns An ActsPackage containing all the information about the zipped
70 artifact.
71 """
Joe Brennana0db27c2017-02-27 15:40:00 -080072 devserver.trigger_download(
73 target, build_id, branch, files='acts.zip', synchronous=True)
Benny Peake9190a132016-11-02 12:57:01 -070074
Benny Peaked6ac8972017-02-22 23:09:01 -080075 pull_base_url = devserver.get_pull_url(target, build_id, branch)
76 download_ulr = os.path.join(pull_base_url, 'acts.zip')
Benny Peake9190a132016-11-02 12:57:01 -070077
78 test_station.download_file(download_ulr, target_zip_file)
79
80 return ActsPackage(test_station, target_zip_file)
81
82
83def create_acts_package_from_zip(test_station, zip_location, target_zip_file):
84 """Creates an acts package from an existing zip.
85
86 Creates an acts package from a zip file that already sits on the drone.
87
88 @param test_station: The teststation to create the package on.
89 @param zip_location: The location of the zip on the drone.
90 @param target_zip_file: The zip file to create on the teststaiton.
91
92 @returns An ActsPackage containing all the information about the zipped
93 artifact.
94 """
95 if not os.path.isabs(zip_location):
96 zip_location = os.path.join(CONFIG_FOLDER_LOCATION, 'acts_artifacts',
97 zip_location)
98
Benny Peake33fb5ff2017-03-22 18:12:11 -070099 test_station.send_file(zip_location, target_zip_file)
Benny Peake9190a132016-11-02 12:57:01 -0700100
Benny Peake33fb5ff2017-03-22 18:12:11 -0700101 return ActsPackage(test_station, target_zip_file)
Benny Peake9190a132016-11-02 12:57:01 -0700102
103
104class ActsPackage(object):
105 """A packaged version of acts on a teststation."""
Joe Brennana0db27c2017-02-27 15:40:00 -0800106
Benny Peake9190a132016-11-02 12:57:01 -0700107 def __init__(self, test_station, zip_file_path):
108 """
109 @param test_station: The teststation this package is on.
110 @param zip_file_path: The path to the zip file on the test station that
111 holds the package on the teststation.
112 """
113 self.test_station = test_station
114 self.zip_file = zip_file_path
115
116 def create_container(self,
117 container_directory,
118 internal_acts_directory=None):
119 """Unpacks this package into a container.
120
Benny Peake9519ea82016-12-02 17:23:38 -0800121 Unpacks this acts package into a container to interact with acts.
Benny Peake9190a132016-11-02 12:57:01 -0700122
123 @param container_directory: The directory on the teststation to hold
124 the container.
125 @param internal_acts_directory: The directory inside of the package
126 that holds acts.
127
128 @returns: An ActsContainer with info on the unpacked acts container.
129 """
Benny Peake9519ea82016-12-02 17:23:38 -0800130 self.test_station.run('unzip "%s" -x -d "%s"' %
131 (self.zip_file, container_directory))
132
Joe Brennana0db27c2017-02-27 15:40:00 -0800133 return ActsContainer(
134 self.test_station,
135 container_directory,
136 acts_directory=internal_acts_directory)
Benny Peake9519ea82016-12-02 17:23:38 -0800137
Benny Peakefda1d8c2017-06-02 16:38:31 -0700138 def create_environment(self,
139 container_directory,
140 devices,
141 testbed_name,
142 internal_acts_directory=None):
Benny Peake9519ea82016-12-02 17:23:38 -0800143 """Unpacks this package into an acts testing enviroment.
144
145 Unpacks this acts package into a test enviroment to test with acts.
146
147 @param container_directory: The directory on the teststation to hold
148 the test enviroment.
Benny Peakefda1d8c2017-06-02 16:38:31 -0700149 @param devices: The list of devices in the environment.
150 @param testbed_name: The name of the testbed.
Benny Peake9519ea82016-12-02 17:23:38 -0800151 @param internal_acts_directory: The directory inside of the package
152 that holds acts.
153
Benny Peakefda1d8c2017-06-02 16:38:31 -0700154 @returns: An ActsTestingEnvironment with info on the unpacked
155 acts testing environment.
Benny Peake9519ea82016-12-02 17:23:38 -0800156 """
Benny Peakefda1d8c2017-06-02 16:38:31 -0700157 container = self.create_container(container_directory,
158 internal_acts_directory)
Benny Peake9190a132016-11-02 12:57:01 -0700159
Joe Brennana0db27c2017-02-27 15:40:00 -0800160 return ActsTestingEnviroment(
Benny Peakefda1d8c2017-06-02 16:38:31 -0700161 devices=devices,
162 container=container,
163 testbed_name=testbed_name)
Benny Peake9190a132016-11-02 12:57:01 -0700164
165
Benny Peakefda1d8c2017-06-02 16:38:31 -0700166class AndroidTestingEnvironment(object):
Benny Peake591cff42016-11-21 16:17:29 -0800167 """A container for testing android devices on a test station."""
Joe Brennana0db27c2017-02-27 15:40:00 -0800168
Benny Peakefda1d8c2017-06-02 16:38:31 -0700169 def __init__(self, devices, testbed_name):
170 """Creates a new android testing environment.
Benny Peake591cff42016-11-21 16:17:29 -0800171
Benny Peakefda1d8c2017-06-02 16:38:31 -0700172 @param devices: The devices on the testbed to use.
173 @param testbed_name: The name for the testbed.
Benny Peake9190a132016-11-02 12:57:01 -0700174 """
Benny Peakefda1d8c2017-06-02 16:38:31 -0700175 self.devices = devices
Benny Peake591cff42016-11-21 16:17:29 -0800176 self.testbed_name = testbed_name
177
Benny Peakeccac2d12017-03-23 13:20:05 -0700178 def install_sl4a_apk(self, force_reinstall=True):
Benny Peakefda1d8c2017-06-02 16:38:31 -0700179 """Install sl4a to all provided devices..
Benny Peakeccac2d12017-03-23 13:20:05 -0700180
181 @param force_reinstall: If true the apk will be force to reinstall.
182 """
Benny Peakefda1d8c2017-06-02 16:38:31 -0700183 for device in self.devices:
Benny Peake591cff42016-11-21 16:17:29 -0800184 adb_utils.install_apk_from_build(
Benny Peakefda1d8c2017-06-02 16:38:31 -0700185 device,
Joe Brennana0db27c2017-02-27 15:40:00 -0800186 constants.SL4A_APK,
187 constants.SL4A_ARTIFACT,
Benny Peakeccac2d12017-03-23 13:20:05 -0700188 package_name=constants.SL4A_PACKAGE,
189 force_reinstall=force_reinstall)
Benny Peake591cff42016-11-21 16:17:29 -0800190
Benny Peakeccac2d12017-03-23 13:20:05 -0700191 def install_apk(self, apk_info, force_reinstall=True):
Benny Peake591cff42016-11-21 16:17:29 -0800192 """Installs an additional apk on all adb devices.
Benny Peake2573fae2016-11-28 15:15:52 -0800193
Benny Peakefda1d8c2017-06-02 16:38:31 -0700194 @param apk_info: A dictionary containing the apk info. This dictionary
Benny Peake2573fae2016-11-28 15:15:52 -0800195 should contain the keys:
196 apk="Name of the apk",
197 package="Name of the package".
198 artifact="Name of the artifact", if missing
199 the package name is used."
Benny Peakeccac2d12017-03-23 13:20:05 -0700200 @param force_reinstall: If true the apk will be forced to reinstall.
Benny Peake591cff42016-11-21 16:17:29 -0800201 """
Benny Peakefda1d8c2017-06-02 16:38:31 -0700202 for device in self.devices:
Benny Peake591cff42016-11-21 16:17:29 -0800203 adb_utils.install_apk_from_build(
Benny Peakefda1d8c2017-06-02 16:38:31 -0700204 device,
Joe Brennana0db27c2017-02-27 15:40:00 -0800205 apk_info['apk'],
206 apk_info.get('artifact') or constants.SL4A_ARTIFACT,
Benny Peakeccac2d12017-03-23 13:20:05 -0700207 package_name=apk_info['package'],
208 force_reinstall=force_reinstall)
Benny Peake591cff42016-11-21 16:17:29 -0800209
210
Benny Peake9519ea82016-12-02 17:23:38 -0800211class ActsContainer(object):
212 """A container for working with acts."""
Joe Brennana0db27c2017-02-27 15:40:00 -0800213
214 def __init__(self, test_station, container_directory, acts_directory=None):
Benny Peake591cff42016-11-21 16:17:29 -0800215 """
Benny Peake9519ea82016-12-02 17:23:38 -0800216 @param test_station: The test station that the container is on.
Benny Peake9190a132016-11-02 12:57:01 -0700217 @param container_directory: The directory on the teststation this
218 container operates out of.
219 @param acts_directory: The directory within the container that holds
220 acts. If none then it defaults to
221 DEFAULT_ACTS_INTERNAL_DIRECTORY.
222 """
Benny Peake9519ea82016-12-02 17:23:38 -0800223 self.test_station = test_station
224 self.container_directory = container_directory
Benny Peake9190a132016-11-02 12:57:01 -0700225
226 if not acts_directory:
227 acts_directory = DEFAULT_ACTS_INTERNAL_DIRECTORY
228
229 if not os.path.isabs(acts_directory):
230 self.acts_directory = os.path.join(container_directory,
231 acts_directory)
232 else:
233 self.acts_directory = acts_directory
234
Benny Peake9190a132016-11-02 12:57:01 -0700235 self.tests_directory = os.path.join(self.acts_directory, TEST_DIR_NAME)
236 self.framework_directory = os.path.join(self.acts_directory,
237 FRAMEWORK_DIR_NAME)
Benny Peake9190a132016-11-02 12:57:01 -0700238
239 self.acts_file = os.path.join(self.framework_directory,
240 ACTS_EXECUTABLE_IN_FRAMEWORK)
241
242 self.setup_file = os.path.join(self.framework_directory,
243 SETUP_FILE_NAME)
244
Benny Peakefda1d8c2017-06-02 16:38:31 -0700245 self.log_directory = os.path.join(container_directory,
246 LOG_DIR_NAME)
247
248 self.config_location = os.path.join(container_directory,
249 CONFIG_DIR_NAME)
250
251 self.acts_file = os.path.join(self.framework_directory,
252 ACTS_EXECUTABLE_IN_FRAMEWORK)
253
254 self.working_directory = os.path.join(container_directory,
255 CONFIG_DIR_NAME)
256 test_station.run('mkdir %s' % self.working_directory,
257 ignore_status=True)
258
Benny Peake9190a132016-11-02 12:57:01 -0700259 def get_test_paths(self):
260 """Get all test paths within this container.
261
262 Gets all paths that hold tests within the container.
263
264 @returns: A list of paths on the teststation that hold tests.
265 """
266 get_test_paths_result = self.test_station.run('find %s -type d' %
267 self.tests_directory)
268 test_search_dirs = get_test_paths_result.stdout.splitlines()
269 return test_search_dirs
270
271 def get_python_path(self):
272 """Get the python path being used.
273
274 Gets the python path that will be set in the enviroment for this
275 container.
276
277 @returns: A string of the PYTHONPATH enviroment variable to be used.
278 """
279 return '%s:$PYTHONPATH' % self.framework_directory
280
281 def get_enviroment(self):
282 """Gets the enviroment variables to be used for this container.
283
284 @returns: A dictionary of enviroment variables to be used by this
285 container.
286 """
Joe Brennana0db27c2017-02-27 15:40:00 -0800287 env = {
288 ACTS_TESTPATHS_ENV_KEY: ':'.join(self.get_test_paths()),
289 ACTS_LOGPATH_ENV_KEY: self.log_directory,
290 ACTS_PYTHONPATH_ENV_KEY: self.get_python_path()
291 }
Benny Peake9190a132016-11-02 12:57:01 -0700292
293 return env
294
295 def upload_file(self, src, dst):
296 """Uploads a file to be used by the container.
297
298 Uploads a file from the drone to the test staiton to be used by the
299 test container.
300
301 @param src: The source file on the drone. If a relative path is given
302 it is assumed to exist in CONFIG_FOLDER_LOCATION.
303 @param dst: The destination on the teststation. If a relative path is
304 given it is assumed that it is within the container.
305
306 @returns: The full path on the teststation.
307 """
308 if not os.path.isabs(src):
309 src = os.path.join(CONFIG_FOLDER_LOCATION, src)
310
311 if not os.path.isabs(dst):
312 dst = os.path.join(self.container_directory, dst)
313
Benny Peakef1b9f392016-11-30 18:25:38 -0800314 path = os.path.dirname(dst)
Benny Peakefda1d8c2017-06-02 16:38:31 -0700315 self.test_station.run('mkdir "%s"' % path, ignore_status=True)
Benny Peakef1b9f392016-11-30 18:25:38 -0800316
317 original_dst = dst
318 if os.path.basename(src) == os.path.basename(dst):
319 dst = os.path.dirname(dst)
Benny Peake9190a132016-11-02 12:57:01 -0700320
321 self.test_station.send_file(src, dst)
322
Benny Peakef1b9f392016-11-30 18:25:38 -0800323 return original_dst
Benny Peake9190a132016-11-02 12:57:01 -0700324
Benny Peake9519ea82016-12-02 17:23:38 -0800325
Benny Peakefda1d8c2017-06-02 16:38:31 -0700326class ActsTestingEnviroment(AndroidTestingEnvironment):
Benny Peake9519ea82016-12-02 17:23:38 -0800327 """A container for running acts tests with a contained version of acts."""
Joe Brennana0db27c2017-02-27 15:40:00 -0800328
Benny Peakefda1d8c2017-06-02 16:38:31 -0700329 def __init__(self, container, devices, testbed_name):
Benny Peake9519ea82016-12-02 17:23:38 -0800330 """
Benny Peakefda1d8c2017-06-02 16:38:31 -0700331 @param container: The acts container to use.
332 @param devices: The list of devices to use.
333 @testbed_name: The name of the testbed being used.
Benny Peake9519ea82016-12-02 17:23:38 -0800334 """
Benny Peakefda1d8c2017-06-02 16:38:31 -0700335 super(ActsTestingEnviroment, self).__init__(devices=devices,
336 testbed_name=testbed_name)
Benny Peake9519ea82016-12-02 17:23:38 -0800337
Benny Peakefda1d8c2017-06-02 16:38:31 -0700338 self.container = container
Benny Peake9519ea82016-12-02 17:23:38 -0800339
340 self.configs = {}
341 self.campaigns = {}
342
Benny Peake9190a132016-11-02 12:57:01 -0700343 def upload_config(self, config_file):
344 """Uploads a config file to the container.
345
346 Uploads a config file to the config folder in the container.
347
348 @param config_file: The config file to upload. This must be a file
349 within the autotest_config directory under the
350 CONFIG_FOLDER_LOCATION.
351
352 @returns: The full path of the config on the test staiton.
353 """
354 full_name = os.path.join(CONFIG_DIR_NAME, config_file)
355
Benny Peakefda1d8c2017-06-02 16:38:31 -0700356 full_path = self.container.upload_file(full_name, full_name)
Benny Peake9190a132016-11-02 12:57:01 -0700357 self.configs[config_file] = full_path
358
359 return full_path
360
361 def upload_campaign(self, campaign_file):
362 """Uploads a campaign file to the container.
363
364 Uploads a campaign file to the campaign folder in the container.
365
366 @param campaign_file: The campaign file to upload. This must be a file
367 within the autotest_campaign directory under the
368 CONFIG_FOLDER_LOCATION.
369
370 @returns: The full path of the campaign on the test staiton.
371 """
372 full_name = os.path.join(CAMPAIGN_DIR_NAME, campaign_file)
373
Benny Peakefda1d8c2017-06-02 16:38:31 -0700374 full_path = self.container.upload_file(full_name, full_name)
Benny Peake9190a132016-11-02 12:57:01 -0700375 self.campaigns[campaign_file] = full_path
376
377 return full_path
378
Benny Peakefda1d8c2017-06-02 16:38:31 -0700379 def setup_enviroment(self, python_bin='python'):
380 """Sets up the teststation system enviroment so the container can run.
381
382 Prepares the remote system so that the container can run. This involves
383 uninstalling all versions of acts for the version of python being
384 used and installing all needed dependencies.
385
386 @param python_bin: The python binary to use.
387 """
388 uninstall_command = '%s %s uninstall' % (
389 python_bin, self.container.setup_file)
390 install_deps_command = '%s %s install_deps' % (
391 python_bin, self.container.setup_file)
392
393 self.container.test_station.run(uninstall_command)
394 self.container.test_station.run(install_deps_command)
395
Benny Peake9190a132016-11-02 12:57:01 -0700396 def run_test(self,
Benny Peake9190a132016-11-02 12:57:01 -0700397 config,
398 campaign=None,
399 test_case=None,
400 extra_env={},
401 python_bin='python',
Joe Brennana0db27c2017-02-27 15:40:00 -0800402 timeout=7200,
403 additional_cmd_line_params=None):
Benny Peake9190a132016-11-02 12:57:01 -0700404 """Runs a test within the container.
405
406 Runs a test within a container using the given settings.
407
Benny Peake9190a132016-11-02 12:57:01 -0700408 @param config: The name of the config file to use as the main config.
409 This should have already been uploaded with
410 upload_config. The string passed into upload_config
411 should be used here.
412 @param campaign: The campaign file to use for this test. If none then
413 test_case is assumed. This file should have already
414 been uploaded with upload_campaign. The string passed
415 into upload_campaign should be used here.
416 @param test_case: The test case to run the test with. If none then the
Benny Peake913791f2016-12-08 11:00:56 -0800417 campaign will be used. If multiple are given,
418 multiple will be run.
Benny Peake9190a132016-11-02 12:57:01 -0700419 @param extra_env: Extra enviroment variables to run the test with.
420 @param python_bin: The python binary to execute the test with.
Benny Peake9190a132016-11-02 12:57:01 -0700421 @param timeout: How many seconds to wait before timing out.
Joe Brennana0db27c2017-02-27 15:40:00 -0800422 @param additional_cmd_line_params: Adds the ability to add any string
423 to the end of the acts.py command
424 line string. This is intended to
425 add acts command line flags however
426 this is unbounded so it could cause
427 errors if incorrectly set.
Benny Peake9190a132016-11-02 12:57:01 -0700428
429 @returns: The results of the test run.
430 """
Benny Peake9190a132016-11-02 12:57:01 -0700431 if not config in self.configs:
432 # Check if the config has been uploaded and upload if it hasn't
433 self.upload_config(config)
434
435 full_config = self.configs[config]
436
437 if campaign:
438 # When given a campaign check if it's upload.
439 if not campaign in self.campaigns:
440 self.upload_campaign(campaign)
441
442 full_campaign = self.campaigns[campaign]
443 else:
444 full_campaign = None
445
Benny Peakefda1d8c2017-06-02 16:38:31 -0700446 full_env = self.container.get_enviroment()
Benny Peake9190a132016-11-02 12:57:01 -0700447
Benny Peakefda1d8c2017-06-02 16:38:31 -0700448 # Setup environment variables.
Benny Peake9190a132016-11-02 12:57:01 -0700449 if extra_env:
450 for k, v in extra_env.items():
451 full_env[k] = extra_env
452
453 logging.info('Using env: %s', full_env)
454 exports = ('export %s=%s' % (k, v) for k, v in full_env.items())
455 env_command = ';'.join(exports)
456
457 # Make sure to execute in the working directory.
Benny Peakefda1d8c2017-06-02 16:38:31 -0700458 command_setup = 'cd %s' % self.container.working_directory
Benny Peake9190a132016-11-02 12:57:01 -0700459
Joe Brennana0db27c2017-02-27 15:40:00 -0800460 if additional_cmd_line_params:
461 act_base_cmd = '%s %s -c %s -tb %s %s ' % (
Benny Peakefda1d8c2017-06-02 16:38:31 -0700462 python_bin, self.container.acts_file, full_config,
463 self.testbed_name, additional_cmd_line_params)
Joe Brennana0db27c2017-02-27 15:40:00 -0800464 else:
465 act_base_cmd = '%s %s -c %s -tb %s ' % (
Benny Peakefda1d8c2017-06-02 16:38:31 -0700466 python_bin, self.container.acts_file, full_config,
467 self.testbed_name)
Benny Peake9190a132016-11-02 12:57:01 -0700468
469 # Format the acts command based on what type of test is being run.
470 if test_case and campaign:
471 raise error.TestError(
Benny Peakefda1d8c2017-06-02 16:38:31 -0700472 'campaign and test_file cannot both have a value.')
Benny Peake9190a132016-11-02 12:57:01 -0700473 elif test_case:
Benny Peake913791f2016-12-08 11:00:56 -0800474 if isinstance(test_case, str):
475 test_case = [test_case]
476 if len(test_case) < 1:
477 raise error.TestError('At least one test case must be given.')
478
479 tc_str = ''
480 for tc in test_case:
481 tc_str = '%s %s' % (tc_str, tc)
482 tc_str = tc_str.strip()
483
484 act_cmd = '%s -tc %s' % (act_base_cmd, tc_str)
Benny Peake9190a132016-11-02 12:57:01 -0700485 elif campaign:
486 act_cmd = '%s -tf %s' % (act_base_cmd, full_campaign)
487 else:
488 raise error.TestFail('No tests was specified!')
489
490 # Format all commands into a single command.
491 command_list = [command_setup, env_command, act_cmd]
492 full_command = '; '.join(command_list)
493
494 try:
495 # Run acts on the remote machine.
Benny Peakefda1d8c2017-06-02 16:38:31 -0700496 act_result = self.container.test_station.run(full_command,
497 timeout=timeout)
Benny Peake9190a132016-11-02 12:57:01 -0700498 excep = None
499 except Exception as e:
500 # Catch any error to store in the results.
501 act_result = None
502 excep = e
503
Benny Peakefda1d8c2017-06-02 16:38:31 -0700504 return ActsTestResults(str(test_case) or campaign,
505 container=self.container,
506 devices=self.devices,
507 testbed_name=self.testbed_name,
508 run_result=act_result,
509 exception=excep)
Benny Peake9190a132016-11-02 12:57:01 -0700510
511
512class ActsTestResults(object):
513 """The packaged results of a test run."""
514 acts_result_to_autotest = {
515 'PASS': 'GOOD',
516 'FAIL': 'FAIL',
517 'UNKNOWN': 'WARN',
518 'SKIP': 'ABORT'
519 }
520
521 def __init__(self,
522 name,
Benny Peakefda1d8c2017-06-02 16:38:31 -0700523 container,
524 devices,
525 testbed_name,
Benny Peake9190a132016-11-02 12:57:01 -0700526 run_result=None,
Benny Peake9190a132016-11-02 12:57:01 -0700527 exception=None):
528 """
529 @param name: A name to identify the test run.
Benny Peake9190a132016-11-02 12:57:01 -0700530 @param testbed_name: The name the testbed was run with, if none the
531 default name of the testbed is used.
532 @param run_result: The raw i/o result of the test run.
Benny Peake51c675b2016-11-15 15:55:39 -0800533 @param log_directory: The directory that acts logged to.
Benny Peake9190a132016-11-02 12:57:01 -0700534 @param exception: An exception that was thrown while running the test.
535 """
536 self.name = name
537 self.run_result = run_result
Benny Peake9190a132016-11-02 12:57:01 -0700538 self.exception = exception
Benny Peakefda1d8c2017-06-02 16:38:31 -0700539 self.log_directory = container.log_directory
540 self.test_station = container.test_station
541 self.testbed_name = testbed_name
542 self.devices = devices
Benny Peake9190a132016-11-02 12:57:01 -0700543
544 self.reported_to = set()
545
Benny Peake51c675b2016-11-15 15:55:39 -0800546 self.json_results = {}
547 self.results_dir = None
548 if self.log_directory:
549 self.results_dir = os.path.join(self.log_directory,
550 self.testbed_name, 'latest')
551 results_file = os.path.join(self.results_dir,
552 'test_run_summary.json')
553 cat_log_result = self.test_station.run('cat %s' % results_file,
554 ignore_status=True)
555 if not cat_log_result.exit_status:
556 self.json_results = json.loads(cat_log_result.stdout)
557
Benny Peake9190a132016-11-02 12:57:01 -0700558 def log_output(self):
559 """Logs the output of the test."""
560 if self.run_result:
561 logging.debug('ACTS Output:\n%s', self.run_result.stdout)
562
Benny Peake83904b12017-03-09 14:08:07 -0800563 def save_test_info(self, test):
564 """Save info about the test.
565
566 @param test: The test to save.
567 """
Benny Peakefda1d8c2017-06-02 16:38:31 -0700568 for device in self.devices:
569 device.save_info(test.resultsdir)
Benny Peake83904b12017-03-09 14:08:07 -0800570
Benny Peake9190a132016-11-02 12:57:01 -0700571 def rethrow_exception(self):
572 """Re-throws the exception thrown during the test."""
573 if self.exception:
574 raise self.exception
575
Benny Peake51c675b2016-11-15 15:55:39 -0800576 def upload_to_local(self, local_dir):
577 """Saves all acts results to a local directory.
Benny Peake9190a132016-11-02 12:57:01 -0700578
Benny Peake51c675b2016-11-15 15:55:39 -0800579 @param local_dir: The directory on the local machine to save all results
580 to.
Benny Peake9190a132016-11-02 12:57:01 -0700581 """
Benny Peake51c675b2016-11-15 15:55:39 -0800582 if self.results_dir:
583 self.test_station.get_file(self.results_dir, local_dir)
Benny Peake9190a132016-11-02 12:57:01 -0700584
Benny Peake9190a132016-11-02 12:57:01 -0700585 def report_to_autotest(self, test):
586 """Reports the results to an autotest test object.
587
Benny Peake51c675b2016-11-15 15:55:39 -0800588 Reports the results to the test and saves all acts results under the
589 tests results directory.
590
Benny Peake9190a132016-11-02 12:57:01 -0700591 @param test: The autotest test object to report to. If this test object
592 has already recived our report then this call will be
593 ignored.
594 """
595 if test in self.reported_to:
596 return
597
Benny Peake51c675b2016-11-15 15:55:39 -0800598 if self.results_dir:
599 self.upload_to_local(test.resultsdir)
600
Benny Peake9190a132016-11-02 12:57:01 -0700601 if not 'Results' in self.json_results:
602 return
603
604 results = self.json_results['Results']
605 for result in results:
606 verdict = self.acts_result_to_autotest[result['Result']]
607 details = result['Details']
608 test.job.record(verdict, None, self.name, status=(details or ''))
609
610 self.reported_to.add(test)