Skip to content

Commit 0a42a18

Browse files
feat(plugins/forks): Fix covariant markers, add selector/marks parameters (#762)
* fix(plugins/forks): fix case where the covariant fork method returns tuple or list * fix(plugins/forks): Disallow args in `with_all` markers * fix(plugins/forks): Add test * feat(plugins/forks): Fix covariant markers * feat(plugins/forks): Support for markers on covariant values * tests(filler): check correct error string is reported * test(forks): add marks_selector tests for `with_all` decorators * tests(forks): remove bad test case from previous commit * Apply suggestions from code review Co-authored-by: danceratopz <[email protected]> * refactor(forks): Move helpers to forks library * fix(plugins/forks): Fix `with_all` `marks` field * feat(docs): Add `marks` field to docs * fix(docs): tox * docs: changelog --------- Co-authored-by: danceratopz <[email protected]>
1 parent b8d318f commit 0a42a18

File tree

9 files changed

+630
-184
lines changed

9 files changed

+630
-184
lines changed

docs/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ Test fixtures for use by clients are available for each release on the [Github r
3232
- ✨ Added `Container.Init` to `ethereum_test_types.EOF.V1` package, which allows generation of an EOF init container more easily ([#739](https://github.com/ethereum/execution-spec-tests/pull/739)).
3333
- ✨ Introduce method valid_opcodes() to the fork class ([#748](https://github.com/ethereum/execution-spec-tests/pull/748)).
3434
- 🐞 Fixed `consume` exit code return values, ensuring that pytest's return value is correctly propagated to the shell. This allows the shell to accurately reflect the test results (e.g., failures) based on the pytest exit code ([#765](https://github.com/ethereum/execution-spec-tests/pull/765)).
35-
- ✨ Added a new flag `--solc-version` to the `fill` command, which allows the user to specify the version of the Solidity compiler to use when compiling Yul source code; this version will now be automatically downloaded by `fill` via [`solc-select`](https://github.com/crytic/solc-select) ([#772](https://github.com/ethereum/execution-spec-tests/pull/772).
35+
- ✨ Added a new flag `--solc-version` to the `fill` command, which allows the user to specify the version of the Solidity compiler to use when compiling Yul source code; this version will now be automatically downloaded by `fill` via [`solc-select`](https://github.com/crytic/solc-select) ([#772](https://github.com/ethereum/execution-spec-tests/pull/772)).
36+
- 🐞 Fix usage of multiple `@pytest.mark.with_all*` markers which shared parameters, such as `with_all_call_opcodes` and `with_all_create_opcodes` which shared `evm_code_type`, and now only parametrize compatible values ([#762](https://github.com/ethereum/execution-spec-tests/pull/762)).
37+
- ✨ Added `selector` and `marks` fields to all `@pytest.mark.with_all*` markers, which allows passing lambda functions to select or mark specific parametrized values (see [documentation](https://ethereum.github.io/execution-spec-tests/main/writing_tests/test_markers/#covariant-marker-keyword-arguments) for more information) ([#762](https://github.com/ethereum/execution-spec-tests/pull/762)).
3638

3739
### 🔧 EVM Tools
3840

docs/writing_tests/test_markers.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,24 @@ def test_something_with_all_tx_types(
253253

254254
Ideally, the lambda function should be used to explicitly filter out values that are not compatible with the test (exclusive filter), rather than explicitly selecting values (inclusive filter), as the parametrized values might change with future forks.
255255

256+
#### `marks`
257+
258+
A marker, list of markers, or a lambda function that can be used to add additional markers to the test.
259+
260+
```python
261+
import pytest
262+
263+
@pytest.mark.with_all_tx_types(
264+
marks=lambda tx_type: pytest.mark.skip("incompatible") if tx_type == 1 else None,
265+
)
266+
@pytest.mark.valid_from("London")
267+
def test_something_with_all_tx_types_but_skip_type_1(state_test_only, tx_type):
268+
assert tx_type != 1
269+
...
270+
```
271+
272+
In this example, the test will be skipped if `tx_type` is equal to 1 by returning a `pytest.mark.skip` marker, and return `None` otherwise.
273+
256274
## Other Markers
257275

258276
### `@pytest.mark.slow`

src/ethereum_test_forks/__init__.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,12 @@
3333
get_deployed_forks,
3434
get_development_forks,
3535
get_forks,
36+
get_forks_with_no_descendants,
37+
get_forks_with_no_parents,
3638
get_forks_with_solc_support,
3739
get_forks_without_solc_support,
40+
get_from_until_fork_set,
41+
get_last_descendants,
3842
get_transition_forks,
3943
transition_fork_from_to,
4044
transition_fork_to,
@@ -65,12 +69,16 @@
6569
"get_transition_forks",
6670
"forks_from",
6771
"forks_from_until",
72+
"get_closest_fork_with_solc_support",
6873
"get_deployed_forks",
6974
"get_development_forks",
70-
"get_forks",
75+
"get_forks_with_no_descendants",
76+
"get_forks_with_no_parents",
7177
"get_forks_with_solc_support",
7278
"get_forks_without_solc_support",
73-
"get_closest_fork_with_solc_support",
79+
"get_forks",
80+
"get_from_until_fork_set",
81+
"get_last_descendants",
7482
"transition_fork_from_to",
7583
"transition_fork_to",
7684
]

src/ethereum_test_forks/helpers.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
Helper methods to resolve forks during test filling
33
"""
4-
from typing import List, Optional
4+
from typing import List, Optional, Set
55

66
from semver import Version
77

@@ -103,6 +103,66 @@ def get_transition_forks() -> List[Fork]:
103103
return transition_forks
104104

105105

106+
def get_from_until_fork_set(
107+
forks: Set[Fork], forks_from: Set[Fork], forks_until: Set[Fork]
108+
) -> Set[Fork]:
109+
"""
110+
Get the fork range from forks_from to forks_until.
111+
"""
112+
resulting_set = set()
113+
for fork_from in forks_from:
114+
for fork_until in forks_until:
115+
for fork in forks:
116+
if fork <= fork_until and fork >= fork_from:
117+
resulting_set.add(fork)
118+
return resulting_set
119+
120+
121+
def get_forks_with_no_parents(forks: Set[Fork]) -> Set[Fork]:
122+
"""
123+
Get the forks with no parents in the inheritance hierarchy.
124+
"""
125+
resulting_forks: Set[Fork] = set()
126+
for fork in forks:
127+
parents = False
128+
for next_fork in forks - {fork}:
129+
if next_fork < fork:
130+
parents = True
131+
break
132+
if not parents:
133+
resulting_forks = resulting_forks | {fork}
134+
return resulting_forks
135+
136+
137+
def get_forks_with_no_descendants(forks: Set[Fork]) -> Set[Fork]:
138+
"""
139+
Get the forks with no descendants in the inheritance hierarchy.
140+
"""
141+
resulting_forks: Set[Fork] = set()
142+
for fork in forks:
143+
descendants = False
144+
for next_fork in forks - {fork}:
145+
if next_fork > fork:
146+
descendants = True
147+
break
148+
if not descendants:
149+
resulting_forks = resulting_forks | {fork}
150+
return resulting_forks
151+
152+
153+
def get_last_descendants(forks: Set[Fork], forks_from: Set[Fork]) -> Set[Fork]:
154+
"""
155+
Get the last descendant of a class in the inheritance hierarchy.
156+
"""
157+
resulting_forks: Set[Fork] = set()
158+
forks = get_forks_with_no_descendants(forks)
159+
for fork_from in forks_from:
160+
for fork in forks:
161+
if fork >= fork_from:
162+
resulting_forks = resulting_forks | {fork}
163+
return resulting_forks
164+
165+
106166
def transition_fork_from_to(fork_from: Fork, fork_to: Fork) -> Fork | None:
107167
"""
108168
Returns the transition fork that transitions to and from the specified

0 commit comments

Comments
 (0)