diff --git a/test/runner/lib/classification.py b/test/runner/lib/classification.py index f94ce79eb4..5cc72b0c23 100644 --- a/test/runner/lib/classification.py +++ b/test/runner/lib/classification.py @@ -12,6 +12,7 @@ from lib.target import ( walk_compile_targets, walk_sanity_targets, load_integration_prefixes, + analyze_integration_target_dependencies, ) from lib.util import ( @@ -125,6 +126,7 @@ class PathMapper(object): if 'network/' in t.aliases for m in t.modules) self.prefixes = load_integration_prefixes() + self.integration_dependencies = analyze_integration_target_dependencies(self.integration_targets) self.python_module_utils_imports = {} # populated on first use to reduce overhead when not needed @@ -139,6 +141,9 @@ class PathMapper(object): if ext == '.py': return self.get_python_module_utils_usage(path) + if path.startswith('test/integration/targets/'): + return self.get_integration_target_usage(path) + return [] def get_python_module_utils_usage(self, path): @@ -163,6 +168,16 @@ class PathMapper(object): return sorted(self.python_module_utils_imports[name]) + def get_integration_target_usage(self, path): + """ + :type path: str + :rtype: list[str] + """ + target_name = path.split('/')[3] + dependents = [os.path.join('test/integration/targets/%s/' % target) for target in sorted(self.integration_dependencies.get(target_name, set()))] + + return dependents + def classify(self, path): """ :type path: str @@ -339,6 +354,9 @@ class PathMapper(object): target = self.integration_targets_by_name[path.split('/')[3]] if 'hidden/' in target.aliases: + if target.type == 'role': + return minimal # already expanded using get_dependent_paths + return { 'integration': 'all', 'windows-integration': 'all', diff --git a/test/runner/lib/target.py b/test/runner/lib/target.py index b1a86ac3dd..b3875ae03f 100644 --- a/test/runner/lib/target.py +++ b/test/runner/lib/target.py @@ -310,6 +310,43 @@ def walk_test_targets(path=None, module_path=None, extensions=None, prefix=None) yield TestTarget(os.path.join(root, file_name), module_path, prefix, path) +def analyze_integration_target_dependencies(integration_targets): + """ + :type: list[IntegrationTarget] + :rtype: dict[str,set[str]] + """ + hidden_role_target_names = set(t.name for t in integration_targets if t.type == 'role' and 'hidden/' in t.aliases) + normal_role_targets = [t for t in integration_targets if t.type == 'role' and 'hidden/' not in t.aliases] + dependencies = dict((target_name, set()) for target_name in hidden_role_target_names) + + # intentionally primitive analysis of role meta to avoid a dependency on pyyaml + for role_target in normal_role_targets: + meta_dir = os.path.join(role_target.path, 'meta') + + if not os.path.isdir(meta_dir): + continue + + meta_paths = sorted([os.path.join(meta_dir, name) for name in os.listdir(meta_dir)]) + + for meta_path in meta_paths: + if os.path.exists(meta_path): + with open(meta_path, 'r') as meta_fd: + meta_lines = meta_fd.read().splitlines() + + for meta_line in meta_lines: + if re.search(r'^ *#.*$', meta_line): + continue + + if not meta_line.strip(): + continue + + for hidden_target_name in hidden_role_target_names: + if hidden_target_name in meta_line: + dependencies[hidden_target_name].add(role_target.name) + + return dependencies + + class CompletionTarget(object): """Command-line argument completion target base class.""" __metaclass__ = abc.ABCMeta @@ -435,7 +472,7 @@ class IntegrationTarget(CompletionTarget): self.script_path = os.path.join(path, runme_files[0]) elif test_files: self.type = 'special' - elif os.path.isdir(os.path.join(path, 'tasks')): + elif os.path.isdir(os.path.join(path, 'tasks')) or os.path.isdir(os.path.join(path, 'defaults')): self.type = 'role' else: self.type = 'unknown'