| #!/usr/bin/env python3 | |
| # SPDX-License-Identifier: Apache-2.0 | |
| # Copyright © 2021-2024 Intel Corporation | |
| """Script for running a single project test. | |
| This script is meant for Meson developers who want to run a single project | |
| test, with all of the rules from the test.json file loaded. | |
| """ | |
| import argparse | |
| import pathlib | |
| import typing as T | |
| from mesonbuild import mlog | |
| from mesonbuild.mesonlib import is_windows | |
| from run_tests import handle_meson_skip_test | |
| from run_project_tests import TestDef, TestCategory, load_test_json, run_test, BuildStep | |
| from run_project_tests import setup_commands, detect_system_compiler, detect_tools | |
| from run_project_tests import scan_test_data_symlinks, setup_symlinks, clear_transitive_files | |
| if T.TYPE_CHECKING: | |
| from run_project_tests import CompilerArgumentType | |
| class ArgumentType(CompilerArgumentType): | |
| """Typing information for command line arguments.""" | |
| case: pathlib.Path | |
| subtests: T.List[int] | |
| backend: str | |
| extra_args: T.List[str] | |
| quick: bool | |
| def main() -> None: | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument('case', type=pathlib.Path, help='The test case to run') | |
| parser.add_argument('extra_args', nargs='*', | |
| help='arguments that are passed directly to Meson (remember to have -- before these).') | |
| parser.add_argument('--subtest', type=int, action='append', dest='subtests', help='which subtests to run') | |
| parser.add_argument('--backend', action='store', help="Which backend to use") | |
| parser.add_argument('--cross-file', action='store', help='File describing cross compilation environment.') | |
| parser.add_argument('--native-file', action='store', help='File describing native compilation environment.') | |
| parser.add_argument('--use-tmpdir', action='store_true', help='Use tmp directory for temporary files.') | |
| parser.add_argument('--quick', action='store_true', help='Skip some compiler and tool checking') | |
| args = T.cast('ArgumentType', parser.parse_args()) | |
| if not is_windows(): | |
| scan_test_data_symlinks() | |
| setup_symlinks() | |
| setup_commands(args.backend) | |
| if not args.quick: | |
| detect_system_compiler(args) | |
| detect_tools(not args.quick) | |
| cat = TestCategory(args.case.stem, args.case.stem, False, False) | |
| test = TestDef(args.case, None, [], False, cat) | |
| tests = load_test_json(test, cat) | |
| if args.subtests: | |
| tests = [t for i, t in enumerate(tests) if i in args.subtests] | |
| def should_fail(path: pathlib.Path) -> str: | |
| dir_ = path.parent.stem | |
| # FIXME: warning tets might not be handled correctly still… | |
| if dir_.startswith(('failing', 'warning')): | |
| if ' ' in dir_: | |
| return dir_.split(' ')[1] | |
| return 'meson' | |
| return '' | |
| results = [run_test(t, t.args + args.extra_args, should_fail(t.path), args.use_tmpdir) for t in tests] | |
| failed = False | |
| for test, result in zip(tests, results): | |
| if result is None: | |
| is_skipped = True | |
| skip_reason = 'not run because preconditions were not met' | |
| else: | |
| is_skipped, skip_reason = handle_meson_skip_test(result.stdo) | |
| if is_skipped: | |
| msg = mlog.yellow('SKIP:') | |
| elif result.msg: | |
| msg = mlog.red('FAIL:') | |
| failed = True | |
| else: | |
| msg = mlog.green('PASS:') | |
| mlog.log(msg, *test.display_name()) | |
| if skip_reason: | |
| mlog.log(mlog.bold('Reason:'), skip_reason) | |
| if result is not None and result.msg and 'MESON_SKIP_TEST' not in result.stdo: | |
| mlog.log('reason:', result.msg) | |
| if result.step is BuildStep.configure: | |
| # For configure failures, instead of printing stdout, | |
| # print the meson log if available since it's a superset | |
| # of stdout and often has very useful information. | |
| mlog.log(result.mlog) | |
| else: | |
| mlog.log(result.stdo) | |
| for cmd_res in result.cicmds: | |
| mlog.log(cmd_res) | |
| mlog.log(result.stde) | |
| clear_transitive_files() | |
| exit(1 if failed else 0) | |
| if __name__ == "__main__": | |
| main() |