From c2283aa4052c81650ba82e45294e552dc3bab009 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 27 Apr 2026 14:05:37 +0200 Subject: [PATCH] Add hints to pytest failures --- tests/collectors.py | 20 +++++--------------- tests/conftest.py | 20 ++++++++++++++++++++ tests/examples/test_plan.py | 14 +++++++++++--- tests/examples/utils.py | 8 +++++--- tools/generate_plan_summary.py | 2 -- 5 files changed, 41 insertions(+), 23 deletions(-) diff --git a/tests/collectors.py b/tests/collectors.py index 87f89b03b..c28232804 100644 --- a/tests/collectors.py +++ b/tests/collectors.py @@ -1,4 +1,4 @@ -# Copyright 2024 Google LLC +# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -111,23 +111,13 @@ class FabricTestItem(pytest.Item): return [str(root_path / x) for x in paths] files_root = self.parent.path.parent - # extra_dirs and extra_files need additional .parent - extra_dirs = [ - f"--extra-dirs={x}" - for x in full_paths(files_root.parent, self.extra_dirs) - ] - extra_files = [ - f"--extra-files={x}" - for x in full_paths(files_root.parent, self.extra_files) - ] print( f'Error in inventory file: {" ".join(full_paths(files_root, self.inventory))}' ) - print(f'To regenerate inventory run: python tools/plan_summary.py ' - f'{" ".join(extra_dirs)} ' - f'{" ".join(extra_files)} ' - f'{self.module} ' - f'{" ".join(full_paths(files_root, self.tf_var_files))}') + test_name = self.name.split('[')[0] + print( + f'To regenerate inventory run:\nuv run tools/generate_plan_summary.py ' + f'{self.parent.path.relative_to(_REPO_ROOT)} {test_name} --save') raise def reportinfo(self): diff --git a/tests/conftest.py b/tests/conftest.py index 58e117e69..049c1672d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,9 +13,29 @@ # limitations under the License. 'Pytest configuration.' +import itertools import pytest pytest_plugins = ( 'tests.fixtures', 'tests.collectors', ) + + +def pytest_terminal_summary(terminalreporter, exitstatus, config): + failed_reports = terminalreporter.stats.get('failed', []) + commands = [] + for rep in failed_reports: + capstdout = getattr(rep, 'capstdout', '') + lines = capstdout.splitlines() + for line, next_line in itertools.pairwise(lines): + if line.strip() == 'To regenerate inventory run:': + commands.append(next_line.strip()) + + if commands: + terminalreporter.ensure_newline() + terminalreporter.section( + 'Commands to regenerate inventories for failed tests', sep='=', + bold=True) + for cmd in sorted(set(commands)): + terminalreporter.write_line(cmd) diff --git a/tests/examples/test_plan.py b/tests/examples/test_plan.py index fc96d08c1..42f72718c 100644 --- a/tests/examples/test_plan.py +++ b/tests/examples/test_plan.py @@ -1,4 +1,4 @@ -# Copyright 2024 Google LLC +# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -81,8 +81,16 @@ def _test_terraform_example(plan_validator, example): inventory = BASE_PATH.parent / python_test_path / 'examples' inventory = inventory / directive.kwargs['inventory'] - summary = plan_validator(module_path=tmp_path, inventory_paths=inventory, - tf_var_files=tf_var_files) + try: + summary = plan_validator(module_path=tmp_path, inventory_paths=inventory, + tf_var_files=tf_var_files) + except AssertionError: + repo_root = BASE_PATH.parents[1] + readme_rel = example.readme_path.relative_to(repo_root) + print( + f'To regenerate inventory run:\nuv run tools/generate_plan_summary.py ' + f'{readme_rel} "{example.header}" --save') + raise print('\n') print(yaml.dump({'values': summary.values})) diff --git a/tests/examples/utils.py b/tests/examples/utils.py index a597f6bb7..cc582de87 100644 --- a/tests/examples/utils.py +++ b/tests/examples/utils.py @@ -1,4 +1,4 @@ -# Copyright 2024 Google LLC +# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,7 +20,8 @@ import marko Directive = collections.namedtuple('Directive', 'name args kwargs') TerraformExample = collections.namedtuple( - 'TerraformExample', 'name code module files fixtures type directive') + 'TerraformExample', + 'name code module files fixtures type directive readme_path header index') YamlExample = collections.namedtuple('YamlExample', 'body module schema directive') File = collections.namedtuple('File', 'path content') @@ -95,7 +96,8 @@ def get_readme_examples(readme_path, fabric_root): marks.append('serial') example = TerraformExample(name, code, path, files[last_header], - fixtures, child.lang, directive) + fixtures, child.lang, directive, readme_path, + last_header, index) examples.append((example, example_id, marks, last_header, index)) elif child.lang == "yaml": schema = directive.kwargs.get('schema') diff --git a/tools/generate_plan_summary.py b/tools/generate_plan_summary.py index a21637f2a..df349108b 100755 --- a/tools/generate_plan_summary.py +++ b/tools/generate_plan_summary.py @@ -85,9 +85,7 @@ def output_summary(summary, inventory_path, save): print(f"Inventory saved to {inventory_path}") else: print(yaml.dump({'values': values})) - print() print(yaml.dump({'counts': summary.counts})) - print() print(yaml.dump({'outputs': outputs}))