Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
get_transition_forks,
transition_fork_to,
)
from execution_testing.forks.forks.eips import ALL_EIPS
from execution_testing.logging import (
get_logger,
)
Expand Down Expand Up @@ -513,6 +514,14 @@ def pytest_configure(config: pytest.Config) -> None:
"shown in verbose collection output."
),
)
for eip in sorted(ALL_EIPS):
config.addinivalue_line(
"markers",
(
f"{eip.name()}: tests explicitly enabled by "
f"{eip.name()} via valid_from(...)"
),
)
for d in fork_covariant_decorators:
config.addinivalue_line("markers", f"{d.marker_name}: {d.description}")

Expand Down Expand Up @@ -1485,6 +1494,31 @@ def blob_params_changed_at_transition(fork: Fork | TransitionFork) -> bool:
return False


def get_valid_from_eip_marker_names(markers: Iterator[pytest.Mark]) -> Set[str]:
"""
Return explicit EIP names from ``valid_from`` markers.

Only positive EIP-based selectors are promoted to plain pytest markers so
``-m EIP1234`` matches tests explicitly enabled by that EIP.
"""
eip_marker_names: Set[str] = set()
for marker in markers:
if not marker.args:
continue
for fork_or_eip in ForkEIPSetAdapter.validate_python(marker.args):
if not fork_or_eip.is_transition_fork and fork_or_eip.is_eip():
eip_marker_names.add(fork_or_eip.name())
return eip_marker_names


def add_explicit_eip_markers(item: pytest.Item) -> None:
"""Attach plain pytest EIP markers derived from ``valid_from``."""
for eip_marker_name in sorted(
get_valid_from_eip_marker_names(item.iter_markers("valid_from"))
):
item.add_marker(getattr(pytest.mark, eip_marker_name))


def _get_item_params(
item: pytest.Item,
) -> Dict[str, Any] | None:
Expand Down Expand Up @@ -1543,6 +1577,14 @@ def pytest_collection_modifyitems(
filter_stats: Dict[str, Tuple[str, int, int]] = {}

for i, item in enumerate(items):
try:
add_explicit_eip_markers(item)
except Exception as e:
pytest.exit(
f"Error in test '{item.name}': {e}",
returncode=pytest.ExitCode.USAGE_ERROR,
)

params = _get_item_params(item)
if not params:
continue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -521,3 +521,128 @@ def test_param_level_validity_markers(
*pytest_args,
)
result.assert_outcomes(**outcomes)


def generate_function_level_eip_marker_test() -> str:
"""Generate a test with a function-level EIP validity marker."""
return """
import pytest

@pytest.mark.valid_from("EIP3675")
@pytest.mark.state_test_only
def test_function_level_eip_marker(state_test):
pass
"""


def generate_param_level_eip_marker_test() -> str:
"""Generate a test with mixed param-level EIP and fork markers."""
return """
import pytest

@pytest.mark.parametrize(
"value",
[
pytest.param(
True,
id="post_eip",
marks=pytest.mark.valid_from("EIP7928"),
),
pytest.param(
False,
id="post_paris",
marks=pytest.mark.valid_from("Paris"),
),
],
)
@pytest.mark.state_test_only
def test_param_level_eip_marker(state_test, value):
pass
"""


def generate_negative_eip_marker_test() -> str:
"""Generate a test that mentions an EIP only in a negative selector."""
return """
import pytest

@pytest.mark.valid_before("EIP7928")
@pytest.mark.state_test_only
def test_negative_eip_marker(state_test):
pass
"""


def test_function_level_valid_from_eip_is_selectable_by_mark(
pytester: pytest.Pytester,
) -> None:
"""``-m EIP####`` should select tests enabled via function-level valid_from."""
pytester.makepyfile(generate_function_level_eip_marker_test())
pytester.copy_example(
name="src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-fill.ini"
)
result = pytester.runpytest(
"-c",
"pytest-fill.ini",
"--until=Prague",
"--collect-only",
"-q",
"-m",
"EIP3675",
)
assert result.ret == pytest.ExitCode.OK
stdout = "\n".join(result.stdout.lines)
assert "test_function_level_eip_marker[fork_Paris-state_test]" in stdout
assert (
"test_function_level_eip_marker[fork_Shanghai-state_test]" in stdout
)
assert "test_function_level_eip_marker[fork_Cancun-state_test]" in stdout
assert "test_function_level_eip_marker[fork_Prague-state_test]" in stdout


def test_param_level_valid_from_eip_is_selectable_by_mark(
pytester: pytest.Pytester,
) -> None:
"""``-m EIP####`` should select only matching param-level variants."""
pytester.makepyfile(generate_param_level_eip_marker_test())
pytester.copy_example(
name="src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-fill.ini"
)
result = pytester.runpytest(
"-c",
"pytest-fill.ini",
"--until=Amsterdam",
"--collect-only",
"-q",
"-m",
"EIP7928",
)
assert result.ret == pytest.ExitCode.OK
stdout = "\n".join(result.stdout.lines)
assert "test_param_level_eip_marker[fork_Amsterdam-state_test-post_eip]" in stdout
assert (
"test_param_level_eip_marker[fork_Amsterdam-state_test-post_paris]"
not in stdout
)


def test_negative_eip_selectors_do_not_add_eip_markers(
pytester: pytest.Pytester,
) -> None:
"""``valid_before(EIP####)`` should not make a test selectable by that EIP."""
pytester.makepyfile(generate_negative_eip_marker_test())
pytester.copy_example(
name="src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-fill.ini"
)
result = pytester.runpytest(
"-c",
"pytest-fill.ini",
"--until=Amsterdam",
"--collect-only",
"-q",
"-m",
"EIP7928",
)
assert result.ret == pytest.ExitCode.NO_TESTS_COLLECTED
stdout = "\n".join(result.stdout.lines)
assert "test_negative_eip_marker" not in stdout