Skip to content

chore(fill): delete output directory if no tests were ran #2037

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 37 additions & 6 deletions src/pytest_plugins/filler/filler.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import configparser
import datetime
import os
import shutil
import warnings
from enum import Enum
from pathlib import Path
Expand Down Expand Up @@ -431,9 +432,28 @@ def pytest_terminal_summary(
Emphasize that fixtures have only been filled; they must now be executed to
actually run the tests.
"""
yield
if config.fixture_output.is_stdout or hasattr(config, "workerinput"): # type: ignore[attr-defined]
yield
return
# if no tests were collected, suppress the "Generated html report" message;
# the html report will be deleted in pytest_sessionfinish hook
session = terminalreporter._session
if getattr(session, "testscollected", 0) == 0:
orig_write_sep = terminalreporter.write_sep

def filtered_write_sep(sep, msg, **kwargs):
if isinstance(msg, str) and "Generated html report:" in msg:
return # swallow it
return orig_write_sep(sep, msg, **kwargs)

terminalreporter.write_sep = filtered_write_sep # type: ignore
try:
yield # let all other terminal_summary hooks run
finally:
terminalreporter.write_sep = orig_write_sep # type: ignore
else:
# if tests ran, no action needed, just yield to run the rest of the hooks
yield
stats = terminalreporter.stats
if "passed" in stats and stats["passed"]:
# Custom message for Phase 1 (pre-allocation group generation)
Expand Down Expand Up @@ -1162,6 +1182,7 @@ def pytest_collection_modifyitems(
items.pop(i)


@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_sessionfinish(session: pytest.Session, exitstatus: int):
"""
Perform session finish tasks.
Expand All @@ -1179,25 +1200,35 @@ def pytest_sessionfinish(session: pytest.Session, exitstatus: int):
pre_alloc_groups_folder = fixture_output.pre_alloc_groups_folder_path
pre_alloc_groups_folder.mkdir(parents=True, exist_ok=True)
session.config.pre_alloc_groups.to_folder(pre_alloc_groups_folder)
return

if xdist.is_xdist_worker(session):
yield
return

if fixture_output.is_stdout or is_help_or_collectonly_mode(session.config):
yield
return

# Remove any lock files that may have been created.
for file in fixture_output.directory.rglob("*.lock"):
file.unlink()

# Generate index file for all produced fixtures.
yield # ensure that the following runs after pytest-html's pytest_sessionfinish hook

# Generate index file for all produced fixtures, respectively remove output dir.
if session.config.getoption("generate_index") and not session.config.getoption(
"generate_pre_alloc_groups"
):
generate_fixtures_index(
fixture_output.directory, quiet_mode=True, force_flag=False, disable_infer_format=False
)
amount_of_collected_tests = getattr(session, "testscollected", 0)
if amount_of_collected_tests > 0:
generate_fixtures_index(
fixture_output.directory,
quiet_mode=True,
force_flag=False,
disable_infer_format=False,
)
else:
shutil.rmtree(fixture_output.output_path)

# Create tarball of the output directory if the output is a tarball.
fixture_output.create_tarball()
97 changes: 97 additions & 0 deletions src/pytest_plugins/filler/tests/test_filler.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,103 @@ def test_fixture_output_based_on_command_line_args(
assert properties["build"] == build_name


test_module_no_matching_tests = textwrap.dedent(
"""\
import pytest

from ethereum_test_tools import Account, Environment, TestAddress, Transaction

@pytest.mark.valid_from("Paris")
def test_will_not_match_filter(state_test):
state_test(env=Environment(),
pre={TestAddress: Account(balance=1_000_000)}, post={}, tx=Transaction())
"""
)


def test_no_tests_collected_deletes_output_directory(testdir, default_t8n):
"""
Test that when no tests are collected/run, the output directory is deleted.
"""
tests_dir = testdir.mkdir("tests")

test_module = tests_dir.join("test_no_match.py")
test_module.write(test_module_no_matching_tests)

testdir.copy_example(name="src/cli/pytest_commands/pytest_ini_files/pytest-fill.ini")
args = [
"-c",
"pytest-fill.ini",
"-v",
"--t8n-server-url",
default_t8n.server_url,
"-k",
"no_such_test_contains_this_filter", # Filter that won't match any test
]

result = testdir.runpytest(*args)
result.assert_outcomes(
passed=0,
failed=0,
skipped=0,
errors=0,
)

# Check that no tests were selected (all were deselected)
assert "0 selected" in result.stdout.str()

# The output directory should be deleted when no tests ran
output_dir = Path(default_output_directory()).absolute()
assert not output_dir.exists(), (
f"Output directory {output_dir} should have been deleted when no tests ran"
)


def test_html_report_message_suppressed_when_no_tests_ran(testdir, default_t8n):
"""
Test that the HTML report message is suppressed when no tests are collected/run.
"""
tests_dir = testdir.mkdir("tests")

test_module = tests_dir.join("test_no_match.py")
test_module.write(test_module_no_matching_tests)

testdir.copy_example(name="src/cli/pytest_commands/pytest_ini_files/pytest-fill.ini")
args = [
"-c",
"pytest-fill.ini",
"-v",
"--t8n-server-url",
default_t8n.server_url,
"-k",
"no_such_test_contains_this_filter", # Filter that won't match any test
]

result = testdir.runpytest(*args)
result.assert_outcomes(
passed=0,
failed=0,
skipped=0,
errors=0,
)

# Check that no tests were selected (all were deselected)
assert "0 selected" in result.stdout.str()

# The HTML report message should NOT be present when no tests ran
# (even though HTML reporting is enabled, the message should be suppressed)
stdout_str = result.stdout.str()
assert "Generated html report:" not in stdout_str, (
"HTML report message should be suppressed when no tests ran"
)

# The output directory should still be deleted when no tests ran
output_dir = Path(default_output_directory()).absolute()
assert not output_dir.exists(), (
f"Output directory {output_dir} should have been deleted when no tests ran"
)


test_module_environment_variables = textwrap.dedent(
"""\
import pytest
Expand Down