Skip to content

Ethereum Test Fixtures package

Ethereum test fixture format definitions.

BaseFixture

Bases: CamelModel

Represents a base Ethereum test fixture of any type.

Source code in src/ethereum_test_fixtures/base.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
class BaseFixture(CamelModel):
    """Represents a base Ethereum test fixture of any type."""

    info: Dict[str, str] = Field(default_factory=dict, alias="_info")

    # Fixture format properties
    fixture_format_name: ClassVar[str] = "unset"
    output_file_extension: ClassVar[str] = ".json"
    description: ClassVar[str] = "Unknown fixture format; it has not been set."

    @classmethod
    def output_base_dir_name(cls) -> str:
        """
        Returns the name of the subdirectory where this type of fixture should be dumped to.
        """
        return cls.fixture_format_name.replace("test", "tests")

    @cached_property
    def json_dict(self) -> Dict[str, Any]:
        """
        Returns the JSON representation of the fixture.
        """
        return self.model_dump(mode="json", by_alias=True, exclude_none=True, exclude={"info"})

    @cached_property
    def hash(self) -> str:
        """
        Returns the hash of the fixture.
        """
        json_str = json.dumps(self.json_dict, sort_keys=True, separators=(",", ":"))
        h = hashlib.sha256(json_str.encode("utf-8")).hexdigest()
        return f"0x{h}"

    def json_dict_with_info(self, hash_only: bool = False) -> Dict[str, Any]:
        """
        Returns the JSON representation of the fixture with the info field.
        """
        dict_with_info = self.json_dict.copy()
        dict_with_info["_info"] = {"hash": self.hash}
        if not hash_only:
            dict_with_info["_info"].update(self.info)
        return dict_with_info

    def fill_info(
        self,
        t8n_version: str,
        test_case_description: str,
        fixture_source_url: str,
        ref_spec: ReferenceSpec | None,
    ):
        """
        Fill the info field for this fixture
        """
        if "comment" not in self.info:
            self.info["comment"] = "`execution-spec-tests` generated test"
        self.info["filling-transition-tool"] = t8n_version
        self.info["description"] = test_case_description
        self.info["url"] = fixture_source_url
        if ref_spec is not None:
            ref_spec.write_info(self.info)

    def get_fork(self) -> str | None:
        """
        Returns the fork of the fixture as a string.
        """
        raise NotImplementedError

    @classmethod
    def supports_fork(cls, fork: Fork) -> bool:
        """
        Returns whether the fixture can be generated for the given fork.

        By default, all fixtures support all forks.
        """
        return True

output_base_dir_name() classmethod

Returns the name of the subdirectory where this type of fixture should be dumped to.

Source code in src/ethereum_test_fixtures/base.py
26
27
28
29
30
31
@classmethod
def output_base_dir_name(cls) -> str:
    """
    Returns the name of the subdirectory where this type of fixture should be dumped to.
    """
    return cls.fixture_format_name.replace("test", "tests")

json_dict: Dict[str, Any] cached property

Returns the JSON representation of the fixture.

hash: str cached property

Returns the hash of the fixture.

json_dict_with_info(hash_only=False)

Returns the JSON representation of the fixture with the info field.

Source code in src/ethereum_test_fixtures/base.py
49
50
51
52
53
54
55
56
57
def json_dict_with_info(self, hash_only: bool = False) -> Dict[str, Any]:
    """
    Returns the JSON representation of the fixture with the info field.
    """
    dict_with_info = self.json_dict.copy()
    dict_with_info["_info"] = {"hash": self.hash}
    if not hash_only:
        dict_with_info["_info"].update(self.info)
    return dict_with_info

fill_info(t8n_version, test_case_description, fixture_source_url, ref_spec)

Fill the info field for this fixture

Source code in src/ethereum_test_fixtures/base.py
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def fill_info(
    self,
    t8n_version: str,
    test_case_description: str,
    fixture_source_url: str,
    ref_spec: ReferenceSpec | None,
):
    """
    Fill the info field for this fixture
    """
    if "comment" not in self.info:
        self.info["comment"] = "`execution-spec-tests` generated test"
    self.info["filling-transition-tool"] = t8n_version
    self.info["description"] = test_case_description
    self.info["url"] = fixture_source_url
    if ref_spec is not None:
        ref_spec.write_info(self.info)

get_fork()

Returns the fork of the fixture as a string.

Source code in src/ethereum_test_fixtures/base.py
77
78
79
80
81
def get_fork(self) -> str | None:
    """
    Returns the fork of the fixture as a string.
    """
    raise NotImplementedError

supports_fork(fork) classmethod

