blob: 2726d0c7bd3c51e317d7e46b9bb383aa309eca22 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2022 The Fuchsia Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import os
import signal
import sys
import traceback
from mobly import config_parser as mobly_config_parser
from antlion import config_parser, keys, signals, test_runner, utils
from antlion.config_parser import ActsConfigError
def _run_test(parsed_config, test_identifiers, repeat=1):
"""Instantiate and runs test_runner.TestRunner.
This is the function to start separate processes with.
Args:
parsed_config: A mobly.config_parser.TestRunConfig that is a set of
configs for one test_runner.TestRunner.
test_identifiers: A list of tuples, each identifies what test case to
run on what test class.
repeat: Number of times to iterate the specified tests.
Returns:
True if all tests passed without any error, False otherwise.
"""
runner = _create_test_runner(parsed_config, test_identifiers)
try:
for i in range(repeat):
runner.run()
return runner.results.is_all_pass
except signals.TestAbortAll:
return True
except:
print(f"Exception when executing {runner.testbed_name}, iteration {i}.")
print(traceback.format_exc())
finally:
runner.stop()
def _create_test_runner(parsed_config, test_identifiers):
"""Instantiates one test_runner.TestRunner object and register termination
signal handlers that properly shut down the test_runner.TestRunner run.
Args:
parsed_config: A mobly.config_parser.TestRunConfig that is a set of
configs for one test_runner.TestRunner.
test_identifiers: A list of tuples, each identifies what test case to
run on what test class.
Returns:
A test_runner.TestRunner object.
"""
try:
t = test_runner.TestRunner(parsed_config, test_identifiers)
except:
print("Failed to instantiate test runner, abort.")
print(traceback.format_exc())
sys.exit(1)
# Register handler for termination signals.
handler = config_parser.gen_term_signal_handler([t])
signal.signal(signal.SIGTERM, handler)
signal.signal(signal.SIGINT, handler)
return t
def _run_tests(parsed_configs, test_identifiers, repeat):
"""Executes requested tests sequentially.
Requested test runs will commence one after another according to the order
of their corresponding configs.
Args:
parsed_configs: A list of mobly.config_parser.TestRunConfig, each is a
set of configs for one test_runner.TestRunner.
test_identifiers: A list of tuples, each identifies what test case to
run on what test class.
repeat: Number of times to iterate the specified tests.
Returns:
True if all test runs executed successfully, False otherwise.
"""
ok = True
for c in parsed_configs:
try:
ret = _run_test(c, test_identifiers, repeat)
ok = ok and ret
except Exception as e:
print(f"Exception occurred when executing test bed {c.testbed_name}. {e}")
return ok
def main():
"""This is the default implementation of a cli entry point for ACTS test
execution.
Or you could implement your own cli entry point using acts.config_parser
functions and acts.test_runner.execute_one_test_class.
"""
parser = argparse.ArgumentParser(
description=(
"Specify tests to run. If nothing specified, " "run all test cases found."
)
)
parser.add_argument(
"-c",
"--config",
type=str,
required=True,
metavar="<PATH>",
help="Path to the test configuration file.",
)
parser.add_argument(
"-ci",
"--campaign_iterations",
metavar="<CAMPAIGN_ITERATIONS>",
nargs="?",
type=int,
const=1,
default=1,
help="Number of times to run the campaign or a group of test cases.",
)
parser.add_argument(
"-tb",
"--testbed",
nargs="+",
type=str,
metavar="[<TEST BED NAME1> <TEST BED NAME2> ...]",
help="Specify which test beds to run tests on.",
)
parser.add_argument(
"-lp",
"--logpath",
type=str,
metavar="<PATH>",
help="Root path under which all logs will be placed.",
)
parser.add_argument(
"-tp",
"--testpaths",
nargs="*",
type=str,
metavar="<PATH> <PATH>",
help="One or more non-recursive test class search paths.",
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
"-tc",
"--testclass",
nargs="+",
type=str,
metavar="[TestClass1 TestClass2:test_xxx ...]",
help="A list of test classes/cases to run.",
)
group.add_argument(
"-tf",
"--testfile",
nargs=1,
type=str,
metavar="<PATH>",
help=(
"Path to a file containing a comma delimited list of test "
"classes to run."
),
)
parser.add_argument(
"-ti",
"--test_case_iterations",
metavar="<TEST_CASE_ITERATIONS>",
nargs="?",
type=int,
help="Number of times to run every test case.",
)
args = parser.parse_args(sys.argv[1:])
test_list = None
if args.testfile:
test_list = config_parser.parse_test_file(args.testfile[0])
elif args.testclass:
test_list = args.testclass
config = args.config
if config.endswith(".json"):
print(
"DEPRECATION NOTICE: Converting ACTS JSON to Mobly YAML. ACTS is "
+ "deprecated. Support will be removed in the next release."
)
config = utils.acts_json_to_mobly_yaml(config)
print(f"Wrote YAML config to {config}")
parsed_configs = mobly_config_parser.load_test_config_file(config, args.testbed)
for test_run_config in parsed_configs:
if args.testpaths:
tp_key = keys.Config.key_test_paths.value
test_run_config.controller_configs[tp_key] = args.testpaths
if args.logpath:
test_run_config.log_path = args.logpath
if args.test_case_iterations:
ti_key = keys.Config.key_test_case_iterations.value
test_run_config.user_params[ti_key] = args.test_case_iterations
# Sets the --testpaths flag to the default test directory if left unset.
testpath_key = keys.Config.key_test_paths.value
if (
testpath_key not in test_run_config.controller_configs
or test_run_config.controller_configs[testpath_key] is None
):
test_run_config.controller_configs[testpath_key] = [
os.path.join(os.path.dirname(__file__), "../tests/"),
]
for path in test_run_config.controller_configs[testpath_key]:
path = utils.abs_path(path)
# TODO(markdr): Find a way to merge this with the validation done in
# Mobly's load_test_config_file.
if not test_run_config.log_path:
raise ActsConfigError(
f"Required key {keys.Config.key_log_path.value} missing in test config."
)
test_run_config.log_path = utils.abs_path(test_run_config.log_path)
# Prepare args for test runs
test_identifiers = config_parser.parse_test_list(test_list)
print(
"\n\nDEPRECATION NOTICE: Running antlion tests with act.py is "
"deprecated and will be removed in the next release. Please migrate "
"by using Mobly YAML configs and executing the test class directly:\n\n"
)
for test_class, _ in test_identifiers:
print(f" python {test_class}.py -c {config}")
print("\n")
exec_result = _run_tests(parsed_configs, test_identifiers, args.campaign_iterations)
if exec_result is False:
# return 1 upon test failure.
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
main()