blob: 678e3127dfe70bf9b0d815952a25a30ab33aa938 [file] [log] [blame]
#!/usr/bin/env python3.8
# Copyright 2022 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import sys
import subprocess
import pathlib
import os
import json
import unittest
from typing import List
# These are variables will be substituted with the real values at run time.
# We do not know where the test artifacts are stored until after `ffx test`
# completes. So we offer this place holder to substitute the values once they
# are evaluated.
VARIABLE_SUBSTITUTION = {"{test_artifact_dir}": ""}
def run_target_test(
ffx_bin: str, test_url: str, outdir: str,
ffx_test_args: List[str]) -> subprocess.CompletedProcess:
"""Runs 'ffx test run <url> --output-directory outdir [args]'"""
root_build_dir = os.getcwd()
# use the same configuration in //src/developer/ffx/build/ffx_action.gni
base_config = [
'analytics.disabled=true',
'assembly_enabled=true',
'sdk.root=' + root_build_dir,
'sdk.type=in-tree',
'sdk.module=host_tools.modular',
]
args = [ffx_bin]
for c in base_config:
args += ['--config', c]
args += [
'--env', './.ffx.env', 'test', 'run', test_url, '--output-directory',
outdir
]
args += ffx_test_args
return subprocess.run(args, stderr=sys.stdout)
def get_test_suite_artifact_dir(
output_root: pathlib.Path, test_suite_name: str):
"""Parses run_summary.json and reads back 'artifact_dir'.
Raises:
KeyError: if json keys for parsing test artifact directory does not exist.
Returns:
subdir path where test suite artifact path is stored.
"""
summary_json = os.path.join(output_root, 'run_summary.json')
with open(summary_json, 'r') as f:
test_summary = json.load(f)
test_summary = test_summary['data']
for suite in test_summary['suites']:
if test_suite_name in suite['name']:
return suite['artifact_dir']
return ""
def do_variable_substitution(input: str) -> str:
"""Substitute input with values defined in VARIABLE_SUBSTITUTION."""
for var in VARIABLE_SUBSTITUTION:
input = input.replace(var, VARIABLE_SUBSTITUTION[var])
return input
def run_host_script(
script_bin: str, script_args: List[str]) -> subprocess.CompletedProcess:
"""Runs the host script."""
substituted_args = []
for arg in script_args:
substituted_args.append(do_variable_substitution(arg))
return subprocess.run([script_bin] + substituted_args, stderr=sys.stdout)
class RunTargetTestWithHostScript(unittest.TestCase):
"""Executes the target test via `ffx test`, then executes the host script in tearDown."""
def __init__(self, args=None, test_output_dir=None):
super().__init__()
self.args = args
self.test_output_dir = test_output_dir
self.test_status = None
def runTest(self):
"""Runs test on target"""
self.test_status = run_target_test(
self.args.ffx_bin, self.args.test_url, self.test_output_dir,
self.args.ffx_test_args)
self.assertEqual(
self.test_status.returncode, 0,
"test returned non-zero status, error: %s" %
self.test_status.stderr)
def tearDown(self):
if self.args.host_script_bin and (self.test_status.returncode == 0 or
self.args.run_host_script_on_fail):
artifact_subdir = get_test_suite_artifact_dir(
self.test_output_dir, self.args.test_url)
VARIABLE_SUBSTITUTION['{test_artifact_dir}'] = os.path.join(
self.test_output_dir, artifact_subdir)
script_status = run_host_script(
self.args.host_script_bin, self.args.host_script_args)
self.assertEqual(
script_status.returncode, 0,
"host script returned non-zero status, error: %s" %
script_status.stderr)
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'--ffx-bin',
type=pathlib.Path,
required=True,
help='Path to ffx binary.')
parser.add_argument('--test-url', type=str, required=True, help='Test URL')
parser.add_argument(
'--test-outdir', type=pathlib.Path, help='Dir to store test outputs')
parser.add_argument(
'--ffx-test-args',
action='append',
default=[],
help='List of args to be forwarded to ffx test')
parser.add_argument(
'--host-script-bin',
type=pathlib.Path,
help='Path to host script, optional')
parser.add_argument(
'--host-script-args',
action='append',
type=str,
default=[],
help='List of args to be forwarded to host script')
parser.add_argument(
'--run-host-script-on-fail',
help='whether to run the host script if test fails')
args = parser.parse_args()
test_output_dir = os.getenv('FUCHSIA_TEST_OUTDIR')
if not test_output_dir:
test_output_dir = args.test_outdir
print('storing test output to: ', test_output_dir)
test = RunTargetTestWithHostScript(
args=args, test_output_dir=test_output_dir)
result = unittest.TextTestRunner().run(test)
sys.exit(not result.wasSuccessful())
if __name__ == '__main__':
main()