Skip to content

Commit 8a85b54

Browse files
authored
feat(test-fill): enable phase-1-only pre-alloc generation (#2720)
* refactor(test-fill): allow phase-1-only pre-alloc generation Re-order `FillCommand.create_executions` so that the execution plan reflects the user's intent: - `--use-pre-alloc-groups` now takes priority. The flag means pre-alloc groups already exist on disk from a previous run, so even alongside `--generate-all-formats` the run is single-phase. - `--generate-pre-alloc-groups` without `--generate-all-formats` now runs phase 1 only; don't continue to phase 2. - `--generate-all-formats` continues to trigger the full two-phase run. Update `test_legacy_generate_pre_alloc_groups_still_works` to reflect the new phase-1-only behaviour and add a test covering the `--use-pre-alloc-groups` priority. * test(test-fill): cover contradictory `--use-pre-alloc-groups` flag pairs Add failing tests asserting that `FillCommand.create_executions` raises `click.UsageError` when `--use-pre-alloc-groups` is combined with: - `--generate-pre-alloc-groups`: the first asserts the groups already exist on disk, the second regenerates them: contradictory intents. - `--clean`: `--clean` wipes the output directory that holds the pre-alloc groups the user is asserting exist. Without a guard, `FillCommand.create_executions` silently returns a single-phase plan and `FillingSession._load_pre_alloc_groups_from_folder` then fails on a missing folder. Validation logic follows in the next commit. * fix(test-fill): reject contradictory `--use-pre-alloc-groups` flag pairs Add `_validate_flag_combinations` to `FillCommand.create_executions` so contradictory flag pairs fail fast with `click.UsageError` instead of silently falling through to a broken single-phase execution. Rejected combinations: - `--use-pre-alloc-groups` + `--generate-pre-alloc-groups`: the first asserts the groups already exist, the second regenerates them. - `--use-pre-alloc-groups` + `--clean`: `--clean` wipes the output directory that holds the pre-alloc groups. `--use-pre-alloc-groups` + `--generate-all-formats` remains valid and single-phase, per the intent stated in 116e5093e83. * refactor(tooling): restore `bench-gas` two-phase fill Swap `--generate-pre-alloc-groups` for `--generate-all-formats` in the `bench-gas` recipe. After 116e5093e83, `--generate-pre-alloc-groups` alone runs phase 1 only and produces no benchmark fixtures; the recipe needs the full two-phase run to generate the `BlockchainEngineXFixture` output it targets. Matches the flag used in `.github/configs/feature.yaml`. * doc(test-fill): refresh pre-alloc flag docs Update documentation to reflect the phase-1-only behaviour of `--generate-pre-alloc-groups` introduced in 116e5093e83: - Remove the "Alternative approach" note in `filling_tests_command_line.md` that recommended `--generate-pre-alloc-groups` as a single-command replacement for `--generate-all-formats`: it now produces no fixtures. - Reframe the `blockchain_test_engine_x.md` usage notes to describe the two-invocation workflow (`--generate-pre-alloc-groups` then `--use-pre-alloc-groups`) as the explicit alternative to the single-command `--generate-all-formats` path.
1 parent 739ecad commit 8a85b54

5 files changed

Lines changed: 136 additions & 61 deletions

File tree

Justfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ bench-gas *args:
220220
uv run fill \
221221
--evm-bin="{{ evm_bin }}" \
222222
--gas-benchmark-values 1 \
223-
--generate-pre-alloc-groups \
223+
--generate-all-formats \
224224
--fork Osaka \
225225
-m "not slow" \
226226
-n auto --maxprocesses 10 --dist=loadgroup \

docs/filling_tests/filling_tests_command_line.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,6 @@ This flag automatically performs a two-phase execution:
107107
uv run fill --generate-all-formats --output=fixtures.tar.gz tests/shanghai/
108108
```
109109

110-
!!! note "Alternative approach"
111-
You can still use the legacy approach, but this will only generate the `BlockchainEngineXFixture` format:
112-
```console
113-
# Single command that automatically does 2-phase execution
114-
# but only generates BlockchainEngineXFixture
115-
uv run fill --generate-pre-alloc-groups tests/shanghai/
116-
```
117-
118110
## Debugging the `t8n` Command
119111

120112
The `--evm-dump-dir` flag can be used to dump the inputs and outputs of every call made to the `t8n` command for debugging purposes, see [Debugging Transition Tools](./debugging_t8n_tools.md).

docs/running_tests/test_formats/blockchain_test_engine_x.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ Engine API payload structure identical to the one defined in [Blockchain Engine
139139
## Usage Notes
140140

141141
- This format is generated when using:
142-
- `--generate-pre-alloc-groups` flag (automatically triggers 2-phase execution, generates only `BlockchainEngineXFixture`)
142+
- `--generate-pre-alloc-groups` followed by `--use-pre-alloc-groups` (two invocations: phase 1 populates the `pre_alloc` folder, phase 2 generates only `BlockchainEngineXFixture`)
143143
- `--generate-all-formats` flag (automatically triggers 2-phase execution, generates all fixture formats)
144144
- The `pre_alloc` folder is essential and must be distributed with the test fixtures
145145
- Tests are grouped by identical (fork + environment + pre-allocation) combinations

packages/testing/src/execution_testing/cli/pytest_commands/fill.py

Lines changed: 68 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -33,33 +33,81 @@ def create_executions(
3333
self, pytest_args: List[str]
3434
) -> List[PytestExecution]:
3535
"""
36-
Create execution plan that supports two-phase pre-allocation group
36+
Create execution plan supporting two-phase pre-allocation group
3737
generation.
3838
39-
Returns single execution for normal filling, or two-phase execution
40-
when --generate-pre-alloc-groups or --generate-all-formats is
41-
specified.
39+
Returns:
40+
- Phase-1-only execution when `--generate-pre-alloc-groups` is
41+
set without `--generate-all-formats`.
42+
- Single-phase execution when `--use-pre-alloc-groups` is set,
43+
regardless of `--generate-all-formats` (pre-alloc groups
44+
already exist on disk from a previous run).
45+
- Two-phase execution when `--generate-all-formats` is set.
46+
- Normal single-phase execution otherwise.
47+
4248
"""
4349
processed_args = self.process_arguments(pytest_args)
4450
processed_args = self._add_default_ignores(processed_args)
51+
self._validate_flag_combinations(processed_args)
4552

46-
# Check if we need two-phase execution
47-
if self._should_use_two_phase_execution(processed_args):
48-
return self._create_two_phase_executions(processed_args)
49-
elif "--use-pre-alloc-groups" in processed_args:
50-
# Only phase 2: using existing pre-allocation groups
53+
if "--use-pre-alloc-groups" in processed_args:
54+
# Pre-alloc groups already exist: single-phase fill only.
5155
return self._create_single_phase_with_pre_alloc_groups(
5256
processed_args
5357
)
54-
else:
55-
# Normal single-phase execution
56-
return [
57-
PytestExecution(
58-
config_file=self.config_path,
59-
args=processed_args,
60-
allowed_exit_codes=self.allowed_exit_codes,
61-
)
62-
]
58+
59+
has_phase_1_flag = "--generate-pre-alloc-groups" in processed_args
60+
has_phase_2_flag = "--generate-all-formats" in processed_args
61+
62+
if has_phase_2_flag:
63+
# --generate-all-formats always regenerates pre-alloc as phase 1.
64+
return self._create_two_phase_executions(processed_args)
65+
if has_phase_1_flag:
66+
# Phase 1 only: generate pre-alloc groups without filling.
67+
return [self._create_phase1_only_execution(processed_args)]
68+
69+
# Normal single-phase execution.
70+
return [
71+
PytestExecution(
72+
config_file=self.config_path,
73+
args=processed_args,
74+
allowed_exit_codes=self.allowed_exit_codes,
75+
)
76+
]
77+
78+
def _validate_flag_combinations(self, args: List[str]) -> None:
79+
"""
80+
Reject contradictory flag combinations up front so phase 2 never
81+
runs against a missing pre-alloc folder.
82+
"""
83+
if "--use-pre-alloc-groups" not in args:
84+
return
85+
if "--generate-pre-alloc-groups" in args:
86+
raise click.UsageError(
87+
"--use-pre-alloc-groups and --generate-pre-alloc-groups "
88+
"are mutually exclusive: the first asserts the groups "
89+
"already exist on disk, the second regenerates them."
90+
)
91+
if "--clean" in args:
92+
raise click.UsageError(
93+
"--use-pre-alloc-groups cannot be combined with --clean: "
94+
"--clean wipes the output directory that holds the "
95+
"pre-alloc groups."
96+
)
97+
98+
def _create_phase1_only_execution(
99+
self, args: List[str]
100+
) -> PytestExecution:
101+
"""Create a phase-1 execution that generates pre-allocation groups."""
102+
return PytestExecution(
103+
config_file=self.config_path,
104+
args=self._create_phase1_args(args),
105+
description="generating pre-allocation groups",
106+
allowed_exit_codes=[
107+
*self.allowed_exit_codes,
108+
pytest.ExitCode.NO_TESTS_COLLECTED,
109+
],
110+
)
63111

64112
def _create_two_phase_executions(
65113
self, args: List[str]
@@ -68,25 +116,11 @@ def _create_two_phase_executions(
68116
Create two-phase execution: pre-allocation group generation + fixture
69117
filling.
70118
"""
71-
# Phase 1: Pre-allocation group generation (clean and minimal output)
72-
phase1_args = self._create_phase1_args(args)
73-
74-
# Phase 2: Main fixture generation (full user options)
75-
phase2_args = self._create_phase2_args(args)
76-
77119
return [
120+
self._create_phase1_only_execution(args),
78121
PytestExecution(
79122
config_file=self.config_path,
80-
args=phase1_args,
81-
description="generating pre-allocation groups",
82-
allowed_exit_codes=[
83-
*self.allowed_exit_codes,
84-
pytest.ExitCode.NO_TESTS_COLLECTED,
85-
],
86-
),
87-
PytestExecution(
88-
config_file=self.config_path,
89-
args=phase2_args,
123+
args=self._create_phase2_args(args),
90124
description="filling test fixtures",
91125
),
92126
]
@@ -195,13 +229,6 @@ def _add_use_pre_alloc_groups_flag(self, args: List[str]) -> List[str]:
195229
"""Add --use-pre-alloc-groups flag to argument list."""
196230
return args + ["--use-pre-alloc-groups"]
197231

198-
def _should_use_two_phase_execution(self, args: List[str]) -> bool:
199-
"""Determine if two-phase execution is needed."""
200-
return (
201-
"--generate-pre-alloc-groups" in args
202-
or "--generate-all-formats" in args
203-
)
204-
205232
def _is_watch_mode(self, args: List[str]) -> bool:
206233
"""Check if any watch flag is present in arguments."""
207234
return any(flag in args for flag in ["--watch", "--watcherfall"])

packages/testing/src/execution_testing/cli/tests/test_generate_all_formats.py

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
from unittest.mock import patch
44

5+
import click
6+
import pytest
7+
58
from execution_testing.cli.pytest_commands.fill import (
69
FillCommand,
710
)
@@ -76,26 +79,79 @@ def test_generate_all_formats_removes_clean_from_phase2() -> None:
7679
assert "--clean" not in phase2_args
7780

7881

79-
def test_legacy_generate_pre_alloc_groups_still_works() -> None:
80-
"""Test that the legacy --generate-pre-alloc-groups flag still works."""
82+
def test_generate_pre_alloc_groups_alone_is_phase_1_only() -> None:
83+
"""
84+
Test that --generate-pre-alloc-groups without --generate-all-formats
85+
runs phase 1 only, so CI can populate pre-alloc groups on a
86+
dedicated runner without wasting time on phase 2.
87+
"""
8188
command = FillCommand()
8289

8390
with patch.object(command, "process_arguments", side_effect=lambda x: x):
8491
pytest_args = ["--generate-pre-alloc-groups", "tests/somedir/"]
8592
executions = command.create_executions(pytest_args)
8693

87-
assert len(executions) == 2
94+
assert len(executions) == 1
8895

89-
# Phase 1: Should have --generate-pre-alloc-groups
9096
phase1_args = executions[0].args
9197
assert "--generate-pre-alloc-groups" in phase1_args
98+
assert "--use-pre-alloc-groups" not in phase1_args
99+
assert "--generate-all-formats" not in phase1_args
92100

93-
# Phase 2: Should have --use-pre-alloc-groups but NOT --generate-all-
94-
# formats
95-
phase2_args = executions[1].args
96-
assert "--use-pre-alloc-groups" in phase2_args
97-
assert "--generate-all-formats" not in phase2_args
98-
assert "--generate-pre-alloc-groups" not in phase2_args
101+
102+
def test_use_pre_alloc_groups_forces_single_phase() -> None:
103+
"""
104+
Test that --use-pre-alloc-groups always runs a single phase, even
105+
alongside --generate-all-formats (pre-alloc groups already exist on
106+
disk from a previous run).
107+
"""
108+
command = FillCommand()
109+
110+
with patch.object(command, "process_arguments", side_effect=lambda x: x):
111+
pytest_args = [
112+
"--use-pre-alloc-groups",
113+
"--generate-all-formats",
114+
"tests/somedir/",
115+
]
116+
executions = command.create_executions(pytest_args)
117+
118+
assert len(executions) == 1
119+
assert "--use-pre-alloc-groups" in executions[0].args
120+
assert "--generate-all-formats" in executions[0].args
121+
122+
123+
def test_use_and_generate_pre_alloc_groups_together_is_rejected() -> None:
124+
"""
125+
--use-pre-alloc-groups + --generate-pre-alloc-groups are contradictory:
126+
the first asserts the groups exist, the second regenerates them.
127+
"""
128+
command = FillCommand()
129+
130+
with patch.object(command, "process_arguments", side_effect=lambda x: x):
131+
pytest_args = [
132+
"--use-pre-alloc-groups",
133+
"--generate-pre-alloc-groups",
134+
"tests/somedir/",
135+
]
136+
with pytest.raises(click.UsageError, match="mutually exclusive"):
137+
command.create_executions(pytest_args)
138+
139+
140+
def test_use_pre_alloc_groups_with_clean_is_rejected() -> None:
141+
"""
142+
--use-pre-alloc-groups + --clean is contradictory: --clean wipes the
143+
output directory that holds the pre-alloc groups.
144+
"""
145+
command = FillCommand()
146+
147+
with patch.object(command, "process_arguments", side_effect=lambda x: x):
148+
pytest_args = [
149+
"--use-pre-alloc-groups",
150+
"--clean",
151+
"tests/somedir/",
152+
]
153+
with pytest.raises(click.UsageError, match="--clean"):
154+
command.create_executions(pytest_args)
99155

100156

101157
def test_single_phase_without_flags() -> None:

0 commit comments

Comments
 (0)