Returns whether the fixture can be generated for the given fork.

By default, all fixtures support all forks.

Source code in src/ethereum_test_fixtures/base.py
83
84
85
86
87
88
89
90
@classmethod
def supports_fork(cls, fork: Fork) -> bool:
    """
    Returns whether the fixture can be generated for the given fork.

    By default, all fixtures support all forks.
    """
    return True

BlockchainEngineFixture

Bases: FixtureCommon

Engine specific test fixture information.

Source code in src/ethereum_test_fixtures/blockchain.py
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
class EngineFixture(FixtureCommon):
    """
    Engine specific test fixture information.
    """

    fixture_format_name: ClassVar[str] = "blockchain_test_engine"
    description: ClassVar[
        str
    ] = "Tests that generate a blockchain test fixture in Engine API format."

    payloads: List[FixtureEngineNewPayload] = Field(..., alias="engineNewPayloads")
    sync_payload: FixtureEngineNewPayload | None = None

    @classmethod
    def supports_fork(cls, fork: Fork) -> bool:
        """
        Returns whether the fixture can be generated for the given fork.

        The Engine API is available only on Paris and afterwards.
        """
        return fork >= Paris

supports_fork(fork) classmethod

Returns whether the fixture can be generated for the given fork.

The Engine API is available only on Paris and afterwards.

Source code in src/ethereum_test_fixtures/blockchain.py
522
523
524
525
526
527
528
529
@classmethod
def supports_fork(cls, fork: Fork) -> bool:
    """
    Returns whether the fixture can be generated for the given fork.

    The Engine API is available only on Paris and afterwards.
    """
    return fork >= Paris

BlockchainFixture

Bases: FixtureCommon

Cross-client specific blockchain test model use in JSON fixtures.

Source code in src/ethereum_test_fixtures/blockchain.py
496
497
498
499
500
501
502
503
504
505
506
class Fixture(FixtureCommon):
    """
    Cross-client specific blockchain test model use in JSON fixtures.
    """

    fixture_format_name: ClassVar[str] = "blockchain_test"
    description: ClassVar[str] = "Tests that generate a blockchain test fixture."

    genesis_rlp: Bytes = Field(..., alias="genesisRLP")
    blocks: List[FixtureBlock | InvalidFixtureBlock]
    seal_engine: Literal["NoProof"] = Field("NoProof")

BlockchainFixtureCommon

Bases: BaseFixture

Base blockchain test fixture model.

Source code in src/ethereum_test_fixtures/blockchain.py
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
class FixtureCommon(BaseFixture):
    """
    Base blockchain test fixture model.
    """

    fork: str = Field(..., alias="network")
    genesis: FixtureHeader = Field(..., alias="genesisBlockHeader")
    pre: Alloc
    post_state: Alloc | None = Field(None)
    last_block_hash: Hash = Field(..., alias="lastblockhash")  # FIXME: lastBlockHash

    def get_fork(self) -> str | None:
        """
        Returns the fork of the fixture as a string.
        """
        return self.fork

get_fork()

Returns the fork of the fixture as a string.

Source code in src/ethereum_test_fixtures/blockchain.py
489
490
491
492
493
def get_fork(self) -> str | None:
    """
    Returns the fork of the fixture as a string.
    """
    return self.fork

FixtureCollector dataclass

Collects all fixtures generated by the test cases.

