ansible/test/lib/ansible_test/_internal/units/__init__.py

159 lines
4.2 KiB
Python

"""Execute unit tests using pytest."""
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os
import sys
from ..util import (
ANSIBLE_TEST_DATA_ROOT,
display,
get_available_python_versions,
is_subdir,
SubprocessError,
)
from ..util_common import (
intercept_command,
ResultType,
)
from ..ansible_util import (
ansible_environment,
check_pyyaml,
)
from ..target import (
walk_internal_targets,
walk_units_targets,
)
from ..config import (
UnitsConfig,
)
from ..coverage_util import (
coverage_context,
)
from ..data import (
data_context,
)
from ..executor import (
AllTargetsSkipped,
Delegate,
get_changes_filter,
install_command_requirements,
REMOTE_ONLY_PYTHON_VERSIONS,
SUPPORTED_PYTHON_VERSIONS,
)
def command_units(args):
"""
:type args: UnitsConfig
"""
changes = get_changes_filter(args)
require = args.require + changes
include = walk_internal_targets(walk_units_targets(), args.include, args.exclude, require)
paths = [target.path for target in include]
remote_paths = [path for path in paths
if is_subdir(path, data_context().content.unit_module_path)
or is_subdir(path, data_context().content.unit_module_utils_path)]
if not paths:
raise AllTargetsSkipped()
if args.python and args.python in REMOTE_ONLY_PYTHON_VERSIONS and not remote_paths:
raise AllTargetsSkipped()
if args.delegate:
raise Delegate(require=changes, exclude=args.exclude)
version_commands = []
available_versions = sorted(get_available_python_versions(list(SUPPORTED_PYTHON_VERSIONS)).keys())
for version in SUPPORTED_PYTHON_VERSIONS:
# run all versions unless version given, in which case run only that version
if args.python and version != args.python_version:
continue
if not args.python and version not in available_versions:
display.warning("Skipping unit tests on Python %s due to missing interpreter." % version)
continue
if args.requirements_mode != 'skip':
install_command_requirements(args, version)
env = ansible_environment(args)
cmd = [
'pytest',
'--boxed',
'-r', 'a',
'-n', str(args.num_workers) if args.num_workers else 'auto',
'--color',
'yes' if args.color else 'no',
'-p', 'no:cacheprovider',
'-c', os.path.join(ANSIBLE_TEST_DATA_ROOT, 'pytest.ini'),
'--junit-xml', os.path.join(ResultType.JUNIT.path, 'python%s-units.xml' % version),
]
if not data_context().content.collection:
cmd.append('--durations=25')
if version != '2.6':
# added in pytest 4.5.0, which requires python 2.7+
cmd.append('--strict-markers')
plugins = []
if args.coverage:
plugins.append('ansible_pytest_coverage')
if data_context().content.collection:
plugins.append('ansible_pytest_collections')
if plugins:
env['PYTHONPATH'] += ':%s' % os.path.join(ANSIBLE_TEST_DATA_ROOT, 'pytest/plugins')
for plugin in plugins:
cmd.extend(['-p', plugin])
if args.collect_only:
cmd.append('--collect-only')
if args.verbosity:
cmd.append('-' + ('v' * args.verbosity))
if version in REMOTE_ONLY_PYTHON_VERSIONS:
test_paths = remote_paths
else:
test_paths = paths
if not test_paths:
continue
cmd.extend(test_paths)
version_commands.append((version, cmd, env))
if args.requirements_mode == 'only':
sys.exit()
for version, command, env in version_commands:
check_pyyaml(args, version)
display.info('Unit test with Python %s' % version)
try:
with coverage_context(args):
intercept_command(args, command, target_name='units', env=env, python_version=version)
except SubprocessError as ex:
# pytest exits with status code 5 when all tests are skipped, which isn't an error for our use case
if ex.status != 5:
raise