• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3#   Copyright 2016 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import argparse
18import os
19import re
20import signal
21import sys
22import traceback
23from builtins import str
24
25from mobly import config_parser as mobly_config_parser
26
27from acts import config_parser
28from acts import keys
29from acts import signals
30from acts import test_runner
31from acts import utils
32from acts.config_parser import ActsConfigError
33
34
35def _run_test(parsed_config, test_identifiers, repeat=1):
36    """Instantiate and runs test_runner.TestRunner.
37
38    This is the function to start separate processes with.
39
40    Args:
41        parsed_config: A mobly.config_parser.TestRunConfig that is a set of
42                       configs for one test_runner.TestRunner.
43        test_identifiers: A list of tuples, each identifies what test case to
44                          run on what test class.
45        repeat: Number of times to iterate the specified tests.
46
47    Returns:
48        True if all tests passed without any error, False otherwise.
49    """
50    runner = _create_test_runner(parsed_config, test_identifiers)
51    try:
52        for i in range(repeat):
53            runner.run()
54        return runner.results.is_all_pass
55    except signals.TestAbortAll:
56        return True
57    except:
58        print("Exception when executing %s, iteration %s." %
59              (runner.testbed_name, i))
60        print(traceback.format_exc())
61    finally:
62        runner.stop()
63
64
65def _create_test_runner(parsed_config, test_identifiers):
66    """Instantiates one test_runner.TestRunner object and register termination
67    signal handlers that properly shut down the test_runner.TestRunner run.
68
69    Args:
70        parsed_config: A mobly.config_parser.TestRunConfig that is a set of
71                       configs for one test_runner.TestRunner.
72        test_identifiers: A list of tuples, each identifies what test case to
73                          run on what test class.
74
75    Returns:
76        A test_runner.TestRunner object.
77    """
78    try:
79        t = test_runner.TestRunner(parsed_config, test_identifiers)
80    except:
81        print("Failed to instantiate test runner, abort.")
82        print(traceback.format_exc())
83        sys.exit(1)
84    # Register handler for termination signals.
85    handler = config_parser.gen_term_signal_handler([t])
86    signal.signal(signal.SIGTERM, handler)
87    signal.signal(signal.SIGINT, handler)
88    return t
89
90
91def _run_tests(parsed_configs, test_identifiers, repeat):
92    """Executes requested tests sequentially.
93
94    Requested test runs will commence one after another according to the order
95    of their corresponding configs.
96
97    Args:
98        parsed_configs: A list of mobly.config_parser.TestRunConfig, each is a
99                        set of configs for one test_runner.TestRunner.
100        test_identifiers: A list of tuples, each identifies what test case to
101                          run on what test class.
102        repeat: Number of times to iterate the specified tests.
103
104    Returns:
105        True if all test runs executed successfully, False otherwise.
106    """
107    ok = True
108    for c in parsed_configs:
109        try:
110            ret = _run_test(c, test_identifiers, repeat)
111            ok = ok and ret
112        except Exception as e:
113            print("Exception occurred when executing test bed %s. %s" %
114                  (c.testbed_name, e))
115    return ok
116
117
118def main():
119    """This is the default implementation of a cli entry point for ACTS test
120    execution.
121
122    Or you could implement your own cli entry point using acts.config_parser
123    functions and acts.test_runner.execute_one_test_class.
124    """
125    parser = argparse.ArgumentParser(
126        description=("Specify tests to run. If nothing specified, "
127                     "run all test cases found."))
128    parser.add_argument('-c',
129                        '--config',
130                        type=str,
131                        required=True,
132                        metavar="<PATH>",
133                        help="Path to the test configuration file.")
134    parser.add_argument(
135        '-ci',
136        '--campaign_iterations',
137        metavar="<CAMPAIGN_ITERATIONS>",
138        nargs='?',
139        type=int,
140        const=1,
141        default=1,
142        help="Number of times to run the campaign or a group of test cases.")
143    parser.add_argument('-tb',
144                        '--testbed',
145                        nargs='+',
146                        type=str,
147                        metavar="[<TEST BED NAME1> <TEST BED NAME2> ...]",
148                        help="Specify which test beds to run tests on.")
149    parser.add_argument('-lp',
150                        '--logpath',
151                        type=str,
152                        metavar="<PATH>",
153                        help="Root path under which all logs will be placed.")
154    parser.add_argument(
155        '-tp',
156        '--testpaths',
157        nargs='*',
158        type=str,
159        metavar="<PATH> <PATH>",
160        help="One or more non-recursive test class search paths.")
161
162    group = parser.add_mutually_exclusive_group(required=True)
163    group.add_argument('-tc',
164                       '--testclass',
165                       nargs='+',
166                       type=str,
167                       metavar="[TestClass1 TestClass2:test_xxx ...]",
168                       help="A list of test classes/cases to run.")
169    group.add_argument(
170        '-tf',
171        '--testfile',
172        nargs=1,
173        type=str,
174        metavar="<PATH>",
175        help=("Path to a file containing a comma delimited list of test "
176              "classes to run."))
177    parser.add_argument('-ti',
178                        '--test_case_iterations',
179                        metavar="<TEST_CASE_ITERATIONS>",
180                        nargs='?',
181                        type=int,
182                        help="Number of times to run every test case.")
183
184    args = parser.parse_args(sys.argv[1:])
185    test_list = None
186    if args.testfile:
187        test_list = config_parser.parse_test_file(args.testfile[0])
188    elif args.testclass:
189        test_list = args.testclass
190    if re.search(r'\.ya?ml$', args.config):
191        parsed_configs = mobly_config_parser.load_test_config_file(
192            args.config, args.testbed)
193    else:
194        parsed_configs = config_parser.load_test_config_file(
195            args.config, args.testbed)
196
197    for test_run_config in parsed_configs:
198        if args.testpaths:
199            tp_key = keys.Config.key_test_paths.value
200            test_run_config.controller_configs[tp_key] = args.testpaths
201        if args.logpath:
202            test_run_config.log_path = args.logpath
203        if args.test_case_iterations:
204            ti_key = keys.Config.key_test_case_iterations.value
205            test_run_config.user_params[ti_key] = args.test_case_iterations
206
207        # Sets the --testpaths flag to the default test directory if left unset.
208        testpath_key = keys.Config.key_test_paths.value
209        if (testpath_key not in test_run_config.controller_configs
210                or test_run_config.controller_configs[testpath_key] is None):
211            test_run_config.controller_configs[testpath_key] = utils.abs_path(
212                utils.os.path.join(os.path.dirname(__file__),
213                                   '../../../../acts_tests/tests/'))
214
215        # TODO(markdr): Find a way to merge this with the validation done in
216        # Mobly's load_test_config_file.
217        if not test_run_config.log_path:
218            raise ActsConfigError("Required key %s missing in test config." %
219                                  keys.Config.key_log_path.value)
220        test_run_config.log_path = utils.abs_path(test_run_config.log_path)
221
222    # Prepare args for test runs
223    test_identifiers = config_parser.parse_test_list(test_list)
224
225    exec_result = _run_tests(parsed_configs, test_identifiers,
226                             args.campaign_iterations)
227    if exec_result is False:
228        # return 1 upon test failure.
229        sys.exit(1)
230    sys.exit(0)
231
232
233if __name__ == "__main__":
234    main()
235