Source code in src/ethereum_test_fixtures/collector.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
@dataclass(kw_only=True)
class FixtureCollector:
    """
    Collects all fixtures generated by the test cases.
    """

    output_dir: Path
    flat_output: bool
    single_fixture_per_file: bool
    filler_path: Path
    base_dump_dir: Optional[Path] = None

    # Internal state
    all_fixtures: Dict[Path, Fixtures] = field(default_factory=dict)
    json_path_to_test_item: Dict[Path, TestInfo] = field(default_factory=dict)

    def get_fixture_basename(self, info: TestInfo) -> Path:
        """
        Returns the basename of the fixture file for a given test case.
        """
        if self.flat_output:
            if self.single_fixture_per_file:
                return Path(strip_test_prefix(info.get_single_test_name()))
            return Path(strip_test_prefix(info.original_name))
        else:
            relative_fixture_output_dir = Path(info.path).parent / strip_test_prefix(
                Path(info.path).stem
            )
            module_relative_output_dir = get_module_relative_output_dir(
                relative_fixture_output_dir, self.filler_path
            )

            if self.single_fixture_per_file:
                return module_relative_output_dir / strip_test_prefix(info.get_single_test_name())
            return module_relative_output_dir / strip_test_prefix(info.original_name)

    def add_fixture(self, info: TestInfo, fixture: BaseFixture) -> Path:
        """
        Adds a fixture to the list of fixtures of a given test case.
        """
        fixture_basename = self.get_fixture_basename(info)

        fixture_path = (
            self.output_dir
            / fixture.output_base_dir_name()
            / fixture_basename.with_suffix(fixture.output_file_extension)
        )
        if fixture_path not in self.all_fixtures.keys():  # relevant when we group by test function
            self.all_fixtures[fixture_path] = Fixtures(root={})
            self.json_path_to_test_item[fixture_path] = info

        self.all_fixtures[fixture_path][info.id] = fixture

        return fixture_path

    def dump_fixtures(self) -> None:
        """
        Dumps all collected fixtures to their respective files.
        """
        if self.output_dir.name == "stdout":
            combined_fixtures = {
                k: to_json(v) for fixture in self.all_fixtures.values() for k, v in fixture.items()
            }
            json.dump(combined_fixtures, sys.stdout, indent=4)
            return
        os.makedirs(self.output_dir, exist_ok=True)
        for fixture_path, fixtures in self.all_fixtures.items():
            os.makedirs(fixture_path.parent, exist_ok=True)
            if len({fixture.__class__ for fixture in fixtures.values()}) != 1:
                raise TypeError("All fixtures in a single file must have the same format.")
            fixtures.collect_into_file(fixture_path)

    def verify_fixture_files(self, evm_fixture_verification: FixtureVerifier) -> None:
        """
        Runs `evm [state|block]test` on each fixture.
        """
        for fixture_path, name_fixture_dict in self.all_fixtures.items():
            for fixture_name, fixture in name_fixture_dict.items():
                if evm_fixture_verification.is_verifiable(fixture.__class__):
                    info = self.json_path_to_test_item[fixture_path]
                    verify_fixtures_dump_dir = self._get_verify_fixtures_dump_dir(info)
                    evm_fixture_verification.verify_fixture(
                        fixture.__class__,
                        fixture_path,
                        fixture_name=None,
                        debug_output_path=verify_fixtures_dump_dir,
                    )

    def _get_verify_fixtures_dump_dir(
        self,
        info: TestInfo,
    ):
        """
        The directory to dump the current test function's fixture.json and fixture
        verification debug output.
        """
        if not self.base_dump_dir:
            return None
        if self.single_fixture_per_file:
            return info.get_dump_dir_path(
                self.base_dump_dir, self.filler_path, level="test_parameter"
            )
        else:
            return info.get_dump_dir_path(
                self.base_dump_dir, self.filler_path, level="test_function"
            )

get_fixture_basename(info)

Returns the basename of the fixture file for a given test case.

Source code in src/ethereum_test_fixtures/collector.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
def get_fixture_basename(self, info: TestInfo) -> Path:
    """
    Returns the basename of the fixture file for a given test case.
    """
    if self.flat_output:
        if self.single_fixture_per_file:
            return Path(strip_test_prefix(info.get_single_test_name()))
        return Path(strip_test_prefix(info.original_name))
    else:
        relative_fixture_output_dir = Path(info.path).parent / strip_test_prefix(
            Path(info.path).stem
        )
        module_relative_output_dir = get_module_relative_output_dir(
            relative_fixture_output_dir, self.filler_path
        )

        if self.single_fixture_per_file:
            return module_relative_output_dir / strip_test_prefix(info.get_single_test_name())
        return module_relative_output_dir / strip_test_prefix(info.original_name)

add_fixture(info, fixture)

Adds a fixture to the list of fixtures of a given test case.

Source code in src/ethereum_test_fixtures/collector.py
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
def add_fixture(self, info: TestInfo, fixture: BaseFixture) -> Path:
    """
    Adds a fixture to the list of fixtures of a given test case.
    """
    fixture_basename = self.get_fixture_basename(info)

    fixture_path = (
        self.output_dir
        / fixture.output_base_dir_name()
        / fixture_basename.with_suffix(fixture.output_file_extension)
    )
    if fixture_path not in self.all_fixtures.keys():  # relevant when we group by test function
        self.all_fixtures[fixture_path] = Fixtures(root={})
        self.json_path_to_test_item[fixture_path] = info

    self.all_fixtures[fixture_path][info.id] = fixture

    return fixture_path

dump_fixtures()

Dumps all collected fixtures to their respective files.

