| #!/usr/bin/env python3 |
| # |
| # Copyright 2022 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import argparse |
| import json |
| import os |
| import re |
| import sys |
| |
| # The basic shell script for client test run in Skylab. The arguments listed |
| # here will be fed by autotest at the run time. |
| # |
| # * test-launcher-summary-output: the path for the json result. It will be |
| # assigned by autotest, who will upload it to GCS upon test completion. |
| # * test-launcher-shard-index: the index for this test run. |
| # * test-launcher-total-shards: the total test shards. |
| # * test_args: arbitrary runtime arguments configured in test_suites.pyl, |
| # attached after '--'. |
| BASIC_SHELL_SCRIPT = """ |
| #!/bin/sh |
| |
| while [[ $# -gt 0 ]]; do |
| case "$1" in |
| --test-launcher-summary-output) |
| summary_output=$2 |
| shift 2 |
| ;; |
| |
| --test-launcher-shard-index) |
| shard_index=$2 |
| shift 2 |
| ;; |
| |
| --test-launcher-total-shards) |
| total_shards=$2 |
| shift 2 |
| ;; |
| |
| --) |
| test_args=$2 |
| break |
| ;; |
| |
| *) |
| break |
| ;; |
| esac |
| done |
| |
| if [ ! -d $(dirname $summary_output) ] ; then |
| mkdir -p $(dirname $summary_output) |
| fi |
| |
| cd `dirname $0` && cd .. |
| """ |
| |
| |
| def build_test_script(args): |
| # Build the shell script that will be used on the device to invoke the test. |
| # Stored here as a list of lines. |
| device_test_script_contents = BASIC_SHELL_SCRIPT.split('\n') |
| |
| test_invocation = ('LD_LIBRARY_PATH=./ ./%s ' |
| ' --test-launcher-summary-output=$summary_output' |
| ' --test-launcher-shard-index=$shard_index' |
| ' --test-launcher-total-shards=$total_shards' |
| ' $test_args' % args.test_exe) |
| |
| device_test_script_contents.append(test_invocation) |
| with open(args.output, 'w') as w: |
| w.write('\n'.join(device_test_script_contents)) |
| os.chmod(args.output, 0o755) |
| |
| |
| def build_filter_file(args): |
| # TODO(b/227381644): This expression is hard to follow and should be |
| # simplified. This would require a change on the cros infra side as well |
| tast_expr_dict = {} |
| default_disabled_tests = [] |
| if args.disabled_tests is not None: |
| default_disabled_tests = [ |
| '!"name:{0}"'.format(test) for test in args.disabled_tests |
| ] |
| |
| default_enabled_test_term = '' |
| if args.enabled_tests is not None: |
| default_enabled_test_term = (' || ').join( |
| ['"name:{0}"'.format(test) for test in args.enabled_tests]) |
| |
| # Generate the default expression to be used when there is no known key |
| tast_expr = args.tast_expr if args.tast_expr else "" |
| |
| if default_disabled_tests: |
| default_disabled_term = " && ".join(default_disabled_tests) |
| tast_expr = "{0} && {1}".format(tast_expr, default_disabled_term) if \ |
| tast_expr else default_disabled_term |
| |
| if default_enabled_test_term: |
| tast_expr = "{0} && ({1})".format( |
| tast_expr, |
| default_enabled_test_term) if tast_expr else default_enabled_test_term |
| |
| tast_expr_dict['default'] = "({0})".format(tast_expr) |
| |
| # Generate an expression for each collection in the gni file |
| if args.tast_control is not None: |
| with open(args.tast_control, 'r') as tast_control_file: |
| gni = tast_control_file.read() |
| filter_lists = re.findall(r'(.*) = \[([^\]]*)\]', gni) |
| for filter_list in filter_lists: |
| tast_expr = args.tast_expr if args.tast_expr else "" |
| |
| milestone_disabled_tests = { |
| '!"name:{0}"'.format(test) |
| for test in re.findall(r'"([^"]+)"', filter_list[1]) |
| } |
| |
| milestone_disabled_tests.update(default_disabled_tests) |
| |
| if milestone_disabled_tests: |
| tast_expr = "{0} && {1}".format( |
| tast_expr, " && ".join(milestone_disabled_tests) |
| ) if tast_expr else " && ".join(milestone_disabled_tests) |
| |
| if default_enabled_test_term: |
| tast_expr = "{0} && ({1})".format( |
| tast_expr, default_enabled_test_term |
| ) if tast_expr else default_enabled_test_term |
| |
| if tast_expr: |
| tast_expr_dict[filter_list[0]] = "({0})".format(tast_expr) |
| |
| if len(tast_expr_dict) > 0: |
| with open(args.output, "w") as file: |
| json.dump(tast_expr_dict, file, indent=2) |
| os.chmod(args.output, 0o644) |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| subparsers = parser.add_subparsers(dest='command') |
| |
| script_gen_parser = subparsers.add_parser('generate-runner') |
| script_gen_parser.add_argument( |
| '--test-exe', |
| type=str, |
| required=True, |
| help='Path to test executable to run inside the device.') |
| script_gen_parser.add_argument('--verbose', '-v', action='store_true') |
| script_gen_parser.add_argument( |
| '--output', |
| required=True, |
| type=str, |
| help='Path to create the runner script.') |
| script_gen_parser.set_defaults(func=build_test_script) |
| |
| filter_gen_parser = subparsers.add_parser('generate-filter') |
| filter_gen_parser.add_argument( |
| '--tast-expr', |
| type=str, |
| required=False, |
| help='Tast expression to determine tests to run. This creates the ' |
| 'initial set of tests that can be further filtered.') |
| filter_gen_parser.add_argument( |
| '--enabled-tests', |
| type=str, |
| required=False, |
| action='append', |
| help='Name of tests to allow to test (unnamed tests will not run).') |
| filter_gen_parser.add_argument( |
| '--disabled-tests', |
| type=str, |
| required=False, |
| action='append', |
| help='Names of tests to disable from running') |
| filter_gen_parser.add_argument( |
| '--tast-control', |
| type=str, |
| required=False, |
| help='Filename for the tast_control file containing version skew ' |
| 'test filters to generate.') |
| filter_gen_parser.add_argument( |
| '--output', |
| required=True, |
| type=str, |
| help='Path to create the plain text filter file.') |
| filter_gen_parser.set_defaults(func=build_filter_file) |
| |
| args = parser.parse_args() |
| |
| if (args.command == "generate-filter" and args.disabled_tests is None and |
| args.enabled_tests is None and args.tast_expr is None): |
| parser.error( |
| '--disabled-tests, --enabled-tests, or --tast-expr must be provided ' |
| 'to generate-filter') |
| |
| args.func(args) |
| |
| return 0 |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |