Skip to content
2 changes: 2 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ Users can select any of the artifacts depending on their testing needs for their
- ✨ Opcode classes now validate keyword arguments and raise `ValueError` with clear error messages ([#1739](https://github.com/ethereum/execution-spec-tests/pull/1739), [#1856](https://github.com/ethereum/execution-spec-tests/pull/1856)).
- ✨ All commands (`fill`, `consume`, `execute`) now work without having to clone the repository, e.g. `uv run --with git+https://github.com/ethereum/execution-spec-tests.git consume` now works from any folder ([#1863](https://github.com/ethereum/execution-spec-tests/pull/1863)).
- 🔀 Move Prague to stable and Osaka to develop ([#1573](https://github.com/ethereum/execution-spec-tests/pull/1573)).
- ✨ Add a `pytest.mark.with_all_typed_transactions` marker that creates default typed transactions for each `tx_type` supported by the current `fork` ([#1890](https://github.com/ethereum/execution-spec-tests/pull/1890)).

### 🧪 Test Cases

Expand All @@ -128,6 +129,7 @@ Users can select any of the artifacts depending on their testing needs for their
- ✨ [EIP-7934](https://eips.ethereum.org/EIPS/eip-7934): Add test cases for the block RLP max limit of 10MiB ([#1730](https://github.com/ethereum/execution-spec-tests/pull/1730)).
- ✨ [EIP-7939](https://eips.ethereum.org/EIPS/eip-7939) Add count leading zeros (CLZ) opcode tests for Osaka ([#1733](https://github.com/ethereum/execution-spec-tests/pull/1733)).
- ✨ [EIP-7918](https://eips.ethereum.org/EIPS/eip-7918): Blob base fee bounded by execution cost test cases (initial), includes some adjustments to [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) tests ([#1685](https://github.com/ethereum/execution-spec-tests/pull/1685)). Update the blob_base_cost ([#1915](https://github.com/ethereum/EIPs/pull/1915)).
- ✨ [EIP-7934](https://eips.ethereum.org/EIPS/eip-7934): Add additional test cases for block RLP max limit with all typed transactions and for a log-creating transactions ([#1890](https://github.com/ethereum/execution-spec-tests/pull/1890)).

## [v4.5.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v4.5.0) - 2025-05-14

Expand Down
53 changes: 53 additions & 0 deletions docs/writing_tests/test_markers.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,59 @@ This marker is used to automatically parameterize a test with all contract creat

This marker only differs from `pytest.mark.with_all_tx_types` in that it does not include transaction type 3 (Blob Transaction type) on fork Cancun and after.

### `@pytest.mark.with_all_typed_transactions`

This marker is used to automatically parameterize a test with all typed transactions, including `type=0` (legacy transaction), that are valid for the fork being tested.
This marker is an indirect marker that utilizes the `tx_type` values from the `pytest.mark.with_all_tx_types` marker to build default typed transactions for each `tx_type`.

Optional: Default typed transactions used as values for `typed_transaction` exist in `src/pytest_plugins/shared/transaction_fixtures.py` and can be overridden for the scope of
the test by re-defining the appropriate `pytest.fixture` for that transaction type.

```python
import pytest

from ethereum_test_tools import Account, Alloc, StateTestFiller
from ethereum_test_types import Transaction

# Optional override for type 2 transaction
@pytest.fixture
def type_2_default_transaction(sender: Account):
return Transaction(
ty=2,
sender=sender,
max_fee_per_gas=0x1337,
max_priority_fee_per_gas=0x1337,
...
)

# Optional override for type 4 transaction
@pytest.fixture
def type_4_default_transaction(sender: Account, pre: Alloc):
return Transaction(
ty=4,
sender=sender,
...,
authorization_list=[
AuthorizationTuple(
address=Address(1234),
nonce=0,
chain_id=1,
signer=pre.fund_eoa(),
)
]
)


@pytest.mark.with_all_typed_transactions
@pytest.mark.valid_from("Prague")
def test_something_with_all_tx_types(
state_test: StateTestFiller,
pre: Alloc,
typed_transaction: Transaction
):
pass
```

### `@pytest.mark.with_all_precompiles`

This marker is used to automatically parameterize a test with all precompiles that are valid for the fork being tested.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ addopts =
-p pytest_plugins.filler.pre_alloc
-p pytest_plugins.filler.filler
-p pytest_plugins.shared.execute_fill
-p pytest_plugins.shared.transaction_fixtures
-p pytest_plugins.forks.forks
-p pytest_plugins.help.help
-m eip_version_check
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ addopts =
-p pytest_plugins.execute.rpc.hive
-p pytest_plugins.execute.execute
-p pytest_plugins.shared.execute_fill
-p pytest_plugins.shared.transaction_fixtures
-p pytest_plugins.forks.forks
-p pytest_plugins.pytest_hive.pytest_hive
-p pytest_plugins.help.help
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ addopts =
-p pytest_plugins.execute.pre_alloc
-p pytest_plugins.execute.execute
-p pytest_plugins.shared.execute_fill
-p pytest_plugins.shared.transaction_fixtures
-p pytest_plugins.execute.rpc.remote_seed_sender
-p pytest_plugins.execute.rpc.remote
-p pytest_plugins.forks.forks
Expand Down
1 change: 1 addition & 0 deletions src/cli/pytest_commands/pytest_ini_files/pytest-fill.ini
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ addopts =
-p pytest_plugins.filler.ported_tests
-p pytest_plugins.filler.static_filler
-p pytest_plugins.shared.execute_fill
-p pytest_plugins.shared.transaction_fixtures
-p pytest_plugins.forks.forks
-p pytest_plugins.eels_resolver
-p pytest_plugins.help.help
Expand Down
40 changes: 35 additions & 5 deletions src/pytest_plugins/forks/forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,13 +257,16 @@ class CovariantDecorator(CovariantDescriptor):
description: Description of the marker.
fork_attribute_name: Name of the method to call on the fork to get the values.
marker_parameter_names: Names of the parameters to be parametrized in the test function.
indirect: Whether the parameters should be passed through fixtures (indirect
parametrization).

"""

marker_name: ClassVar[str]
description: ClassVar[str]
fork_attribute_name: ClassVar[str]
marker_parameter_names: ClassVar[List[str]]
indirect: ClassVar[bool]

def __init__(self, metafunc: Metafunc):
"""
Expand Down Expand Up @@ -317,6 +320,7 @@ def covariant_decorator(
description: str,
fork_attribute_name: str,
argnames: List[str],
indirect: bool = False,
) -> Type[CovariantDecorator]:
"""Generate a new covariant decorator subclass."""
return type(
Expand All @@ -327,6 +331,7 @@ def covariant_decorator(
"description": description,
"fork_attribute_name": fork_attribute_name,
"marker_parameter_names": argnames,
"indirect": indirect,
},
)

Expand All @@ -346,6 +351,16 @@ def covariant_decorator(
fork_attribute_name="contract_creating_tx_types",
argnames=["tx_type"],
),
covariant_decorator(
marker_name="with_all_typed_transactions",
description="marks a test to be parametrized with default typed transactions named "
"typed_transaction",
fork_attribute_name="tx_types",
argnames=["typed_transaction"],
# indirect means the values from `tx_types` will be passed to the
# `typed_transaction` fixture which will then be used in the test
indirect=True,
),
covariant_decorator(
marker_name="with_all_precompiles",
description="marks a test to be parametrized for all precompiles at parameter named"
Expand Down Expand Up @@ -972,10 +987,15 @@ def add_fork_covariant_parameters(
metafunc: Metafunc, fork_parametrizers: List[ForkParametrizer]
) -> None:
"""Iterate over the fork covariant descriptors and add their values to the test function."""
# Process all covariant decorators uniformly
for covariant_descriptor in fork_covariant_decorators:
for fork_parametrizer in fork_parametrizers:
covariant_descriptor(metafunc=metafunc).add_values(fork_parametrizer=fork_parametrizer)
if list(metafunc.definition.iter_markers(covariant_descriptor.marker_name)):
for fork_parametrizer in fork_parametrizers:
covariant_descriptor(metafunc=metafunc).add_values(
fork_parametrizer=fork_parametrizer
)

# Handle custom parametrize_by_fork markers
for marker in metafunc.definition.iter_markers():
if marker.name == "parametrize_by_fork":
descriptor = CovariantDescriptor(
Expand Down Expand Up @@ -1029,6 +1049,16 @@ def parameters_from_fork_parametrizer_list(

def parametrize_fork(metafunc: Metafunc, fork_parametrizers: List[ForkParametrizer]) -> None:
"""Add the fork parameters to the test function."""
metafunc.parametrize(
*parameters_from_fork_parametrizer_list(fork_parametrizers), scope="function"
)
param_names, param_values = parameters_from_fork_parametrizer_list(fork_parametrizers)

# Collect all parameters that should be indirect from the decorators
indirect = []
for covariant_descriptor in fork_covariant_decorators:
if (
list(metafunc.definition.iter_markers(covariant_descriptor.marker_name))
and covariant_descriptor.indirect
):
# Add all argnames from this decorator to indirect list
indirect.extend(covariant_descriptor.marker_parameter_names)

metafunc.parametrize(param_names, param_values, scope="function", indirect=indirect)
73 changes: 73 additions & 0 deletions src/pytest_plugins/forks/tests/test_covariant_markers.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,79 @@ def test_case(state_test, system_contract):
None,
id="with_all_system_contracts",
),
pytest.param(
"""
import pytest
from ethereum_test_tools import Transaction
@pytest.mark.with_all_typed_transactions
@pytest.mark.valid_from("Berlin")
@pytest.mark.valid_until("Berlin")
@pytest.mark.state_test_only
def test_case(state_test, typed_transaction):
assert isinstance(typed_transaction, Transaction)
assert typed_transaction.ty in [0, 1] # Berlin supports types 0 and 1
""",
{"passed": 2, "failed": 0, "skipped": 0, "errors": 0},
None,
id="with_all_typed_transactions_berlin",
),
pytest.param(
"""
import pytest
from ethereum_test_tools import Transaction
@pytest.mark.with_all_typed_transactions()
@pytest.mark.valid_from("London")
@pytest.mark.valid_until("London")
@pytest.mark.state_test_only
def test_case(state_test, typed_transaction, pre):
assert isinstance(typed_transaction, Transaction)
assert typed_transaction.ty in [0, 1, 2] # London supports types 0, 1, 2
""",
{"passed": 3, "failed": 0, "skipped": 0, "errors": 0},
None,
id="with_all_typed_transactions_london",
),
pytest.param(
"""
import pytest
from ethereum_test_tools import Transaction
from ethereum_test_base_types import AccessList

# Override the type 3 transaction fixture
@pytest.fixture
def type_3_default_transaction(pre):
sender = pre.fund_eoa()

return Transaction(
ty=3,
sender=sender,
max_fee_per_gas=10**10,
max_priority_fee_per_gas=10**9,
max_fee_per_blob_gas=10**8,
gas_limit=300_000,
data=b"\\xFF" * 50,
access_list=[
AccessList(address=0x1111, storage_keys=[10, 20]),
],
blob_versioned_hashes=[
0x0111111111111111111111111111111111111111111111111111111111111111,
],
)

@pytest.mark.with_all_typed_transactions()
@pytest.mark.valid_at("Cancun")
@pytest.mark.state_test_only
def test_case(state_test, typed_transaction, pre):
assert isinstance(typed_transaction, Transaction)
if typed_transaction.ty == 3:
# Verify our override worked
assert typed_transaction.data == b"\\xFF" * 50
assert len(typed_transaction.blob_versioned_hashes) == 1
""",
{"passed": 4, "failed": 0, "skipped": 0, "errors": 0},
None,
id="with_all_typed_transactions_with_override",
),
pytest.param(
"""
import pytest
Expand Down
8 changes: 8 additions & 0 deletions src/pytest_plugins/shared/execute_fill.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ethereum_test_execution import BaseExecute, LabeledExecuteFormat
from ethereum_test_fixtures import BaseFixture, LabeledFixtureFormat
from ethereum_test_specs import BaseTest
from ethereum_test_types import EOA, Alloc
from pytest_plugins.spec_version_checker.spec_version_checker import EIPSpecTestItem


Expand Down Expand Up @@ -150,6 +151,13 @@ def __init__(self, message):
)


# Global `sender` fixture that can be overridden by tests.
@pytest.fixture
def sender(pre: Alloc) -> EOA:
"""Fund an EOA from pre-alloc."""
return pre.fund_eoa()


def pytest_addoption(parser: pytest.Parser):
"""Add command-line options to pytest."""
static_filler_group = parser.getgroup("static", "Arguments defining static filler behavior")
Expand Down
Loading
Loading