# SPDX-License-Identifier: Apache-2.0
# Copyright Contributors to the Rez Project
'''
Run unit tests. Use pytest if available.
'''
import os
import sys
import inspect
import argparse
from pkgutil import iter_modules
try:
import pytest # noqa
use_pytest = True
except ImportError:
use_pytest = False
cli_dir = os.path.dirname(inspect.getfile(inspect.currentframe()))
src_rez_dir = os.path.dirname(cli_dir)
tests_dir = os.path.join(src_rez_dir, 'tests')
all_module_tests = []
[docs]def setup_parser(parser, completions=False):
parser.add_argument(
"tests", metavar="NAMED_TEST", default=[], nargs="*",
help="a specific test module/class/method to run; may be repeated "
"multiple times; if no tests are given, through this or other flags, "
"all tests are run")
parser.add_argument(
"-s", "--only-shell", metavar="SHELL",
help="limit shell-dependent tests to the specified shell. Note: This "
"flag shadowed pytest '–capture=no' shorthand '-s', so the long "
"name must be used for disabling stdout/err capturing in pytest."
)
# make an Action that will append the appropriate test to the "--test" arg
class AddTestModuleAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
name = option_string.lstrip('-')
if getattr(namespace, "module_tests", None) is None:
namespace.module_tests = []
namespace.module_tests.append(name)
# find unit tests
tests = []
prefix = "test_"
for importer, name, ispkg in iter_modules([tests_dir]):
if not ispkg and name.startswith(prefix):
module = importer.find_module(name).load_module(name)
name_ = name[len(prefix):]
all_module_tests.append(name_)
tests.append((name_, module))
# create argparse entry for each module's unit test
for name, module in sorted(tests):
parser.add_argument(
"--%s" % name, action=AddTestModuleAction, nargs=0,
dest="module_tests", default=[],
help=module.__doc__.strip().rstrip('.'))
[docs]def command(opts, parser, extra_arg_groups=None):
os.environ["__REZ_SELFTEST_RUNNING"] = "1"
if opts.only_shell:
os.environ["__REZ_SELFTEST_SHELL"] = opts.only_shell
if not opts.module_tests and not opts.tests:
module_tests = all_module_tests
else:
module_tests = opts.module_tests
if use_pytest:
cwd = os.getcwd()
os.chdir(tests_dir)
try:
run_pytest(module_tests, opts.tests, opts.verbose,
extra_arg_groups)
finally:
os.chdir(cwd)
else:
run_unittest(module_tests, opts.tests, opts.verbose)
[docs]def run_unittest(module_tests, tests, verbosity):
from unittest.main import main
module_tests = [("rez.tests.test_%s" % x) for x in sorted(module_tests)]
tests = module_tests + tests
argv = [sys.argv[0]] + tests
main(module=None, argv=argv, verbosity=verbosity)
[docs]def run_pytest(module_tests, tests, verbosity, extra_arg_groups):
from pytest import main
# parse test name, e.g.
# "rez.tests.test_solver.TestSolver.test_01"
# into
# "test_solver.py::TestSolver::test_01"
test_specifications = []
for test in tests:
specifier = ""
for part in test.split("."):
if specifier:
specifier += "::" + part
continue
if os.path.isfile(part + ".py"):
specifier = part + ".py"
if specifier:
test_specifications.append(specifier)
module_tests = [("test_%s.py" % x) for x in sorted(module_tests)]
tests = module_tests + test_specifications
argv = tests[:]
if verbosity:
argv += ["-" + ("v" * verbosity)]
if extra_arg_groups:
argv += extra_arg_groups[0]
exitcode = main(args=argv)
sys.exit(exitcode)