Skip to content

Commit 48215da

Browse files
marioevzspencer-tb
authored andcommitted
fix(plugins/filler): Index generation concurrency issue (ethereum#725)
* fix(plugins/filler): Index generation concurrency issue * changelog * fix(plugins/filler): tests * fix(plugins/filler): fix parameter group
1 parent 1943dc8 commit 48215da

File tree

4 files changed

+87
-20
lines changed

4 files changed

+87
-20
lines changed

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Test fixtures for use by clients are available for each release on the [Github r
1414
-`consume hive` command is now available to run all types of hive tests ([#712](https://github.com/ethereum/execution-spec-tests/pull/712)).
1515
- ✨ Generated fixtures now contain the test index `index.json` by default ([#716](https://github.com/ethereum/execution-spec-tests/pull/716)).
1616
- ✨ A metadata folder `.meta/` now stores all fixture metadata files by default ([#721](https://github.com/ethereum/execution-spec-tests/pull/721)).
17+
- 🐞 Fixed `fill` command index generation issue due to concurrency ([#725](https://github.com/ethereum/execution-spec-tests/pull/725)).
1718

1819
### 🔧 EVM Tools
1920

src/cli/pytest_commands/fill.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
import sys
6+
from tempfile import TemporaryDirectory
67
from typing import List
78

89
import click
@@ -65,11 +66,12 @@ def fill(
6566
"""
6667
Entry point for the fill command.
6768
"""
68-
result = pytest.main(
69-
handle_fill_command_flags(
70-
pytest_args,
71-
help_flag,
72-
pytest_help_flag,
73-
),
74-
)
69+
with TemporaryDirectory() as temp_dir:
70+
result = pytest.main(
71+
handle_fill_command_flags(
72+
[f"--session-temp-folder={temp_dir}", "--index", *pytest_args],
73+
help_flag,
74+
pytest_help_flag,
75+
),
76+
)
7577
sys.exit(result)

src/pytest_plugins/filler/filler.py

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
and that modifies pytest hooks in order to fill test specs for all tests and
66
writes the generated fixtures to file.
77
"""
8-
8+
import argparse
99
import configparser
1010
import datetime
1111
import os
@@ -15,6 +15,7 @@
1515
from typing import Generator, List, Type
1616

1717
import pytest
18+
from filelock import FileLock
1819
from pytest_metadata.plugin import metadata_key # type: ignore
1920

2021
from cli.gen_index import generate_fixtures_index
@@ -185,6 +186,13 @@ def pytest_addoption(parser: pytest.Parser):
185186
type=str,
186187
help="Specify a build name for the fixtures.ini file, e.g., 'stable'.",
187188
)
189+
test_group.addoption(
190+
"--index",
191+
action="store_true",
192+
dest="generate_index",
193+
default=False,
194+
help="Generate an index file for all produced fixtures.",
195+
)
188196

189197
debug_group = parser.getgroup("debug", "Arguments defining debug behavior")
190198
debug_group.addoption(
@@ -196,6 +204,16 @@ def pytest_addoption(parser: pytest.Parser):
196204
help="Path to dump the transition tool debug output.",
197205
)
198206

207+
internal_group = parser.getgroup("internal", "Internal arguments")
208+
internal_group.addoption(
209+
"--session-temp-folder",
210+
action="store",
211+
dest="session_temp_folder",
212+
type=Path,
213+
default=None,
214+
help=argparse.SUPPRESS,
215+
)
216+
199217

200218
@pytest.hookimpl(tryfirst=True)
201219
def pytest_configure(config):
@@ -610,6 +628,16 @@ def get_fixture_collection_scope(fixture_name, config):
610628
return "module"
611629

612630

631+
@pytest.fixture(scope="session")
632+
def session_temp_folder(request) -> Path | None: # noqa: D103
633+
return request.config.option.session_temp_folder
634+
635+
636+
@pytest.fixture(scope="session")
637+
def generate_index(request) -> bool: # noqa: D103
638+
return request.config.option.generate_index
639+
640+
613641
@pytest.fixture(scope=get_fixture_collection_scope)
614642
def fixture_collector(
615643
request: pytest.FixtureRequest,
@@ -618,11 +646,29 @@ def fixture_collector(
618646
filler_path: Path,
619647
base_dump_dir: Path | None,
620648
output_dir: Path,
649+
session_temp_folder: Path | None,
650+
generate_index: bool,
621651
) -> Generator[FixtureCollector, None, None]:
622652
"""
623653
Returns the configured fixture collector instance used for all tests
624654
in one test module.
625655
"""
656+
if session_temp_folder is not None:
657+
fixture_collector_count_file_name = "fixture_collector_count"
658+
fixture_collector_count_file = session_temp_folder / fixture_collector_count_file_name
659+
fixture_collector_count_file_lock = (
660+
session_temp_folder / f"{fixture_collector_count_file_name}.lock"
661+
)
662+
with FileLock(fixture_collector_count_file_lock):
663+
if fixture_collector_count_file.exists():
664+
with open(fixture_collector_count_file, "r") as f:
665+
fixture_collector_count = int(f.read())
666+
else:
667+
fixture_collector_count = 0
668+
fixture_collector_count += 1
669+
with open(fixture_collector_count_file, "w") as f:
670+
f.write(str(fixture_collector_count))
671+
626672
fixture_collector = FixtureCollector(
627673
output_dir=output_dir,
628674
flat_output=request.config.getoption("flat_output"),
@@ -634,9 +680,19 @@ def fixture_collector(
634680
fixture_collector.dump_fixtures()
635681
if do_fixture_verification:
636682
fixture_collector.verify_fixture_files(evm_fixture_verification)
637-
generate_fixtures_index(
638-
output_dir, quiet_mode=True, force_flag=True, disable_infer_format=False
639-
)
683+
684+
fixture_collector_count = 0
685+
if session_temp_folder is not None:
686+
with FileLock(fixture_collector_count_file_lock):
687+
with open(fixture_collector_count_file, "r") as f:
688+
fixture_collector_count = int(f.read())
689+
fixture_collector_count -= 1
690+
with open(fixture_collector_count_file, "w") as f:
691+
f.write(str(fixture_collector_count))
692+
if generate_index and fixture_collector_count == 0:
693+
generate_fixtures_index(
694+
output_dir, quiet_mode=True, force_flag=True, disable_infer_format=False
695+
)
640696

641697

642698
@pytest.fixture(autouse=True, scope="session")

src/pytest_plugins/filler/tests/test_filler.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def test_shanghai_two(state_test, x):
7777

7878

7979
@pytest.mark.parametrize(
80-
"args, expected_fixture_files, expected_fixture_counts",
80+
"args, expected_fixture_files, expected_fixture_counts, expected_index",
8181
[
8282
pytest.param(
8383
[],
@@ -100,10 +100,11 @@ def test_shanghai_two(state_test, x):
100100
Path("fixtures/state_tests/shanghai/module_shanghai/shanghai_two.json"),
101101
],
102102
[2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6],
103+
False,
103104
id="default-args",
104105
),
105106
pytest.param(
106-
["--build-name", "test_build"],
107+
["--index", "--build-name", "test_build"],
107108
[
108109
Path("fixtures/blockchain_tests/paris/module_paris/paris_one.json"),
109110
Path("fixtures/blockchain_tests_engine/paris/module_paris/paris_one.json"),
@@ -123,10 +124,11 @@ def test_shanghai_two(state_test, x):
123124
Path("fixtures/state_tests/shanghai/module_shanghai/shanghai_two.json"),
124125
],
125126
[2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6],
127+
True,
126128
id="build-name-in-fixtures-ini-file",
127129
),
128130
pytest.param(
129-
["--flat-output"],
131+
["--flat-output", "--index"],
130132
[
131133
Path("fixtures/blockchain_tests/paris_one.json"),
132134
Path("fixtures/blockchain_tests_engine/paris_one.json"),
@@ -142,10 +144,11 @@ def test_shanghai_two(state_test, x):
142144
Path("fixtures/state_tests/shanghai_two.json"),
143145
],
144146
[2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6],
147+
True,
145148
id="flat-output",
146149
),
147150
pytest.param(
148-
["--flat-output", "--output", "other_fixtures"],
151+
["--flat-output", "--index", "--output", "other_fixtures"],
149152
[
150153
Path("other_fixtures/blockchain_tests/paris_one.json"),
151154
Path("other_fixtures/blockchain_tests_engine/paris_one.json"),
@@ -161,10 +164,11 @@ def test_shanghai_two(state_test, x):
161164
Path("other_fixtures/state_tests/shanghai_two.json"),
162165
],
163166
[2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6],
167+
True,
164168
id="flat-output_custom-output-dir",
165169
),
166170
pytest.param(
167-
["--single-fixture-per-file"],
171+
["--single-fixture-per-file", "--index"],
168172
[
169173
Path(
170174
"fixtures/blockchain_tests/paris/module_paris/paris_one__fork_Paris_blockchain_test.json"
@@ -276,10 +280,11 @@ def test_shanghai_two(state_test, x):
276280
),
277281
],
278282
[1] * 36,
283+
True,
279284
id="single-fixture-per-file",
280285
),
281286
pytest.param(
282-
["--single-fixture-per-file", "--output", "other_fixtures"],
287+
["--single-fixture-per-file", "--index", "--output", "other_fixtures"],
283288
[
284289
Path(
285290
"other_fixtures/blockchain_tests/paris/module_paris/paris_one__fork_Paris_blockchain_test.json"
@@ -391,10 +396,11 @@ def test_shanghai_two(state_test, x):
391396
),
392397
],
393398
[1] * 36,
399+
True,
394400
id="single-fixture-per-file_custom_output_dir",
395401
),
396402
pytest.param(
397-
["--flat-output", "--single-fixture-per-file"],
403+
["--flat-output", "--index", "--single-fixture-per-file"],
398404
[
399405
Path("fixtures/blockchain_tests/paris_one__fork_Paris_blockchain_test.json"),
400406
Path("fixtures/state_tests/paris_one__fork_Paris_state_test.json"),
@@ -470,12 +476,13 @@ def test_shanghai_two(state_test, x):
470476
),
471477
],
472478
[1] * 36,
479+
True,
473480
id="flat-single-per-file_flat-output",
474481
),
475482
],
476483
)
477484
def test_fixture_output_based_on_command_line_args(
478-
testdir, args, expected_fixture_files, expected_fixture_counts
485+
testdir, args, expected_fixture_files, expected_fixture_counts, expected_index
479486
):
480487
"""
481488
Test:
@@ -555,7 +562,8 @@ def test_fixture_output_based_on_command_line_args(
555562
config = configparser.ConfigParser()
556563
config.read(ini_file)
557564

558-
assert index_file is not None, f"No {expected_index_file} file was found in {meta_dir}"
565+
if expected_index:
566+
assert index_file is not None, f"No {expected_index_file} file was found in {meta_dir}"
559567

560568
properties = {key: value for key, value in config.items("fixtures")}
561569
assert "timestamp" in properties

0 commit comments

Comments
 (0)