From 6e4c3a1119039bd79058b56c4b28564fb2ee090c Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Mon, 30 Sep 2024 15:19:09 -0600 Subject: [PATCH] refactor(fixtures): Remove `formats.py`, embed properties as class vars (#826) * refactor(fixtures): Embed FixtureFormat methods in instance * refactor(fw): remove `src/ethereum_test_fixtures/formats.py` * refactor(fw): rename * fix(fw): nit * fix(fw): pydantic fixture format * fix(fixtures): remove unused properties * fix(fixtures): remove unused properties * fix(fixtures): remove unused properties * fix(specs): tox --- src/cli/gen_index.py | 21 +++--- src/ethereum_test_fixtures/__init__.py | 24 ++++--- src/ethereum_test_fixtures/base.py | 31 ++++++-- src/ethereum_test_fixtures/blockchain.py | 22 ++++-- src/ethereum_test_fixtures/collector.py | 11 ++- src/ethereum_test_fixtures/consume.py | 17 +++-- src/ethereum_test_fixtures/eof.py | 6 +- src/ethereum_test_fixtures/file.py | 26 +++---- src/ethereum_test_fixtures/formats.py | 72 ------------------- src/ethereum_test_fixtures/state.py | 6 +- src/ethereum_test_fixtures/verify.py | 13 +++- src/ethereum_test_specs/base.py | 6 +- src/ethereum_test_specs/blockchain.py | 19 +++-- src/ethereum_test_specs/eof.py | 43 ++++++----- src/ethereum_test_specs/state.py | 22 +++--- src/ethereum_test_specs/tests/test_expect.py | 38 +++------- .../tests/test_fixtures.py | 43 +++++------ src/ethereum_test_tools/tests/test_code.py | 4 +- src/evm_transition_tool/geth.py | 22 ++++-- src/evm_transition_tool/transition_tool.py | 4 +- src/pytest_plugins/consume/consume.py | 2 +- src/pytest_plugins/consume/decorator.py | 6 +- .../consume/direct/test_via_direct.py | 6 +- .../hive_simulators/engine/test_via_engine.py | 4 +- .../hive_simulators/rlp/test_via_rlp.py | 4 +- src/pytest_plugins/filler/filler.py | 45 +++++------- src/pytest_plugins/forks/tests/test_forks.py | 49 ++++++++----- 27 files changed, 274 insertions(+), 292 deletions(-) delete mode 100644 src/ethereum_test_fixtures/formats.py diff --git a/src/cli/gen_index.py b/src/cli/gen_index.py index 19e49ae6a2..8178e4d1c3 100644 --- a/src/cli/gen_index.py +++ b/src/cli/gen_index.py @@ -20,7 +20,7 @@ ) from ethereum_test_base_types import HexNumber -from ethereum_test_fixtures import FixtureFormats +from ethereum_test_fixtures import FIXTURE_FORMATS, BlockchainFixture, FixtureFormat from ethereum_test_fixtures.consume import IndexFile, TestCaseIndexFile from ethereum_test_fixtures.file import Fixtures @@ -48,21 +48,16 @@ def count_json_files_exclude_index(start_path: Path) -> int: return json_file_count -def infer_fixture_format_from_path(file: Path) -> FixtureFormats: +def infer_fixture_format_from_path(file: Path) -> FixtureFormat | None: """ Attempt to infer the fixture format from the file path. """ - if "blockchain_tests_engine" in file.parts: - return FixtureFormats.BLOCKCHAIN_TEST_ENGINE - if "blockchain_tests" in file.parts: - return FixtureFormats.BLOCKCHAIN_TEST + for fixture_type in FIXTURE_FORMATS.values(): + if fixture_type.output_base_dir_name() in file.parts: + return fixture_type if "BlockchainTests" in file.parts: # ethereum/tests - return FixtureFormats.BLOCKCHAIN_TEST - if "state_tests" in file.parts: - return FixtureFormats.STATE_TEST - if "eof_tests" in file.parts: - return FixtureFormats.EOF_TEST - return FixtureFormats.UNSET_TEST_FORMAT + return BlockchainFixture + return None @click.command( @@ -200,7 +195,7 @@ def generate_fixtures_index( json_path=relative_file_path, fixture_hash=fixture.info.get("hash", None), fork=fixture.get_fork(), - format=fixture.format, + format=fixture.__class__, ) ) diff --git a/src/ethereum_test_fixtures/__init__.py b/src/ethereum_test_fixtures/__init__.py index b6c5c9f317..455eac8a0b 100644 --- a/src/ethereum_test_fixtures/__init__.py +++ b/src/ethereum_test_fixtures/__init__.py @@ -2,33 +2,35 @@ Ethereum test fixture format definitions. """ -from typing import List, Type +from typing import Dict -from .base import BaseFixture +from .base import BaseFixture, FixtureFormat from .blockchain import EngineFixture as BlockchainEngineFixture from .blockchain import Fixture as BlockchainFixture from .blockchain import FixtureCommon as BlockchainFixtureCommon from .collector import FixtureCollector, TestInfo from .eof import Fixture as EOFFixture -from .formats import FixtureFormats from .state import Fixture as StateFixture from .verify import FixtureVerifier -FIXTURE_TYPES: List[Type[BaseFixture]] = [ - BlockchainFixture, - BlockchainEngineFixture, - EOFFixture, - StateFixture, -] +FIXTURE_FORMATS: Dict[str, FixtureFormat] = { + f.fixture_format_name: f # type: ignore + for f in [ + BlockchainFixture, + BlockchainEngineFixture, + EOFFixture, + StateFixture, + ] +} __all__ = [ - "FIXTURE_TYPES", + "FIXTURE_FORMATS", "BaseFixture", "BlockchainFixture", "BlockchainFixtureCommon", "BlockchainEngineFixture", "EOFFixture", "FixtureCollector", - "FixtureFormats", + "FixtureFormat", "FixtureVerifier", "StateFixture", "TestInfo", diff --git a/src/ethereum_test_fixtures/base.py b/src/ethereum_test_fixtures/base.py index 32298230b8..05e83ff62e 100644 --- a/src/ethereum_test_fixtures/base.py +++ b/src/ethereum_test_fixtures/base.py @@ -5,20 +5,30 @@ import hashlib import json from functools import cached_property -from typing import Any, ClassVar, Dict +from typing import Any, ClassVar, Dict, Type from pydantic import Field from ethereum_test_base_types import CamelModel, ReferenceSpec - -from .formats import FixtureFormats +from ethereum_test_forks import Fork class BaseFixture(CamelModel): """Represents a base Ethereum test fixture of any type.""" info: Dict[str, str] = Field(default_factory=dict, alias="_info") - format: ClassVar[FixtureFormats] = FixtureFormats.UNSET_TEST_FORMAT + + # 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]: @@ -69,3 +79,16 @@ 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 + + +# Type alias for a base fixture class +FixtureFormat = Type[BaseFixture] diff --git a/src/ethereum_test_fixtures/blockchain.py b/src/ethereum_test_fixtures/blockchain.py index a230c11f15..9b770ce986 100644 --- a/src/ethereum_test_fixtures/blockchain.py +++ b/src/ethereum_test_fixtures/blockchain.py @@ -23,7 +23,7 @@ ZeroPaddedHexNumber, ) from ethereum_test_exceptions import EngineAPIError, ExceptionInstanceOrList -from ethereum_test_forks import Fork +from ethereum_test_forks import Fork, Paris from ethereum_test_types.types import ( AuthorizationTupleGeneric, ConsolidationRequest, @@ -41,7 +41,6 @@ ) from .base import BaseFixture -from .formats import FixtureFormats class HeaderForkRequirement(str): @@ -499,19 +498,32 @@ 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") - format: ClassVar[FixtureFormats] = FixtureFormats.BLOCKCHAIN_TEST - 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 - format: ClassVar[FixtureFormats] = FixtureFormats.BLOCKCHAIN_TEST_ENGINE + @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 diff --git a/src/ethereum_test_fixtures/collector.py b/src/ethereum_test_fixtures/collector.py index 112a1033a6..24542223fb 100644 --- a/src/ethereum_test_fixtures/collector.py +++ b/src/ethereum_test_fixtures/collector.py @@ -15,7 +15,6 @@ from .base import BaseFixture from .file import Fixtures -from .formats import FixtureFormats from .verify import FixtureVerifier @@ -139,8 +138,8 @@ def add_fixture(self, info: TestInfo, fixture: BaseFixture) -> Path: fixture_path = ( self.output_dir - / fixture.format.output_base_dir_name - / fixture_basename.with_suffix(fixture.format.output_file_extension) + / 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={}) @@ -163,7 +162,7 @@ def dump_fixtures(self) -> None: 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.format for fixture in fixtures.values()}) != 1: + 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) @@ -173,11 +172,11 @@ def verify_fixture_files(self, evm_fixture_verification: FixtureVerifier) -> Non """ for fixture_path, name_fixture_dict in self.all_fixtures.items(): for fixture_name, fixture in name_fixture_dict.items(): - if FixtureFormats.is_verifiable(fixture.format): + 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.format, + fixture.__class__, fixture_path, fixture_name=None, debug_output_path=verify_fixtures_dump_dir, diff --git a/src/ethereum_test_fixtures/consume.py b/src/ethereum_test_fixtures/consume.py index f155388e1d..ab46ee9e1f 100644 --- a/src/ethereum_test_fixtures/consume.py +++ b/src/ethereum_test_fixtures/consume.py @@ -5,15 +5,16 @@ import datetime import json from pathlib import Path -from typing import List, TextIO +from typing import Annotated, List, TextIO -from pydantic import BaseModel, RootModel +from pydantic import BaseModel, PlainSerializer, PlainValidator, RootModel from ethereum_test_base_types import HexNumber +from ethereum_test_fixtures import FIXTURE_FORMATS, FixtureFormat +from .blockchain import EngineFixture as BlockchainEngineFixture from .blockchain import Fixture as BlockchainFixture from .file import Fixtures -from .formats import FixtureFormats from .state import Fixture as StateFixture @@ -25,7 +26,11 @@ class TestCaseBase(BaseModel): id: str fixture_hash: HexNumber | None fork: str | None - format: FixtureFormats + format: Annotated[ + FixtureFormat, + PlainSerializer(lambda f: f.fixture_format_name), + PlainValidator(lambda f: FIXTURE_FORMATS[f] if f in FIXTURE_FORMATS else f), + ] __test__ = False # stop pytest from collecting this class as a test @@ -129,14 +134,14 @@ def from_stream(cls, fd: TextIO) -> "TestCases": fixtures = Fixtures.from_json_data(json.load(fd)) test_cases = [] for fixture_name, fixture in fixtures.items(): - if fixture.format == FixtureFormats.BLOCKCHAIN_TEST_ENGINE: + if fixture == BlockchainEngineFixture: print("Skipping engine fixture", fixture_name) test_cases.append( TestCaseStream( id=fixture_name, fixture_hash=fixture.hash, fork=fixture.get_fork(), - format=fixture.format, + format=fixture.__class__, fixture=fixture, ) ) diff --git a/src/ethereum_test_fixtures/eof.py b/src/ethereum_test_fixtures/eof.py index 987cea8ea0..ae43e0a1fa 100644 --- a/src/ethereum_test_fixtures/eof.py +++ b/src/ethereum_test_fixtures/eof.py @@ -12,7 +12,6 @@ from ethereum_test_types.eof.v1 import ContainerKind from .base import BaseFixture -from .formats import FixtureFormats class Result(CamelModel): @@ -50,9 +49,10 @@ class Fixture(BaseFixture): Fixture for a single EOFTest. """ - vectors: Mapping[Number, Vector] + fixture_format_name: ClassVar[str] = "eof_test" + description: ClassVar[str] = "Tests that generate an EOF test fixture." - format: ClassVar[FixtureFormats] = FixtureFormats.EOF_TEST + vectors: Mapping[Number, Vector] def get_fork(self) -> str | None: """ diff --git a/src/ethereum_test_fixtures/file.py b/src/ethereum_test_fixtures/file.py index ffed125bb5..3d6e573a7a 100644 --- a/src/ethereum_test_fixtures/file.py +++ b/src/ethereum_test_fixtures/file.py @@ -3,20 +3,16 @@ """ import json from pathlib import Path -from typing import Any, Dict, Literal, Optional +from typing import Any, Dict, Optional from pydantic import RootModel +from .base import FixtureFormat from .blockchain import EngineFixture as BlockchainEngineFixture from .blockchain import Fixture as BlockchainFixture from .eof import Fixture as EOFFixture -from .formats import FixtureFormats from .state import Fixture as StateFixture -FixtureFormatsValues = Literal[ - "blockchain_test_engine", "blockchain_test", "state_test", "eof_test", "unset_test_format" -] - FixtureModel = BlockchainFixture | BlockchainEngineFixture | StateFixture | EOFFixture @@ -75,7 +71,7 @@ def collect_into_file(self, file_path: Path): def from_file( cls, file_path: Path, - fixture_format: Optional[FixtureFormats | FixtureFormatsValues] = None, + fixture_format: Optional[FixtureFormat] = None, ) -> "BaseFixturesRootModel": """ Dynamically create a fixture model from the specified json file and, @@ -89,7 +85,7 @@ def from_file( def from_json_data( cls, json_data: Dict[str, Any], - fixture_format: Optional[FixtureFormats | FixtureFormatsValues] = None, + fixture_format: Optional[FixtureFormat] = None, ) -> "BaseFixturesRootModel": """ Dynamically create a fixture model from the specified json data and, @@ -101,17 +97,13 @@ def from_json_data( fixture_format will provide a speed-up. """ model_mapping = { - FixtureFormats.BLOCKCHAIN_TEST: BlockchainFixtures, - FixtureFormats.BLOCKCHAIN_TEST_ENGINE: BlockchainEngineFixtures, - FixtureFormats.STATE_TEST: StateFixtures, - FixtureFormats.EOF_TEST: EOFFixtures, - FixtureFormats.BLOCKCHAIN_TEST.value: BlockchainFixtures, - FixtureFormats.BLOCKCHAIN_TEST_ENGINE.value: BlockchainEngineFixtures, - FixtureFormats.STATE_TEST.value: StateFixtures, - FixtureFormats.EOF_TEST.value: EOFFixtures, + BlockchainFixture: BlockchainFixtures, + BlockchainEngineFixture: BlockchainEngineFixtures, + StateFixture: StateFixtures, + EOFFixture: EOFFixtures, } - if fixture_format not in [None, "unset_test_format", FixtureFormats.UNSET_TEST_FORMAT]: + if fixture_format is not None: if fixture_format not in model_mapping: raise TypeError(f"Unsupported fixture format: {fixture_format}") model_class = model_mapping[fixture_format] diff --git a/src/ethereum_test_fixtures/formats.py b/src/ethereum_test_fixtures/formats.py deleted file mode 100644 index 3acea77a1f..0000000000 --- a/src/ethereum_test_fixtures/formats.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Fixture formats enum. -""" -from enum import Enum -from pathlib import Path - - -class FixtureFormats(Enum): - """ - Helper class to define fixture formats. - """ - - UNSET_TEST_FORMAT = "unset_test_format" - STATE_TEST = "state_test" - BLOCKCHAIN_TEST = "blockchain_test" - BLOCKCHAIN_TEST_ENGINE = "blockchain_test_engine" - EOF_TEST = "eof_test" - - @classmethod - def is_state_test(cls, format): # noqa: D102 - return format == cls.STATE_TEST - - @classmethod - def is_blockchain_test(cls, format): # noqa: D102 - return format in (cls.BLOCKCHAIN_TEST, cls.BLOCKCHAIN_TEST_ENGINE) - - @classmethod - def is_hive_format(cls, format): # noqa: D102 - return format == cls.BLOCKCHAIN_TEST_ENGINE - - @classmethod - def is_standard_format(cls, format): # noqa: D102 - return format in (cls.STATE_TEST, cls.BLOCKCHAIN_TEST) - - @classmethod - def is_verifiable(cls, format): # noqa: D102 - return format in (cls.STATE_TEST, cls.BLOCKCHAIN_TEST) - - @classmethod - def get_format_description(cls, format): - """ - Returns a description of the fixture format. - - Used to add a description to the generated pytest marks. - """ - if format == cls.UNSET_TEST_FORMAT: - return "Unknown fixture format; it has not been set." - elif format == cls.STATE_TEST: - return "Tests that generate a state test fixture." - elif format == cls.BLOCKCHAIN_TEST: - return "Tests that generate a blockchain test fixture." - elif format == cls.BLOCKCHAIN_TEST_ENGINE: - return "Tests that generate a blockchain test fixture in Engine API format." - elif format == cls.EOF_TEST: - return "Tests that generate an EOF test fixture." - raise Exception(f"Unknown fixture format: {format}.") - - @property - def output_base_dir_name(self) -> Path: - """ - Returns the name of the subdirectory where this type of fixture should be dumped to. - """ - return Path(self.value.replace("test", "tests")) - - @property - def output_file_extension(self) -> str: - """ - Returns the file extension for this type of fixture. - - By default, fixtures are dumped as JSON files. - """ - return ".json" diff --git a/src/ethereum_test_fixtures/state.py b/src/ethereum_test_fixtures/state.py index d3c35b1496..7b2cbc57e2 100644 --- a/src/ethereum_test_fixtures/state.py +++ b/src/ethereum_test_fixtures/state.py @@ -18,7 +18,6 @@ ) from .base import BaseFixture -from .formats import FixtureFormats class FixtureEnvironment(EnvironmentGeneric[ZeroPaddedHexNumber]): @@ -108,13 +107,14 @@ 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]] - format: ClassVar[FixtureFormats] = FixtureFormats.STATE_TEST - def get_fork(self) -> str | None: """ Returns the fork of the fixture as a string. diff --git a/src/ethereum_test_fixtures/verify.py b/src/ethereum_test_fixtures/verify.py index c3a6faced5..234834d9ba 100644 --- a/src/ethereum_test_fixtures/verify.py +++ b/src/ethereum_test_fixtures/verify.py @@ -5,7 +5,7 @@ from abc import ABC, abstractmethod from pathlib import Path -from .formats import FixtureFormats +from .base import FixtureFormat class FixtureVerifier(ABC): @@ -13,10 +13,19 @@ 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: FixtureFormats, + fixture_format: FixtureFormat, fixture_path: Path, fixture_name: str | None = None, debug_output_path: Path | None = None, diff --git a/src/ethereum_test_specs/base.py b/src/ethereum_test_specs/base.py index 9e50eb7b31..a79bf6e8ab 100644 --- a/src/ethereum_test_specs/base.py +++ b/src/ethereum_test_specs/base.py @@ -13,7 +13,7 @@ from pydantic import BaseModel, Field from ethereum_test_base_types import to_hex -from ethereum_test_fixtures import BaseFixture, FixtureFormats +from ethereum_test_fixtures import BaseFixture, FixtureFormat from ethereum_test_forks import Fork from ethereum_test_types import Environment, Transaction, Withdrawal from evm_transition_tool import Result, TransitionTool @@ -73,7 +73,7 @@ class BaseTest(BaseModel): t8n_dump_dir: Path | None = Field(None, exclude=True) _t8n_call_counter: Iterator[int] = count(0) - supported_fixture_formats: ClassVar[List[FixtureFormats]] = [] + supported_fixture_formats: ClassVar[List[FixtureFormat]] = [] @abstractmethod def generate( @@ -82,7 +82,7 @@ def generate( request: pytest.FixtureRequest, t8n: TransitionTool, fork: Fork, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, eips: Optional[List[int]] = None, ) -> BaseFixture: """ diff --git a/src/ethereum_test_specs/blockchain.py b/src/ethereum_test_specs/blockchain.py index 3d0c232387..d91fb18180 100644 --- a/src/ethereum_test_specs/blockchain.py +++ b/src/ethereum_test_specs/blockchain.py @@ -21,7 +21,12 @@ Number, ) from ethereum_test_exceptions import BlockException, EngineAPIError, TransactionException -from ethereum_test_fixtures import BaseFixture, FixtureFormats +from ethereum_test_fixtures import ( + BaseFixture, + BlockchainEngineFixture, + BlockchainFixture, + FixtureFormat, +) from ethereum_test_fixtures.blockchain import ( EngineFixture, Fixture, @@ -319,9 +324,9 @@ class BlockchainTest(BaseTest): verify_sync: bool = False chain_id: int = 1 - supported_fixture_formats: ClassVar[List[FixtureFormats]] = [ - FixtureFormats.BLOCKCHAIN_TEST, - FixtureFormats.BLOCKCHAIN_TEST_ENGINE, + supported_fixture_formats: ClassVar[List[FixtureFormat]] = [ + BlockchainFixture, + BlockchainEngineFixture, ] def make_genesis( @@ -718,16 +723,16 @@ def generate( request: pytest.FixtureRequest, t8n: TransitionTool, fork: Fork, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, eips: Optional[List[int]] = None, ) -> BaseFixture: """ Generate the BlockchainTest fixture. """ t8n.reset_traces() - if fixture_format == FixtureFormats.BLOCKCHAIN_TEST_ENGINE: + if fixture_format == BlockchainEngineFixture: return self.make_hive_fixture(t8n, fork, eips) - elif fixture_format == FixtureFormats.BLOCKCHAIN_TEST: + elif fixture_format == BlockchainFixture: return self.make_fixture(t8n, fork, eips) raise Exception(f"Unknown fixture format: {fixture_format}") diff --git a/src/ethereum_test_specs/eof.py b/src/ethereum_test_specs/eof.py index 83a4f0417a..e251eff6f6 100644 --- a/src/ethereum_test_specs/eof.py +++ b/src/ethereum_test_specs/eof.py @@ -15,8 +15,15 @@ from ethereum_test_base_types import Account, Bytes from ethereum_test_exceptions import EvmoneExceptionMapper from ethereum_test_exceptions.exceptions import EOFExceptionInstanceOrList, to_pipe_str -from ethereum_test_fixtures import BaseFixture, FixtureFormats -from ethereum_test_fixtures.eof import Fixture, Result, Vector +from ethereum_test_fixtures import ( + BaseFixture, + BlockchainEngineFixture, + BlockchainFixture, + EOFFixture, + FixtureFormat, + StateFixture, +) +from ethereum_test_fixtures.eof import Result, Vector from ethereum_test_forks import Fork from ethereum_test_types import Alloc, Environment, Transaction from ethereum_test_types.eof.v1 import Container, ContainerKind @@ -143,8 +150,8 @@ class EOFTest(BaseTest): expect_exception: EOFExceptionInstanceOrList | None = None container_kind: ContainerKind | None = None - supported_fixture_formats: ClassVar[List[FixtureFormats]] = [ - FixtureFormats.EOF_TEST, + supported_fixture_formats: ClassVar[List[FixtureFormat]] = [ + EOFFixture, ] @model_validator(mode="before") @@ -192,7 +199,7 @@ def make_eof_test_fixture( request: pytest.FixtureRequest, fork: Fork, eips: Optional[List[int]], - ) -> Fixture: + ) -> EOFFixture: """ Generate the EOF test fixture. """ @@ -213,7 +220,7 @@ def make_eof_test_fixture( }, ) ] - fixture = Fixture(vectors=dict(enumerate(vectors))) + fixture = EOFFixture(vectors=dict(enumerate(vectors))) try: eof_parse = EOFParse() except FileNotFoundError as e: @@ -272,13 +279,13 @@ def generate( t8n: TransitionTool, fork: Fork, eips: Optional[List[int]] = None, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, **_, ) -> BaseFixture: """ Generate the BlockchainTest fixture. """ - if fixture_format == FixtureFormats.EOF_TEST: + if fixture_format == EOFFixture: return self.make_eof_test_fixture(request=request, fork=fork, eips=eips) raise Exception(f"Unknown fixture format: {fixture_format}") @@ -301,11 +308,11 @@ class EOFStateTest(EOFTest): container_post: Account = Field(default_factory=Account) pre: Alloc | None = None - supported_fixture_formats: ClassVar[List[FixtureFormats]] = [ - FixtureFormats.EOF_TEST, - FixtureFormats.STATE_TEST, - FixtureFormats.BLOCKCHAIN_TEST, - FixtureFormats.BLOCKCHAIN_TEST_ENGINE, + supported_fixture_formats: ClassVar[List[FixtureFormat]] = [ + EOFFixture, + StateFixture, + BlockchainFixture, + BlockchainEngineFixture, ] @model_validator(mode="before") @@ -372,22 +379,22 @@ def generate( t8n: TransitionTool, fork: Fork, eips: Optional[List[int]] = None, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, **_, ) -> BaseFixture: """ Generate the BlockchainTest fixture. """ - if fixture_format == FixtureFormats.EOF_TEST: + if fixture_format == EOFFixture: if self.data in existing_tests: # Gracefully skip duplicate tests because one EOFStateTest can generate multiple # state fixtures with the same data. pytest.skip(f"Duplicate EOF container on EOFStateTest: {request.node.nodeid}") return self.make_eof_test_fixture(request=request, fork=fork, eips=eips) elif fixture_format in ( - FixtureFormats.STATE_TEST, - FixtureFormats.BLOCKCHAIN_TEST, - FixtureFormats.BLOCKCHAIN_TEST_ENGINE, + StateFixture, + BlockchainFixture, + BlockchainEngineFixture, ): return self.generate_state_test().generate( request=request, t8n=t8n, fork=fork, fixture_format=fixture_format, eips=eips diff --git a/src/ethereum_test_specs/state.py b/src/ethereum_test_specs/state.py index dcdaac5a6b..2252393253 100644 --- a/src/ethereum_test_specs/state.py +++ b/src/ethereum_test_specs/state.py @@ -7,7 +7,13 @@ import pytest from ethereum_test_exceptions import EngineAPIError -from ethereum_test_fixtures import BaseFixture, FixtureFormats +from ethereum_test_fixtures import ( + BaseFixture, + BlockchainEngineFixture, + BlockchainFixture, + FixtureFormat, + StateFixture, +) from ethereum_test_fixtures.state import ( Fixture, FixtureEnvironment, @@ -39,10 +45,10 @@ class StateTest(BaseTest): blockchain_test_rlp_modifier: Optional[Header] = None chain_id: int = 1 - supported_fixture_formats: ClassVar[List[FixtureFormats]] = [ - FixtureFormats.BLOCKCHAIN_TEST, - FixtureFormats.BLOCKCHAIN_TEST_ENGINE, - FixtureFormats.STATE_TEST, + supported_fixture_formats: ClassVar[List[FixtureFormat]] = [ + BlockchainFixture, + BlockchainEngineFixture, + StateFixture, ] def _generate_blockchain_genesis_environment(self) -> Environment: @@ -166,7 +172,7 @@ def generate( request: pytest.FixtureRequest, t8n: TransitionTool, fork: Fork, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, eips: Optional[List[int]] = None, ) -> BaseFixture: """ @@ -176,7 +182,7 @@ def generate( return self.generate_blockchain_test().generate( request=request, t8n=t8n, fork=fork, fixture_format=fixture_format, eips=eips ) - elif fixture_format == FixtureFormats.STATE_TEST: + elif fixture_format == StateFixture: return self.make_state_test_fixture(t8n, fork, eips) raise Exception(f"Unknown fixture format: {fixture_format}") @@ -187,7 +193,7 @@ class StateTestOnly(StateTest): StateTest filler that only generates a state test fixture. """ - supported_fixture_formats: ClassVar[List[FixtureFormats]] = [FixtureFormats.STATE_TEST] + supported_fixture_formats: ClassVar[List[FixtureFormat]] = [StateFixture] StateTestSpec = Callable[[str], Generator[StateTest, None, None]] diff --git a/src/ethereum_test_specs/tests/test_expect.py b/src/ethereum_test_specs/tests/test_expect.py index 43df2775e7..36ee92f132 100644 --- a/src/ethereum_test_specs/tests/test_expect.py +++ b/src/ethereum_test_specs/tests/test_expect.py @@ -7,7 +7,7 @@ import pytest from ethereum_test_base_types import Account, Address -from ethereum_test_fixtures import FixtureFormats +from ethereum_test_fixtures import StateFixture from ethereum_test_forks import Fork, get_deployed_forks from ethereum_test_types import Alloc, Environment, Storage, Transaction from evm_transition_tool import ExecutionSpecsTransitionTool @@ -116,9 +116,7 @@ def test_post_storage_value_mismatch( Test post state `Account.storage` exceptions during state test fixture generation. """ with pytest.raises(Storage.KeyValueMismatch) as e_info: - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) assert e_info.value == expected_exception @@ -145,14 +143,10 @@ def test_post_nonce_value_mismatch(pre: Alloc, post: Alloc, state_test, t8n, for pre_nonce = pre_account.nonce post_nonce = post_account.nonce if "nonce" not in post_account.model_fields_set: # no exception - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) return with pytest.raises(Account.NonceMismatch) as e_info: - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) assert e_info.value == Account.NonceMismatch( address=ADDRESS_UNDER_TEST, want=post_nonce, got=pre_nonce ) @@ -181,14 +175,10 @@ def test_post_code_value_mismatch(pre: Alloc, post: Alloc, state_test, t8n, fork pre_code = pre_account.code post_code = post_account.code if "code" not in post_account.model_fields_set: # no exception - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) return with pytest.raises(Account.CodeMismatch) as e_info: - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) assert e_info.value == Account.CodeMismatch( address=ADDRESS_UNDER_TEST, want=post_code, got=pre_code ) @@ -217,14 +207,10 @@ def test_post_balance_value_mismatch(pre: Alloc, post: Alloc, state_test, t8n, f pre_balance = pre_account.balance post_balance = post_account.balance if "balance" not in post_account.model_fields_set: # no exception - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) return with pytest.raises(Account.BalanceMismatch) as e_info: - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) assert e_info.value == Account.BalanceMismatch( address=ADDRESS_UNDER_TEST, want=post_balance, got=pre_balance ) @@ -264,11 +250,7 @@ def test_post_account_mismatch(state_test, t8n, fork, exception_type: Type[Excep fixture generation. """ if exception_type is None: - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) return with pytest.raises(exception_type) as _: - state_test.generate( - request=None, t8n=t8n, fork=fork, fixture_format=FixtureFormats.STATE_TEST - ) + state_test.generate(request=None, t8n=t8n, fork=fork, fixture_format=StateFixture) diff --git a/src/ethereum_test_specs/tests/test_fixtures.py b/src/ethereum_test_specs/tests/test_fixtures.py index d31f83816d..de2b1d9cde 100644 --- a/src/ethereum_test_specs/tests/test_fixtures.py +++ b/src/ethereum_test_specs/tests/test_fixtures.py @@ -12,7 +12,12 @@ import cli.check_fixtures from ethereum_test_base_types import Account, Address, Hash from ethereum_test_exceptions import TransactionException -from ethereum_test_fixtures import BlockchainFixture, FixtureFormats +from ethereum_test_fixtures import ( + BlockchainEngineFixture, + BlockchainFixture, + FixtureFormat, + StateFixture, +) from ethereum_test_fixtures.blockchain import FixtureCommon from ethereum_test_forks import Berlin, Fork, Istanbul, London, Paris, Shanghai from ethereum_test_types import Alloc, Environment, Transaction @@ -96,7 +101,7 @@ def test_make_genesis(fork: Fork, hash: bytes): # noqa: D103 request=None, # type: ignore t8n=t8n, fork=fork, - fixture_format=FixtureFormats.BLOCKCHAIN_TEST, + fixture_format=BlockchainFixture, ) assert isinstance(fixture, BlockchainFixture) assert fixture.genesis is not None @@ -109,17 +114,17 @@ def test_make_genesis(fork: Fork, hash: bytes): # noqa: D103 @pytest.mark.parametrize( "fork,fixture_format", [ - (Istanbul, FixtureFormats.BLOCKCHAIN_TEST), - (London, FixtureFormats.BLOCKCHAIN_TEST), - (Paris, FixtureFormats.BLOCKCHAIN_TEST_ENGINE), - (Shanghai, FixtureFormats.BLOCKCHAIN_TEST_ENGINE), - (Paris, FixtureFormats.STATE_TEST), - (Shanghai, FixtureFormats.STATE_TEST), + (Istanbul, BlockchainFixture), + (London, BlockchainFixture), + (Paris, BlockchainEngineFixture), + (Shanghai, BlockchainEngineFixture), + (Paris, StateFixture), + (Shanghai, StateFixture), ], ) def test_fill_state_test( fork: Fork, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, ): """ Test `ethereum_test.filler.fill_fixtures` with `StateTest`. @@ -166,12 +171,12 @@ def test_fill_state_test( fork=fork, fixture_format=fixture_format, ) - assert generated_fixture.format == fixture_format + assert generated_fixture.__class__ == fixture_format fixture = { f"000/my_chain_id_test/{fork}": generated_fixture.json_dict_with_info(hash_only=True), } - expected_json_file = f"chainid_{fork.name().lower()}_{fixture_format.value}.json" + expected_json_file = f"chainid_{fork.name().lower()}_{fixture_format.fixture_format_name}.json" with open( os.path.join( "src", @@ -471,9 +476,7 @@ def genesis_environment(self): # noqa: D102 @pytest.fixture def fixture_format(self, check_hive: bool): # noqa: D102 - return ( - FixtureFormats.BLOCKCHAIN_TEST_ENGINE if check_hive else FixtureFormats.BLOCKCHAIN_TEST - ) + return BlockchainEngineFixture if check_hive else BlockchainFixture @pytest.fixture def blockchain_test_fixture( # noqa: D102 @@ -484,7 +487,7 @@ def blockchain_test_fixture( # noqa: D102 post: Mapping[Any, Any], blocks: List[Block], genesis_environment: Environment, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, ): t8n = ExecutionSpecsTransitionTool() return BlockchainTest( @@ -506,11 +509,11 @@ def test_fill_blockchain_valid_txs( # noqa: D102 self, fork: Fork, check_hive: bool, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, expected_json_file: str, blockchain_test_fixture: BlockchainFixture, ): - assert blockchain_test_fixture.format == fixture_format + assert blockchain_test_fixture.__class__ == fixture_format assert isinstance(blockchain_test_fixture, FixtureCommon) fixture_name = f"000/my_blockchain_test/{fork.name()}" @@ -870,8 +873,8 @@ def test_fill_blockchain_invalid_txs(fork: Fork, check_hive: bool, expected_json ) t8n = ExecutionSpecsTransitionTool() - fixture_format = ( - FixtureFormats.BLOCKCHAIN_TEST_ENGINE if check_hive else FixtureFormats.BLOCKCHAIN_TEST + fixture_format: FixtureFormat = ( + BlockchainEngineFixture if check_hive else BlockchainFixture # type: ignore ) generated_fixture = BlockchainTest( pre=pre, @@ -884,7 +887,7 @@ def test_fill_blockchain_invalid_txs(fork: Fork, check_hive: bool, expected_json fork=fork, fixture_format=fixture_format, ) - assert generated_fixture.format == fixture_format + assert generated_fixture.__class__ == fixture_format assert isinstance(generated_fixture, FixtureCommon) fixture_name = f"000/my_blockchain_test/{fork.name()}" diff --git a/src/ethereum_test_tools/tests/test_code.py b/src/ethereum_test_tools/tests/test_code.py index 71eb614f0c..18fd71bec2 100644 --- a/src/ethereum_test_tools/tests/test_code.py +++ b/src/ethereum_test_tools/tests/test_code.py @@ -9,7 +9,7 @@ from semver import Version from ethereum_test_base_types import Account, Address, Bytes, Hash, TestAddress, TestPrivateKey -from ethereum_test_fixtures import FixtureFormats +from ethereum_test_fixtures import BlockchainFixture from ethereum_test_forks import ( Cancun, Fork, @@ -640,7 +640,7 @@ def test_switch(tx_data: bytes, switch_bytecode: bytes, expected_storage: Mappin request=None, # type: ignore t8n=ExecutionSpecsTransitionTool(), fork=Cancun, - fixture_format=FixtureFormats.BLOCKCHAIN_TEST, + fixture_format=BlockchainFixture, eips=None, ) diff --git a/src/evm_transition_tool/geth.py b/src/evm_transition_tool/geth.py index 02c4d86438..700d3a43d5 100644 --- a/src/evm_transition_tool/geth.py +++ b/src/evm_transition_tool/geth.py @@ -10,9 +10,10 @@ from re import compile from typing import Optional +from ethereum_test_fixtures import BlockchainFixture, StateFixture from ethereum_test_forks import Fork -from .transition_tool import FixtureFormats, TransitionTool, dump_files_to_directory +from .transition_tool import FixtureFormat, TransitionTool, dump_files_to_directory class GethTransitionTool(TransitionTool): @@ -67,9 +68,18 @@ def get_blocktest_help(self) -> str: raise Exception(f"Unexpected exception calling evm tool: {e}.") return result.stdout + def is_verifiable( + self, + fixture_format: FixtureFormat, + ) -> bool: + """ + Returns whether the fixture format is verifiable by this Geth's evm tool. + """ + return fixture_format in {StateFixture, BlockchainFixture} + def verify_fixture( self, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, fixture_path: Path, fixture_name: Optional[str] = None, debug_output_path: Optional[Path] = None, @@ -82,16 +92,16 @@ def verify_fixture( if debug_output_path: command += ["--debug", "--json", "--verbosity", "100"] - if FixtureFormats.is_state_test(fixture_format): + if fixture_format == StateFixture: assert self.statetest_subcommand, "statetest subcommand not set" command.append(self.statetest_subcommand) - elif FixtureFormats.is_blockchain_test(fixture_format): + elif fixture_format == BlockchainFixture: assert self.blocktest_subcommand, "blocktest subcommand not set" command.append(self.blocktest_subcommand) else: raise Exception(f"Invalid test fixture format: {fixture_format}") - if fixture_name and fixture_format == FixtureFormats.BLOCKCHAIN_TEST: + if fixture_name and fixture_format == BlockchainFixture: assert isinstance(fixture_name, str), "fixture_name must be a string" command.append("--run") command.append(fixture_name) @@ -130,7 +140,7 @@ def verify_fixture( f"EVM test failed.\n{' '.join(command)}\n\n Error:\n{result.stderr.decode()}" ) - if FixtureFormats.is_state_test(fixture_format): + if fixture_format == StateFixture: result_json = json.loads(result.stdout.decode()) if not isinstance(result_json, list): raise Exception(f"Unexpected result from evm statetest: {result_json}") diff --git a/src/evm_transition_tool/transition_tool.py b/src/evm_transition_tool/transition_tool.py index 863c9d15dc..b86b67fe2f 100644 --- a/src/evm_transition_tool/transition_tool.py +++ b/src/evm_transition_tool/transition_tool.py @@ -17,7 +17,7 @@ from requests_unixsocket import Session # type: ignore -from ethereum_test_fixtures import FixtureFormats, FixtureVerifier +from ethereum_test_fixtures import FixtureFormat, FixtureVerifier from ethereum_test_forks import Fork from ethereum_test_types import Alloc, Environment, Transaction @@ -616,7 +616,7 @@ def evaluate( def verify_fixture( self, - fixture_format: FixtureFormats, + fixture_format: FixtureFormat, fixture_path: Path, fixture_name: Optional[str] = None, debug_output_path: Optional[Path] = None, diff --git a/src/pytest_plugins/consume/consume.py b/src/pytest_plugins/consume/consume.py index 02999206f3..1a301ac4e8 100644 --- a/src/pytest_plugins/consume/consume.py +++ b/src/pytest_plugins/consume/consume.py @@ -191,7 +191,7 @@ def pytest_generate_tests(metafunc): ( pytest.param(test_case, id=test_case.id) for test_case in metafunc.config.test_cases - if test_case.format in metafunc.function.fixture_formats + if test_case.format in metafunc.function.fixture_format and (not fork or test_case.fork == fork) ), ) diff --git a/src/pytest_plugins/consume/decorator.py b/src/pytest_plugins/consume/decorator.py index 43b9641813..21f3c456e8 100644 --- a/src/pytest_plugins/consume/decorator.py +++ b/src/pytest_plugins/consume/decorator.py @@ -2,16 +2,16 @@ Consume test function decorator used to mark properties of a test function. """ -from ethereum_test_fixtures import FixtureFormats +from ethereum_test_fixtures import FixtureFormat -def fixture_format(*formats: FixtureFormats): +def fixture_format(*formats: FixtureFormat): """ Mark a test function as a test that consumes a specific fixture format. """ def decorator(func): - func.fixture_formats = formats + func.fixture_format = formats return func return decorator diff --git a/src/pytest_plugins/consume/direct/test_via_direct.py b/src/pytest_plugins/consume/direct/test_via_direct.py index 4e1f92ef59..0bdfe13731 100644 --- a/src/pytest_plugins/consume/direct/test_via_direct.py +++ b/src/pytest_plugins/consume/direct/test_via_direct.py @@ -9,7 +9,7 @@ import pytest -from ethereum_test_fixtures import FixtureFormats +from ethereum_test_fixtures import BlockchainFixture, StateFixture from ethereum_test_fixtures.consume import TestCaseIndexFile, TestCaseStream from evm_transition_tool import TransitionTool @@ -18,7 +18,7 @@ statetest_results: dict[Path, List[dict[str, Any]]] = {} -@fixture_format(FixtureFormats.BLOCKCHAIN_TEST) +@fixture_format(BlockchainFixture) def test_blocktest( # noqa: D103 test_case: TestCaseIndexFile | TestCaseStream, evm: TransitionTool, @@ -60,7 +60,7 @@ def run_statetest( @pytest.mark.usefixtures("run_statetest") -@fixture_format(FixtureFormats.STATE_TEST) +@fixture_format(StateFixture) def test_statetest( # noqa: D103 test_case: TestCaseIndexFile | TestCaseStream, fixture_path: Path, diff --git a/src/pytest_plugins/consume/hive_simulators/engine/test_via_engine.py b/src/pytest_plugins/consume/hive_simulators/engine/test_via_engine.py index df3ae5b9be..6871e7dafb 100644 --- a/src/pytest_plugins/consume/hive_simulators/engine/test_via_engine.py +++ b/src/pytest_plugins/consume/hive_simulators/engine/test_via_engine.py @@ -5,7 +5,7 @@ Each `engine_newPayloadVX` is verified against the appropriate VALID/INVALID responses. """ -from ethereum_test_fixtures import BlockchainEngineFixture, FixtureFormats +from ethereum_test_fixtures import BlockchainEngineFixture from ethereum_test_rpc import EngineRPC, EthRPC from ethereum_test_rpc.types import ForkchoiceState, PayloadStatusEnum from pytest_plugins.consume.hive_simulators.exceptions import GenesisBlockMismatchException @@ -14,7 +14,7 @@ from ..timing import TimingData -@fixture_format(FixtureFormats.BLOCKCHAIN_TEST_ENGINE) +@fixture_format(BlockchainEngineFixture) def test_via_engine( timing_data: TimingData, eth_rpc: EthRPC, diff --git a/src/pytest_plugins/consume/hive_simulators/rlp/test_via_rlp.py b/src/pytest_plugins/consume/hive_simulators/rlp/test_via_rlp.py index 6df6dd781e..e3dfdbe9d5 100644 --- a/src/pytest_plugins/consume/hive_simulators/rlp/test_via_rlp.py +++ b/src/pytest_plugins/consume/hive_simulators/rlp/test_via_rlp.py @@ -5,7 +5,7 @@ Clients consume the genesis and RLP-encoded blocks from input files upon start-up. """ -from ethereum_test_fixtures import BlockchainFixture, FixtureFormats +from ethereum_test_fixtures import BlockchainFixture from ethereum_test_rpc import EthRPC from pytest_plugins.consume.hive_simulators.exceptions import GenesisBlockMismatchException @@ -13,7 +13,7 @@ from ..timing import TimingData -@fixture_format(FixtureFormats.BLOCKCHAIN_TEST) +@fixture_format(BlockchainFixture) def test_via_rlp( timing_data: TimingData, eth_rpc: EthRPC, diff --git a/src/pytest_plugins/filler/filler.py b/src/pytest_plugins/filler/filler.py index 180ff55a20..010dac4e85 100644 --- a/src/pytest_plugins/filler/filler.py +++ b/src/pytest_plugins/filler/filler.py @@ -11,7 +11,7 @@ import tarfile import warnings from pathlib import Path -from typing import Generator, List, Type +from typing import Any, Dict, Generator, List, Type import pytest from filelock import FileLock @@ -19,10 +19,9 @@ from cli.gen_index import generate_fixtures_index from ethereum_test_base_types import Alloc, ReferenceSpec -from ethereum_test_fixtures import FixtureCollector, FixtureFormats, TestInfo +from ethereum_test_fixtures import FIXTURE_FORMATS, BaseFixture, FixtureCollector, TestInfo from ethereum_test_forks import ( Fork, - Paris, get_closest_fork_with_solc_support, get_forks_with_solc_support, ) @@ -207,13 +206,10 @@ def pytest_configure(config): called before the pytest-html plugin's pytest_configure to ensure that it uses the modified `htmlpath` option. """ - for fixture_format in FixtureFormats: + for fixture_format in FIXTURE_FORMATS.values(): config.addinivalue_line( "markers", - ( - f"{fixture_format.name.lower()}: " - f"{FixtureFormats.get_format_description(fixture_format)}" - ), + (f"{fixture_format.fixture_format_name.lower()}: {fixture_format.description}"), ) config.addinivalue_line( "markers", @@ -798,7 +794,7 @@ def base_test_parametrizer_func( When parametrize, indirect must be used along with the fixture format as value. """ fixture_format = request.param - assert isinstance(fixture_format, FixtureFormats) + assert issubclass(fixture_format, BaseFixture) class BaseTestWrapper(cls): # type: ignore def __init__(self, *args, **kwargs): @@ -830,7 +826,7 @@ def __init__(self, *args, **kwargs): request.node.config.fixture_path_relative = str( fixture_path.relative_to(output_dir) ) - request.node.config.fixture_format = fixture_format.value + request.node.config.fixture_format = fixture_format.fixture_format_name return BaseTestWrapper @@ -855,8 +851,8 @@ def pytest_generate_tests(metafunc: pytest.Metafunc): [ pytest.param( fixture_format, - id=fixture_format.name.lower(), - marks=[getattr(pytest.mark, fixture_format.name.lower())], + id=fixture_format.fixture_format_name.lower(), + marks=[getattr(pytest.mark, fixture_format.fixture_format_name.lower())], ) for fixture_format in test_type.supported_fixture_formats ], @@ -865,9 +861,9 @@ def pytest_generate_tests(metafunc: pytest.Metafunc): ) -def pytest_collection_modifyitems(config, items): +def pytest_collection_modifyitems(config: pytest.Config, items: List[pytest.Item]): """ - Remove pre-Paris tests parametrized to generate engine type fixtures; these + Remove pre-Paris tests parametrized to generate hive type fixtures; these can't be used in the Hive Pyspec Simulator. This can't be handled in this plugins pytest_generate_tests() as the fork @@ -876,23 +872,16 @@ def pytest_collection_modifyitems(config, items): for item in items[:]: # use a copy of the list, as we'll be modifying it if isinstance(item, EIPSpecTestItem): continue - if "fork" not in item.callspec.params or item.callspec.params["fork"] is None: + params: Dict[str, Any] = item.callspec.params # type: ignore + if "fork" not in params or params["fork"] is None: items.remove(item) continue - if item.callspec.params["fork"] < Paris: - # Even though the `state_test` test spec does not produce an engine STATE_TEST, it does - # produce a BLOCKCHAIN_TEST_ENGINE, so we need to remove it here. - # TODO: Ideally, the logic could be contained in the `FixtureFormat` class, we create - # a `fork_supported` method that returns True if the fork is supported. - if ("state_test" in item.callspec.params) and item.callspec.params[ - "state_test" - ].name.endswith("ENGINE"): + fork: Fork = params["fork"] + for spec_name in [spec_type.pytest_parameter_name() for spec_type in SPEC_TYPES]: + if spec_name in params and not params[spec_name].supports_fork(fork): items.remove(item) - if ("blockchain_test" in item.callspec.params) and item.callspec.params[ - "blockchain_test" - ].name.endswith("ENGINE"): - items.remove(item) - if "yul" in item.fixturenames: + break + if "yul" in item.fixturenames: # type: ignore item.add_marker(pytest.mark.yul_test) diff --git a/src/pytest_plugins/forks/tests/test_forks.py b/src/pytest_plugins/forks/tests/test_forks.py index 29911a47f6..46b0e4ebed 100644 --- a/src/pytest_plugins/forks/tests/test_forks.py +++ b/src/pytest_plugins/forks/tests/test_forks.py @@ -4,13 +4,7 @@ import pytest -from ethereum_test_forks import ( - ArrowGlacier, - Paris, - forks_from_until, - get_deployed_forks, - get_forks, -) +from ethereum_test_forks import ArrowGlacier, forks_from_until, get_deployed_forks, get_forks from ethereum_test_tools import StateTest @@ -47,11 +41,17 @@ def test_all_forks({StateTest.pytest_parameter_name()}): stdout = "\n".join(result.stdout.lines) for fork in forks_under_test: for fixture_format in StateTest.supported_fixture_formats: - if fixture_format.name.endswith("ENGINE") and fork < Paris: + if not fixture_format.supports_fork(fork): expected_passed -= 1 - assert f":test_all_forks[fork_{fork}-{fixture_format.name.lower()}]" not in stdout + assert ( + f":test_all_forks[fork_{fork}-{fixture_format.fixture_format_name.lower()}]" + not in stdout + ) continue - assert f":test_all_forks[fork_{fork}-{fixture_format.name.lower()}]" in stdout + assert ( + f":test_all_forks[fork_{fork}-{fixture_format.fixture_format_name.lower()}]" + in stdout + ) result.assert_outcomes( passed=expected_passed, @@ -85,11 +85,17 @@ def test_all_forks({StateTest.pytest_parameter_name()}): stdout = "\n".join(result.stdout.lines) for fork in forks_under_test: for fixture_format in StateTest.supported_fixture_formats: - if fixture_format.name.endswith("ENGINE") and fork < Paris: + if not fixture_format.supports_fork(fork): expected_passed -= 1 - assert f":test_all_forks[fork_{fork}-{fixture_format.name.lower()}]" not in stdout + assert ( + f":test_all_forks[fork_{fork}-{fixture_format.fixture_format_name.lower()}]" + not in stdout + ) continue - assert f":test_all_forks[fork_{fork}-{fixture_format.name.lower()}]" in stdout + assert ( + f":test_all_forks[fork_{fork}-{fixture_format.fixture_format_name.lower()}]" + in stdout + ) result.assert_outcomes( passed=expected_passed, failed=0, @@ -123,11 +129,17 @@ def test_all_forks({StateTest.pytest_parameter_name()}): expected_passed -= len(StateTest.supported_fixture_formats) for fork in forks_under_test: for fixture_format in StateTest.supported_fixture_formats: - if fixture_format.name.endswith("ENGINE") and fork < Paris: + if not fixture_format.supports_fork(fork): expected_passed -= 1 - assert f":test_all_forks[fork_{fork}-{fixture_format.name.lower()}]" not in stdout + assert ( + f":test_all_forks[fork_{fork}-{fixture_format.fixture_format_name.lower()}]" + not in stdout + ) continue - assert f":test_all_forks[fork_{fork}-{fixture_format.name.lower()}]" in stdout + assert ( + f":test_all_forks[fork_{fork}-{fixture_format.fixture_format_name.lower()}]" + in stdout + ) result.assert_outcomes( passed=expected_passed, failed=0, @@ -158,7 +170,10 @@ def test_all_forks({StateTest.pytest_parameter_name()}): stdout = "\n".join(result.stdout.lines) for fork in forks_under_test: for fixture_format in StateTest.supported_fixture_formats: - assert f":test_all_forks[fork_{fork}-{fixture_format.name.lower()}]" in stdout + assert ( + f":test_all_forks[fork_{fork}-{fixture_format.fixture_format_name.lower()}]" + in stdout + ) result.assert_outcomes( passed=expected_passed, failed=0,