More prep for ansible-test relocation. (#60114)

* Update pytest plugins.
* Fix update-bundled sanity test.
* Remove old validate-modules comment.
* Fix ansible-test plugin loading
* Update code coverage comments.
* Fix Makefile ansible-test reference.
* Remove incorrect path from lint output.
* Update ansible-test unit tests.
* Make ansible-test's own unit tests singular.
This commit is contained in:
Matt Clay 2019-08-05 16:38:21 -07:00 committed by GitHub
parent ac5228390c
commit fa2adf3b7b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 45 additions and 35 deletions

View file

@ -1,9 +1,9 @@
.PHONY: all sanity units all: sanity unit
all: sanity units
.PHONY: sanity
sanity: sanity:
./ansible-test sanity test/runner/ ${TEST_FLAGS} ansible-test sanity test/runner/ ${FLAGS}
units: .PHONY: unit
PYTHONPATH=. pytest units ${TEST_FLAGS} unit:
PYTHONPATH=.:.. pytest unit ${FLAGS}

View file

@ -285,7 +285,7 @@ class TestFailure(TestResult):
if self.summary: if self.summary:
command = self.format_command() command = self.format_command()
message = 'The test `%s` failed. See stderr output for details.' % command message = 'The test `%s` failed. See stderr output for details.' % command
path = 'test/runner/ansible-test' path = ''
message = TestMessage(message, path) message = TestMessage(message, path)
print(message) print(message)
else: else:

View file

@ -812,10 +812,11 @@ def import_plugins(directory, root=None): # type: (str, t.Optional[str]) -> Non
root = os.path.dirname(__file__) root = os.path.dirname(__file__)
path = os.path.join(root, directory) path = os.path.join(root, directory)
prefix = 'lib.%s.' % directory.replace(os.sep, '.') package = __name__.rsplit('.', 1)[0]
prefix = '%s.%s.' % (package, directory.replace(os.sep, '.'))
for (_module_loader, name, _ispkg) in pkgutil.iter_modules([path], prefix=prefix): for (_module_loader, name, _ispkg) in pkgutil.iter_modules([path], prefix=prefix):
module_path = os.path.join(root, name[4:].replace('.', os.sep) + '.py') module_path = os.path.join(root, name[len(package) + 1:].replace('.', os.sep) + '.py')
load_module(module_path, name) load_module(module_path, name)
@ -824,7 +825,7 @@ def load_plugins(base_type, database): # type: (t.Type[C], t.Dict[str, t.Type[C
Load plugins of the specified type and track them in the specified database. Load plugins of the specified type and track them in the specified database.
Only plugins which have already been imported will be loaded. Only plugins which have already been imported will be loaded.
""" """
plugins = dict((sc.__module__.split('.')[2], sc) for sc in get_subclasses(base_type)) # type: t.Dict[str, t.Type[C]] plugins = dict((sc.__module__.rsplit('.', 1)[1], sc) for sc in get_subclasses(base_type)) # type: t.Dict[str, t.Type[C]]
for plugin in plugins: for plugin in plugins:
database[plugin] = plugins[plugin] database[plugin] = plugins[plugin]
@ -832,6 +833,9 @@ def load_plugins(base_type, database): # type: (t.Type[C], t.Dict[str, t.Type[C
def load_module(path, name): # type: (str, str) -> None def load_module(path, name): # type: (str, str) -> None
"""Load a Python module using the given name and path.""" """Load a Python module using the given name and path."""
if name in sys.modules:
return
if sys.version_info >= (3, 4): if sys.version_info >= (3, 4):
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
import importlib.util import importlib.util

View file

@ -169,8 +169,8 @@ def get_coverage_environment(args, target_name, version, temp_path, module_cover
coverage_file = '' coverage_file = ''
# Enable code coverage collection on local Python programs (this does not include Ansible modules). # Enable code coverage collection on local Python programs (this does not include Ansible modules).
# Used by the injectors in test/runner/injector/ to support code coverage. # Used by the injectors to support code coverage.
# Used by unit tests in test/units/conftest.py to support code coverage. # Used by the pytest unit test plugin to support code coverage.
# The COVERAGE_FILE variable is also used directly by the 'coverage' module. # The COVERAGE_FILE variable is also used directly by the 'coverage' module.
env = dict( env = dict(
COVERAGE_CONF=config_file, COVERAGE_CONF=config_file,

View file

@ -6,6 +6,11 @@ import os
import subprocess import subprocess
import pytest import pytest
from lib.util import (
to_text,
to_bytes,
)
from lib.diff import ( from lib.diff import (
parse_diff, parse_diff,
FileDiff, FileDiff,
@ -19,18 +24,18 @@ def get_diff(base, head=None):
:rtype: list[str] :rtype: list[str]
""" """
if not head or head == 'HEAD': if not head or head == 'HEAD':
head = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip() head = to_text(subprocess.check_output(['git', 'rev-parse', 'HEAD'])).strip()
cache = '/tmp/git-diff-cache-%s-%s.log' % (base, head) cache = '/tmp/git-diff-cache-%s-%s.log' % (base, head)
if os.path.exists(cache): if os.path.exists(cache):
with open(cache, 'r') as cache_fd: with open(cache, 'rb') as cache_fd:
lines = cache_fd.read().splitlines() lines = to_text(cache_fd.read()).splitlines()
else: else:
lines = subprocess.check_output(['git', 'diff', base, head]).splitlines() lines = to_text(subprocess.check_output(['git', 'diff', base, head]), errors='replace').splitlines()
with open(cache, 'w') as cache_fd: with open(cache, 'wb') as cache_fd:
cache_fd.write('\n'.join(lines)) cache_fd.write(to_bytes('\n'.join(lines)))
assert lines assert lines

View file

@ -1,7 +1,8 @@
{ {
"all_targets": true, "all_targets": true,
"extensions": [ "ignore_self": true,
".py" "extensions": [
], ".py"
"output": "path-message" ],
"output": "path-message"
} }

View file

@ -70,9 +70,6 @@ def get_files_with_bundled_metadata(paths):
with_metadata = set() with_metadata = set()
for path in paths: for path in paths:
if path == 'test/sanity/code-smell/update-bundled.py':
continue
with open(path, 'rb') as f: with open(path, 'rb') as f:
body = f.read() body = f.read()

View file

@ -7914,8 +7914,6 @@ test/units/plugins/shell/test_cmd.py metaclass-boilerplate
test/units/plugins/shell/test_powershell.py future-import-boilerplate test/units/plugins/shell/test_powershell.py future-import-boilerplate
test/units/plugins/shell/test_powershell.py metaclass-boilerplate test/units/plugins/shell/test_powershell.py metaclass-boilerplate
test/units/plugins/test_plugins.py pylint:blacklisted-name test/units/plugins/test_plugins.py pylint:blacklisted-name
test/units/pytest/plugins/ansible_pytest_collections.py metaclass-boilerplate
test/units/pytest/plugins/ansible_pytest_coverage.py metaclass-boilerplate
test/units/template/test_templar.py pylint:blacklisted-name test/units/template/test_templar.py pylint:blacklisted-name
test/units/test_constants.py future-import-boilerplate test/units/test_constants.py future-import-boilerplate
test/units/test_context.py future-import-boilerplate test/units/test_context.py future-import-boilerplate

View file

@ -38,8 +38,6 @@ class AnsibleTextIOWrapper(TextIOWrapper):
def find_executable(executable, cwd=None, path=None): def find_executable(executable, cwd=None, path=None):
"""Finds the full path to the executable specified""" """Finds the full path to the executable specified"""
# This is mostly a copy from test/runner/lib/util.py. Should be removed once validate-modules has been integrated
# into ansible-test
match = None match = None
real_cwd = os.getcwd() real_cwd = os.getcwd()

View file

@ -1,6 +1,6 @@
"""Enable unit testing of Ansible collections.""" """Enable unit testing of Ansible collections."""
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os import os
import sys import sys
@ -10,6 +10,7 @@ ANSIBLE_COLLECTIONS_PATH = os.path.join(os.environ['ANSIBLE_COLLECTIONS_PATHS'],
def collection_pypkgpath(self): def collection_pypkgpath(self):
"""Configure the Python package path so that pytest can find our collections."""
for parent in self.parts(reverse=True): for parent in self.parts(reverse=True):
if str(parent) == ANSIBLE_COLLECTIONS_PATH: if str(parent) == ANSIBLE_COLLECTIONS_PATH:
return parent return parent
@ -18,13 +19,16 @@ def collection_pypkgpath(self):
def pytest_configure(): def pytest_configure():
"""Configure this pytest plugin."""
from ansible.utils.collection_loader import AnsibleCollectionLoader from ansible.utils.collection_loader import AnsibleCollectionLoader
# allow unit tests to import code from collections # allow unit tests to import code from collections
sys.meta_path.insert(0, AnsibleCollectionLoader()) sys.meta_path.insert(0, AnsibleCollectionLoader())
# noinspection PyProtectedMember
import py._path.local import py._path.local
# force collections unit tests to be loaded with the ansible_collections namespace # force collections unit tests to be loaded with the ansible_collections namespace
# original idea from https://stackoverflow.com/questions/50174130/how-do-i-pytest-a-project-using-pep-420-namespace-packages/50175552#50175552 # original idea from https://stackoverflow.com/questions/50174130/how-do-i-pytest-a-project-using-pep-420-namespace-packages/50175552#50175552
py._path.local.LocalPath.pypkgpath = collection_pypkgpath # noinspection PyProtectedMember
py._path.local.LocalPath.pypkgpath = collection_pypkgpath # pylint: disable=protected-access

View file

@ -1,15 +1,17 @@
"""Monkey patch os._exit when running under coverage so we don't lose coverage data in forks, such as with `pytest --boxed`.""" """Monkey patch os._exit when running under coverage so we don't lose coverage data in forks, such as with `pytest --boxed`."""
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
def pytest_configure(): def pytest_configure():
"""Configure this pytest plugin."""
try: try:
import coverage import coverage
except ImportError: except ImportError:
coverage = None coverage = None
try: try:
test = coverage.Coverage coverage.Coverage
except AttributeError: except AttributeError:
coverage = None coverage = None
@ -41,7 +43,8 @@ def pytest_configure():
else: else:
cov = None cov = None
os_exit = os._exit # noinspection PyProtectedMember
os_exit = os._exit # pylint: disable=protected-access
def coverage_exit(*args, **kwargs): def coverage_exit(*args, **kwargs):
for instance in coverage_instances: for instance in coverage_instances:
@ -50,7 +53,7 @@ def pytest_configure():
os_exit(*args, **kwargs) os_exit(*args, **kwargs)
os._exit = coverage_exit os._exit = coverage_exit # pylint: disable=protected-access
if cov: if cov:
cov.start() cov.start()