#!/usr/bin/env python3 # # Copyright 2016 - The Android Open Source Project # # 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 re import signal import sys import traceback from builtins import str from mobly import config_parser as mobly_config_parser from acts import config_parser from acts import keys from acts import signals from acts import test_runner from acts import utils from acts.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("Exception when executing %s, iteration %s." % (runner.testbed_name, 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("Exception occurred when executing test bed %s. %s" % (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="", help="Path to the test configuration file.") parser.add_argument( '-ci', '--campaign_iterations', metavar="", 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="[ ...]", help="Specify which test beds to run tests on.") parser.add_argument('-lp', '--logpath', type=str, metavar="", help="Root path under which all logs will be placed.") parser.add_argument( '-tp', '--testpaths', nargs='*', type=str, metavar=" ", 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="", help=("Path to a file containing a comma delimited list of test " "classes to run.")) parser.add_argument('-ti', '--test_case_iterations', metavar="", 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 if re.search(r'\.ya?ml$', args.config): parsed_configs = mobly_config_parser.load_test_config_file( args.config, args.testbed) else: parsed_configs = config_parser.load_test_config_file( args.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] = utils.abs_path( utils.os.path.join(os.path.dirname(__file__), '../../../../acts_tests/tests/')) # 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("Required key %s missing in test config." % keys.Config.key_log_path.value) 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) 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()