| #!/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] |
| 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)) |