blob: 6a84f5c05b04a0dae4ec5c93995a83dfcf5bc9d1 [file] [log] [blame]
#!/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))