| #!/usr/bin/env python3 | |
| # SPDX-License-Identifier: Apache-2.0 | |
| # Copyright 2016-2021 The Meson development team | |
| # Work around some pathlib bugs... | |
| from mesonbuild import _pathlib | |
| import sys | |
| sys.modules['pathlib'] = _pathlib | |
| import time | |
| import subprocess | |
| import os | |
| import unittest | |
| import importlib | |
| import typing as T | |
| import mesonbuild.coredata | |
| from mesonbuild.mesonlib import python_command, setup_vsenv | |
| def convert_args(argv): | |
| # If we got passed a list of tests, pass it on | |
| pytest_args = ['-v'] if '-v' in argv else [] | |
| test_list = [] | |
| for arg in argv: | |
| if arg.startswith('-'): | |
| if arg in ('-f', '--failfast'): | |
| arg = '--exitfirst' | |
| pytest_args.append(arg) | |
| continue | |
| # ClassName.test_name => 'ClassName and test_name' | |
| if '.' in arg: | |
| arg = ' and '.join(arg.split('.')) | |
| test_list.append(arg) | |
| if test_list: | |
| pytest_args += ['-k', ' or '.join(test_list)] | |
| return pytest_args | |
| def running_single_tests(argv, cases): | |
| ''' | |
| Check whether we only got arguments for running individual tests, not | |
| entire testcases, and not all testcases (no test args). | |
| ''' | |
| got_test_arg = False | |
| for arg in argv: | |
| if arg.startswith('-'): | |
| continue | |
| for case in cases: | |
| if not arg.startswith(case): | |
| continue | |
| if '.' not in arg: | |
| # Got a testcase, done | |
| return False | |
| got_test_arg = True | |
| return got_test_arg | |
| def setup_backend(): | |
| filtered = [] | |
| be = 'ninja' | |
| for a in sys.argv: | |
| if a.startswith('--backend'): | |
| be = a.split('=', 1)[1] | |
| else: | |
| filtered.append(a) | |
| # Since we invoke the tests via unittest or xtest test runner | |
| # we need to pass the backend to use to the spawned process via | |
| # this side channel. Yes it sucks, but at least is is fully | |
| # internal to this file. | |
| os.environ['MESON_UNIT_TEST_BACKEND'] = be | |
| sys.argv = filtered | |
| def import_test_cases(suite: unittest.TestSuite) -> T.Set[str]: | |
| ''' | |
| Imports all test classes into the current module and returns their names | |
| ''' | |
| classes = set() | |
| for test in suite: | |
| if isinstance(test, unittest.TestSuite): | |
| classes.update(import_test_cases(test)) | |
| elif isinstance(test, unittest.TestCase): | |
| mod = importlib.import_module(test.__module__) | |
| class_name = test.__class__.__name__ | |
| test_class = getattr(mod, class_name) | |
| classes.add(class_name) | |
| setattr(sys.modules[__name__], class_name, test_class) | |
| return classes | |
| def discover_test_cases() -> T.Set[str]: | |
| current_dir = os.path.dirname(os.path.realpath(__file__)) | |
| loader = unittest.TestLoader() | |
| suite = loader.discover(os.path.join(current_dir, 'unittests'), '*tests.py', current_dir) | |
| if loader.errors: | |
| raise SystemExit(loader.errors) | |
| return import_test_cases(suite) | |
| def main(): | |
| cases = sorted(discover_test_cases()) | |
| setup_backend() | |
| try: | |
| import pytest # noqa: F401 | |
| pytest_args = [] | |
| try: | |
| # Need pytest-xdist for `-n` arg | |
| import xdist # noqa: F401 | |
| # Don't use pytest-xdist when running single unit tests since it wastes | |
| # time spawning a lot of processes to distribute tests to in that case. | |
| if not running_single_tests(sys.argv, cases): | |
| pytest_args += ['-n', 'auto'] | |
| except ImportError: | |
| print('pytest-xdist not found, tests will not be distributed across CPU cores') | |
| # Let there be colors! | |
| if 'CI' in os.environ: | |
| pytest_args += ['--color=yes'] | |
| pytest_args += ['unittests'] | |
| pytest_args += convert_args(sys.argv[1:]) | |
| # Always disable pytest-cov because we use a custom setup | |
| try: | |
| import pytest_cov # noqa: F401 | |
| print('Disabling pytest-cov') | |
| pytest_args += ['-p' 'no:cov'] | |
| except ImportError: | |
| pass | |
| return subprocess.run(python_command + ['-m', 'pytest'] + pytest_args).returncode | |
| except ImportError: | |
| print('pytest not found, using unittest instead') | |
| # Fallback to plain unittest. | |
| return unittest.main(defaultTest=cases, buffer=True) | |
| if __name__ == '__main__': | |
| setup_vsenv() | |
| print('Meson build system', mesonbuild.coredata.version, 'Unit Tests') | |
| start = time.monotonic() | |
| try: | |
| raise SystemExit(main()) | |
| finally: | |
| print('Total time: {:.3f} seconds'.format(time.monotonic() - start)) |