| # Copyright 2021 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Script to generate Tauto test wrappers based on JSON configuration. |
| |
| USAGE: python generate_tests.py <config_file.json> [suite] |
| |
| This script generates control files for wrapping all Tast test files provided |
| in the configuration JSON file with a Tauto test cases. No Tauto suite files are |
| generated, these assumed to be added manually. |
| |
| Configuration file may contain multiple suites, in which case, all tests of all |
| suites will be generated, unless [suite] argument is present, in which case |
| only that suite will be re/generated. |
| |
| Configuration file is validated against the schema in config_schema.yaml file. |
| Schema file must be located in the same folder with the current script. |
| """ |
| |
| import copy |
| import json |
| import os |
| import sys |
| from jsonschema import validate |
| import yaml |
| |
| SCHEMA_FILE = 'config_schema.yaml' |
| TEST_TEMPLATE_FILE = 'template.control.performance_cuj' |
| |
| # The priority of the first test. Decremented by 1 for each subsequent test. |
| INITIAL_PRIORITY = 5000 |
| |
| # Max duration of a single test. |
| HOUR_IN_SECS = 60 * 60 |
| DEFAULT_TEST_DURATION = 1 * HOUR_IN_SECS |
| |
| |
| def _get_absolute_path(local_file): |
| return os.path.join(os.path.dirname(os.path.realpath(__file__)), |
| local_file) |
| |
| |
| def _load_json_config(config_path): |
| with open(_get_absolute_path(config_path), 'r') as config_file: |
| return json.load(config_file) |
| |
| |
| def _validate_config_schema(json_config): |
| # Loading the schema file |
| with open(SCHEMA_FILE, 'r') as schema_file: |
| schema = yaml.load(schema_file) |
| |
| validate(json_config, schema) |
| |
| |
| def _parse_constants(json_config): |
| consts = dict() |
| if 'const' in json_config: |
| for c in json_config['const']: |
| consts[c['name']] = c['value'] |
| return consts |
| |
| |
| def _substitute_constants(val, constants): |
| for const in constants: |
| val = val.replace('$' + const + '$', constants[const]) |
| return val |
| |
| |
| def _parse_tests(json_config, constants): |
| tests = [] |
| for test in json_config['tests']: |
| new_test = copy.deepcopy(test) |
| # Substitute constants in all fields of the test. |
| new_test['name'] = _substitute_constants(new_test['name'], constants) |
| new_test['test_expr'] = _substitute_constants(new_test['test_expr'], |
| constants) |
| if 'args' in new_test: |
| new_args = [] |
| for arg in new_test['args']: |
| new_args.append(_substitute_constants(arg, constants)) |
| new_test['args'] = new_args |
| if 'attributes' in new_test: |
| new_attrs = [] |
| for attr in new_test['attributes']: |
| new_attrs.append(_substitute_constants(attr, constants)) |
| new_test['attributes'] = new_attrs |
| tests.append(new_test) |
| return tests |
| |
| |
| def _find_test(test_name, tests): |
| for test in tests: |
| if test['name'] == test_name: |
| return test |
| return None |
| |
| |
| def _parse_suites(json_config, tests, constants): |
| suites = [] |
| for suite in json_config['suites']: |
| new_suite = copy.deepcopy(suite) |
| new_suite['name'] = _substitute_constants(new_suite['name'], constants) |
| if 'args_file' in new_suite: |
| new_suite['args_file'] = _substitute_constants( |
| new_suite['args_file'], constants) |
| if 'args' in new_suite: |
| new_args = [] |
| for arg in new_suite['args']: |
| new_args.append(_substitute_constants(arg, constants)) |
| new_suite['args'] = new_args |
| for test in new_suite['tests']: |
| if not _find_test(test['test'], tests): |
| raise Exception( |
| 'Test %s (requested by suite %s) is not defined.' % |
| (test['test'], new_suite['name'])) |
| test['test'] = _substitute_constants(test['test'], constants) |
| suites.append(new_suite) |
| return suites |
| |
| |
| def _read_file(filename): |
| with open(filename, 'r') as content_file: |
| return content_file.read() |
| |
| |
| def _write_file(filename, data): |
| with open(filename, 'w') as out_file: |
| out_file.write(data) |
| |
| |
| def _normalize_test_name(test_name): |
| return test_name.replace('.', '_').replace('*', '_') |
| |
| |
| def _calculate_suffix(current_index, repeats): |
| # No suffix for single tests. |
| if repeats == 1: |
| return '' |
| # Number of suffix digits depends on the total repeat count. |
| digits = len(str(repeats)) |
| format_string = ('_{{index:0{digits}n}}').format(digits=digits) |
| return format_string.format(index=current_index) |
| |
| |
| def _generate_test_files(version, suites, tests, suite_name=None): |
| template = _read_file(_get_absolute_path(TEST_TEMPLATE_FILE)) |
| for suite in suites: |
| priority = INITIAL_PRIORITY |
| if suite_name and suite['name'] != suite_name: |
| continue |
| for test in suite['tests']: |
| test_data = _find_test(test['test'], tests) |
| repeats = test['repeats'] |
| for i in range(repeats): |
| test_name = _normalize_test_name( |
| test_data['test_expr'] + |
| _calculate_suffix(i + 1, repeats)) |
| control_file = template.format( |
| name=test_name, |
| priority=priority, |
| duration=DEFAULT_TEST_DURATION, |
| test_exprs=test_data['test_expr'], |
| length='long', |
| version=version, |
| attributes='suite:' + suite['name'], |
| iteration=i + 1, |
| ) |
| control_file_name = 'control.' + '_'.join( |
| [suite['name'], test_name]) |
| _write_file(control_file_name, control_file) |
| priority = priority - 1 |
| |
| |
| def main(argv): |
| """Main program that parses JSON configuration and generates test wrappers.""" |
| if not argv or len(argv) < 2 or len(argv) > 3: |
| raise Exception( |
| 'Invalid command-line arguments. Usage: python generate_tests.py <config_file.json> [suite]' |
| ) |
| |
| suite_name = None |
| if (len(argv) == 3): |
| suite_name = argv[2] |
| |
| # Load and validate the config JSON file. |
| json_config = _load_json_config(argv[1]) |
| _validate_config_schema(json_config) |
| |
| version = json_config['version'] |
| constants = _parse_constants(json_config) |
| tests = _parse_tests(json_config, constants) |
| suites = _parse_suites(json_config, tests, constants) |
| _generate_test_files(version, suites, tests, suite_name) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv)) |