blob: 641b12478fd87f32bc864b250a3c1fdda6026ee6 [file] [log] [blame]
# 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>
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.
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):
template = _read_file(_get_absolute_path(TEST_TEMPLATE_FILE))
priority = INITIAL_PRIORITY
for suite in suites:
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'],
)
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:
raise Exception(
'Missing command-line arguments. Usage: python generate_tests.py <config_file.json>'
)
# 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)
if __name__ == '__main__':
sys.exit(main(sys.argv))