Source code in src/ethereum_test_fixtures/collector.py
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def dump_fixtures(self) -> None:
    """
    Dumps all collected fixtures to their respective files.
    """
    if self.output_dir.name == "stdout":
        combined_fixtures = {
            k: to_json(v) for fixture in self.all_fixtures.values() for k, v in fixture.items()
        }
        json.dump(combined_fixtures, sys.stdout, indent=4)
        return
    os.makedirs(self.output_dir, exist_ok=True)
    for fixture_path, fixtures in self.all_fixtures.items():
        os.makedirs(fixture_path.parent, exist_ok=True)
        if len({fixture.__class__ for fixture in fixtures.values()}) != 1:
            raise TypeError("All fixtures in a single file must have the same format.")
        fixtures.collect_into_file(fixture_path)

verify_fixture_files(evm_fixture_verification)

Runs evm [state|block]test on each fixture.

Source code in src/ethereum_test_fixtures/collector.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def verify_fixture_files(self, evm_fixture_verification: FixtureVerifier) -> None:
    """
    Runs `evm [state|block]test` on each fixture.
    """
    for fixture_path, name_fixture_dict in self.all_fixtures.items():
        for fixture_name, fixture in name_fixture_dict.items():
            if evm_fixture_verification.is_verifiable(fixture.__class__):
                info = self.json_path_to_test_item[fixture_path]
                verify_fixtures_dump_dir = self._get_verify_fixtures_dump_dir(info)
                evm_fixture_verification.verify_fixture(
                    fixture.__class__,
                    fixture_path,
                    fixture_name=None,
                    debug_output_path=verify_fixtures_dump_dir,
                )

TestInfo dataclass

Contains test information from the current node.

Source code in src/ethereum_test_fixtures/collector.py
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
@dataclass(kw_only=True)
class TestInfo:
    """
    Contains test information from the current node.
    """

    name: str  # pytest: Item.name
    id: str  # pytest: Item.nodeid
    original_name: str  # pytest: Item.originalname
    path: Path  # pytest: Item.path

    def get_name_and_parameters(self) -> Tuple[str, str]:
        """
        Converts a test name to a tuple containing the test name and test parameters.

        Example:
        test_push0_key_sstore[fork_Shanghai] -> test_push0_key_sstore, fork_Shanghai
        """
        test_name, parameters = self.name.split("[")
        return test_name, re.sub(r"[\[\-]", "_", parameters).replace("]", "")

    def get_single_test_name(self) -> str:
        """
        Converts a test name to a single test name.
        """
        test_name, test_parameters = self.get_name_and_parameters()
        return f"{test_name}__{test_parameters}"

    def get_dump_dir_path(
        self,
        base_dump_dir: Optional[Path],
        filler_path: Path,
        level: Literal["test_module", "test_function", "test_parameter"] = "test_parameter",
    ) -> Optional[Path]:
        """
        The path to dump the debug output as defined by the level to dump at.
        """
        if not base_dump_dir:
            return None
        test_module_relative_dir = get_module_relative_output_dir(self.path, filler_path)
        if level == "test_module":
            return Path(base_dump_dir) / Path(str(test_module_relative_dir).replace(os.sep, "__"))
        test_name, test_parameter_string = self.get_name_and_parameters()
        flat_path = f"{str(test_module_relative_dir).replace(os.sep, '__')}__{test_name}"
        if level == "test_function":
            return Path(base_dump_dir) / flat_path
        elif level == "test_parameter":
            return Path(base_dump_dir) / flat_path / test_parameter_string
        raise Exception("Unexpected level.")

get_name_and_parameters()

Converts a test name to a tuple containing the test name and test parameters.

Example: test_push0_key_sstore[fork_Shanghai] -> test_push0_key_sstore, fork_Shanghai

Source code in src/ethereum_test_fixtures/collector.py
57
58
59
60
61
62
63
64
65
def get_name_and_parameters(self) -> Tuple[str, str]:
    """
    Converts a test name to a tuple containing the test name and test parameters.

    Example:
    test_push0_key_sstore[fork_Shanghai] -> test_push0_key_sstore, fork_Shanghai
    """
    test_name, parameters = self.name.split("[")
    return test_name, re.sub(r"[\[\-]", "_", parameters).replace("]", "")

get_single_test_name()

Converts a test name to a single test name.

Source code in src/ethereum_test_fixtures/collector.py
67
68
69
70
71
72
def get_single_test_name(self) -> str:
    """
    Converts a test name to a single test name.
    """
    test_name, test_parameters = self.get_name_and_parameters()
    return f"{test_name}__{test_parameters}"

get_dump_dir_path(base_dump_dir, filler_path, level='test_parameter')

The path to dump the debug output as defined by the level to dump at.

