[stable-2.11] ansible-test - Use relative paths in junit output. (#76909)

* [stable-2.11] ansible-test - Use relative paths in junit output. (#76871)

* ansible-test - Use relative paths in junit output.
* ansible-test - Handle out-of-tree JUnit paths.
* Also fix a traceback in the junit callback during automatic fact gathering.

(cherry picked from commit fbb5d56bd2)

* Fix task path unicode error in junit callback.

(cherry picked from commit 41db6d8d35)
pull/76962/head
Matt Clay 3 years ago committed by GitHub
parent 4081c8d239
commit cc97353702
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,6 @@
bugfixes:
- ansible-test - Use relative paths in JUnit files generated during integration test runs.
- ansible-test - Replace the directory portion of out-of-tree paths in JUnit files from integration tests with the ``out-of-tree:`` prefix.
- junit callback - Fix traceback during automatic fact gathering when using relative paths.
minor_changes:
- junit callback - Add support for replacing the directory portion of out-of-tree relative task paths with a placeholder.

@ -0,0 +1,2 @@
bugfixes:
- junit callback - Fix unicode error when handling non-ASCII task paths.

@ -40,6 +40,13 @@ DOCUMENTATION = '''
version_added: "2.8"
env:
- name: JUNIT_TASK_RELATIVE_PATH
replace_out_of_tree_path:
name: Replace out of tree path
default: none
description: Replace the directory portion of an out-of-tree relative task path with the given placeholder
version_added: "2.12.3"
env:
- name: JUNIT_REPLACE_OUT_OF_TREE_PATH
fail_on_change:
name: JUnit fail on change
default: False
@ -164,6 +171,7 @@ class CallbackModule(CallbackBase):
self._include_setup_tasks_in_report = os.getenv('JUNIT_INCLUDE_SETUP_TASKS_IN_REPORT', 'True').lower()
self._hide_task_arguments = os.getenv('JUNIT_HIDE_TASK_ARGUMENTS', 'False').lower()
self._test_case_prefix = os.getenv('JUNIT_TEST_CASE_PREFIX', '')
self._replace_out_of_tree_path = os.getenv('JUNIT_REPLACE_OUT_OF_TREE_PATH', None)
self._playbook_path = None
self._playbook_name = None
self._play_name = None
@ -183,6 +191,9 @@ class CallbackModule(CallbackBase):
self._display.warning('The `ordereddict` python module is not installed. '
'Disabling the `junit` callback plugin.')
if self._replace_out_of_tree_path is not None:
self._replace_out_of_tree_path = to_text(self._replace_out_of_tree_path)
if not os.path.exists(self._output_dir):
os.makedirs(self._output_dir)
@ -241,11 +252,14 @@ class CallbackModule(CallbackBase):
name = '[%s] %s: %s' % (host_data.name, task_data.play, task_data.name)
duration = host_data.finish - task_data.start
if self._task_relative_path:
junit_classname = os.path.relpath(task_data.path, self._task_relative_path)
if self._task_relative_path and task_data.path:
junit_classname = to_text(os.path.relpath(to_bytes(task_data.path), to_bytes(self._task_relative_path)))
else:
junit_classname = task_data.path
if self._replace_out_of_tree_path is not None and junit_classname.startswith('../'):
junit_classname = self._replace_out_of_tree_path + to_text(os.path.basename(to_bytes(junit_classname)))
if self._task_class == 'true':
junit_classname = re.sub(r'\.yml:[0-9]+$', '', junit_classname)

@ -143,6 +143,7 @@ from .integration import (
get_inventory_relative_path,
check_inventory,
delegate_inventory,
IntegrationEnvironment,
)
from .data import (
@ -1573,7 +1574,7 @@ def run_setup_targets(args, test_dir, target_names, targets_dict, targets_execut
targets_executed.add(target_name)
def integration_environment(args, target, test_dir, inventory_path, ansible_config, env_config):
def integration_environment(args, target, test_dir, inventory_path, ansible_config, env_config, test_env):
"""
:type args: IntegrationConfig
:type target: IntegrationTarget
@ -1581,6 +1582,7 @@ def integration_environment(args, target, test_dir, inventory_path, ansible_conf
:type inventory_path: str
:type ansible_config: str | None
:type env_config: CloudEnvironmentConfig | None
:type test_env: IntegrationEnvironment
:rtype: dict[str, str]
"""
env = ansible_environment(args, ansible_config=ansible_config)
@ -1595,6 +1597,8 @@ def integration_environment(args, target, test_dir, inventory_path, ansible_conf
integration = dict(
JUNIT_OUTPUT_DIR=ResultType.JUNIT.path,
JUNIT_TASK_RELATIVE_PATH=test_env.test_dir,
JUNIT_REPLACE_OUT_OF_TREE_PATH='out-of-tree:',
ANSIBLE_CALLBACKS_ENABLED=','.join(sorted(set(callback_plugins))),
ANSIBLE_TEST_CI=args.metadata.ci_provider or get_ci_provider().code,
ANSIBLE_TEST_COVERAGE='check' if args.coverage_check else ('yes' if args.coverage else ''),
@ -1641,7 +1645,7 @@ def command_integration_script(args, target, test_dir, inventory_path, temp_path
if args.verbosity:
cmd.append('-' + ('v' * args.verbosity))
env = integration_environment(args, target, test_dir, test_env.inventory_path, test_env.ansible_config, env_config)
env = integration_environment(args, target, test_dir, test_env.inventory_path, test_env.ansible_config, env_config, test_env)
cwd = os.path.join(test_env.targets_dir, target.relative_path)
env.update(dict(
@ -1749,7 +1753,7 @@ def command_integration_role(args, target, start_at_task, test_dir, inventory_pa
if args.verbosity:
cmd.append('-' + ('v' * args.verbosity))
env = integration_environment(args, target, test_dir, test_env.inventory_path, test_env.ansible_config, env_config)
env = integration_environment(args, target, test_dir, test_env.inventory_path, test_env.ansible_config, env_config, test_env)
cwd = test_env.integration_dir
env.update(dict(

@ -208,7 +208,7 @@ def integration_test_environment(args, target, inventory_path_src):
ansible_config = ansible_config_src
vars_file = os.path.join(data_context().content.root, data_context().content.integration_vars_path)
yield IntegrationEnvironment(integration_dir, targets_dir, inventory_path, ansible_config, vars_file)
yield IntegrationEnvironment(data_context().content.root, integration_dir, targets_dir, inventory_path, ansible_config, vars_file)
return
# When testing a collection, the temporary directory must reside within the collection.
@ -286,7 +286,7 @@ def integration_test_environment(args, target, inventory_path_src):
make_dirs(os.path.dirname(file_dst))
shutil.copy2(file_src, file_dst)
yield IntegrationEnvironment(integration_dir, targets_dir, inventory_path, ansible_config, vars_file)
yield IntegrationEnvironment(temp_dir, integration_dir, targets_dir, inventory_path, ansible_config, vars_file)
finally:
if not args.explain:
shutil.rmtree(temp_dir)
@ -324,7 +324,8 @@ def integration_test_config_file(args, env_config, integration_dir):
class IntegrationEnvironment:
"""Details about the integration environment."""
def __init__(self, integration_dir, targets_dir, inventory_path, ansible_config, vars_file):
def __init__(self, test_dir, integration_dir, targets_dir, inventory_path, ansible_config, vars_file):
self.test_dir = test_dir
self.integration_dir = integration_dir
self.targets_dir = targets_dir
self.inventory_path = inventory_path

Loading…
Cancel
Save