Source code in src/ethereum_test_fixtures/collector.py
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
def get_dump_dir_path(
    self,
    base_dump_dir: Optional[Path],
    filler_path: Path,
    level: Literal["test_module", "test_function", "test_parameter"] = "test_parameter",
) -> Optional[Path]:
    """
    The path to dump the debug output as defined by the level to dump at.
    """
    if not base_dump_dir:
        return None
    test_module_relative_dir = get_module_relative_output_dir(self.path, filler_path)
    if level == "test_module":
        return Path(base_dump_dir) / Path(str(test_module_relative_dir).replace(os.sep, "__"))
    test_name, test_parameter_string = self.get_name_and_parameters()
    flat_path = f"{str(test_module_relative_dir).replace(os.sep, '__')}__{test_name}"
    if level == "test_function":
        return Path(base_dump_dir) / flat_path
    elif level == "test_parameter":
        return Path(base_dump_dir) / flat_path / test_parameter_string
    raise Exception("Unexpected level.")

EOFFixture

Bases: BaseFixture

Fixture for a single EOFTest.

Source code in src/ethereum_test_fixtures/eof.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class Fixture(BaseFixture):
    """
    Fixture for a single EOFTest.
    """

    fixture_format_name: ClassVar[str] = "eof_test"
    description: ClassVar[str] = "Tests that generate an EOF test fixture."

    vectors: Mapping[Number, Vector]

    def get_fork(self) -> str | None:
        """
        Returns the fork of the fixture as a string.
        """
        return None

get_fork()

Returns the fork of the fixture as a string.

Source code in src/ethereum_test_fixtures/eof.py
57
58
59
60
61
def get_fork(self) -> str | None:
    """
    Returns the fork of the fixture as a string.
    """
    return None

StateFixture

Bases: BaseFixture

Fixture for a single StateTest.

Source code in src/ethereum_test_fixtures/state.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
class Fixture(BaseFixture):
    """
    Fixture for a single StateTest.
    """

    fixture_format_name: ClassVar[str] = "state_test"
    description: ClassVar[str] = "Tests that generate a state test fixture."

    env: FixtureEnvironment
    pre: Alloc
    transaction: FixtureTransaction
    post: Mapping[str, List[FixtureForkPost]]

    def get_fork(self) -> str | None:
        """
        Returns the fork of the fixture as a string.
        """
        forks = list(self.post.keys())
        assert len(forks) == 1, "Expected state test fixture with single fork"
        return forks[0]

get_fork()

Returns the fork of the fixture as a string.

Source code in src/ethereum_test_fixtures/state.py
118
119
120
121
122
123
124
def get_fork(self) -> str | None:
    """
    Returns the fork of the fixture as a string.
    """
    forks = list(self.post.keys())
    assert len(forks) == 1, "Expected state test fixture with single fork"
    return forks[0]

FixtureVerifier

Bases: ABC

Abstract class for verifying Ethereum test fixtures.

Source code in src/ethereum_test_fixtures/verify.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class FixtureVerifier(ABC):
    """
    Abstract class for verifying Ethereum test fixtures.
    """

    def is_verifiable(
        self,
        fixture_format: FixtureFormat,
    ) -> bool:
        """
        Returns whether the fixture format is verifiable by this verifier.
        """
        return False

    @abstractmethod
    def verify_fixture(
        self,
        fixture_format: FixtureFormat,
        fixture_path: Path,
        fixture_name: str | None = None,
        debug_output_path: Path | None = None,
    ):
        """
        Executes `evm [state|block]test` to verify the fixture at `fixture_path`.

        Currently only implemented by geth's evm.
        """
        raise NotImplementedError(
            "The `verify_fixture()` function is not supported by this tool. Use geth's evm tool."
        )

is_verifiable(fixture_format)

Returns whether the fixture format is verifiable by this verifier.

Source code in src/ethereum_test_fixtures/verify.py
16
17
18
19
20
21
22
23
def is_verifiable(
    self,
    fixture_format: FixtureFormat,
) -> bool:
    """
    Returns whether the fixture format is verifiable by this verifier.
    """
    return False

verify_fixture(fixture_format, fixture_path, fixture_name=None, debug_output_path=None) abstractmethod

Executes evm [state|block]test to verify the fixture at fixture_path.

Currently only implemented by geth's evm.

Source code in src/ethereum_test_fixtures/verify.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@abstractmethod
def verify_fixture(
    self,
    fixture_format: FixtureFormat,
    fixture_path: Path,
    fixture_name: str | None = None,
    debug_output_path: Path | None = None,
):
    """
    Executes `evm [state|block]test` to verify the fixture at `fixture_path`.

    Currently only implemented by geth's evm.
    """
    raise NotImplementedError(
        "The `verify_fixture()` function is not supported by this tool. Use geth's evm tool."
    )