Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,35 @@ jobs:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955
with:
submodules: recursive
fetch-depth: 0 # Fetch full history for commit comparison
- name: Setup Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
with:
python-version: "3.11"
- uses: ./.github/actions/setup-env
- name: Get changed files and save to disk
id: get-changed-files
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE_SHA="${{ github.event.pull_request.base.sha }}"
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
else
# On push or force push to the feature branch
BASE_SHA="${{ github.event.before }}"
HEAD_SHA="${{ github.sha }}"
fi

echo "Diffing commits: $BASE_SHA..$HEAD_SHA"

# Get changed files and save to disk
FILE_LIST="changed_files.txt"
git diff --name-only "$BASE_SHA" "$HEAD_SHA" > "$FILE_LIST"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is BASH_SHA going to be the head of the base branch, or the merge-base of the two branches?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BASE_SHA in this case would be the head of the base branch

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, so a file added in the base branch would get tested here, even if no changes were made to it in this pull request?

Copy link
Contributor

@gurukamath gurukamath Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The added file itself would not be tested but the addition might trigger a broader set of tests than what the PR explicitly changes. Perhaps this is not desirable and we should stick to comparing with the merge-base of the two branches. I'll give it a bit more thought

echo "Changed files saved to $FILE_LIST"
echo "file_list=$FILE_LIST" >> $GITHUB_OUTPUT
echo "List of files changed in the PR"
cat $FILE_LIST
- name: Run json infra tests
run: tox -e json_infra
run: tox -e json_infra -- --file-list="${{ steps.get-changed-files.outputs.file_list }}"
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7
with:
Expand Down
65 changes: 37 additions & 28 deletions src/ethereum_spec_tools/evm_tools/statetest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from copy import deepcopy
from dataclasses import dataclass
from io import StringIO
from typing import Any, Dict, Iterable, List, Optional, TextIO
from typing import Any, Dict, Generator, Iterable, List, Optional, TextIO

from ethereum.utils.hexadecimal import hex_to_bytes

Expand All @@ -35,6 +35,41 @@ class TestCase:
transaction: Dict


def read_test_case(
test_file_path: str, key: str, test: Dict[str, Any]
) -> Generator[TestCase, None, None]:
"""
Given a key and a value, return a `TestCase` object.
"""
env = test["env"]
if not isinstance(env, dict):
raise TypeError("env not dict")

pre = test["pre"]
if not isinstance(pre, dict):
raise TypeError("pre not dict")

transaction = test["transaction"]
if not isinstance(transaction, dict):
raise TypeError("transaction not dict")

for fork_name, content in test["post"].items():
for idx, post in enumerate(content):
if not isinstance(post, dict):
raise TypeError(f'post["{fork_name}"] not dict')

yield TestCase(
path=test_file_path,
key=key,
index=idx,
fork_name=fork_name,
post=post,
env=env,
pre=pre,
transaction=transaction,
)


def read_test_cases(test_file_path: str) -> Iterable[TestCase]:
"""
Given a path to a filled state test in JSON format, return all the
Expand All @@ -44,33 +79,7 @@ def read_test_cases(test_file_path: str) -> Iterable[TestCase]:
tests = json.load(test_file)

for key, test in tests.items():
env = test["env"]
if not isinstance(env, dict):
raise TypeError("env not dict")

pre = test["pre"]
if not isinstance(pre, dict):
raise TypeError("pre not dict")

transaction = test["transaction"]
if not isinstance(transaction, dict):
raise TypeError("transaction not dict")

for fork_name, content in test["post"].items():
for idx, post in enumerate(content):
if not isinstance(post, dict):
raise TypeError(f'post["{fork_name}"] not dict')

yield TestCase(
path=test_file_path,
key=key,
index=idx,
fork_name=fork_name,
post=post,
env=env,
pre=pre,
transaction=transaction,
)
yield from read_test_case(test_file_path, key, test)


def run_test_case(
Expand Down
115 changes: 5 additions & 110 deletions tests/json_infra/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""Tests related to json infrastructure."""

from typing import Dict, Optional, TypedDict
from typing import Dict, TypedDict

from typing_extensions import NotRequired

from .hardfork import TestHardfork


class _FixtureSource(TypedDict):
url: str
Expand Down Expand Up @@ -31,113 +33,6 @@ class _FixtureSource(TypedDict):
}


def _get_fixture_path(key: str) -> str:
return TEST_FIXTURES[key]["fixture_path"]


def _build_ethereum_test_paths(
base_path: str, legacy_fork: Optional[str] = None
) -> tuple:
if legacy_fork:
bc_path = f"{base_path}/LegacyTests/{legacy_fork}/BlockchainTests/"
state_path = (
f"{base_path}/LegacyTests/{legacy_fork}/GeneralStateTests/"
)
else:
bc_path = f"{base_path}/BlockchainTests/"
state_path = f"{base_path}/GeneralStateTests/"
return bc_path, state_path


def _build_eest_test_paths(base_path: str) -> tuple:
bc_path = f"{base_path}/fixtures/blockchain_tests/"
state_path = f"{base_path}/fixtures/state_tests/"
return bc_path, state_path


# Base paths
ETHEREUM_TESTS_BASE = _get_fixture_path("ethereum_tests")
EEST_TESTS_BASE = _get_fixture_path("latest_fork_tests")

# Ethereum test paths
(
PRE_CONSTANTINOPLE_BC_ETHEREUM_TESTS,
PRE_CONSTANTINOPLE_STATE_ETHEREUM_TESTS,
) = _build_ethereum_test_paths(ETHEREUM_TESTS_BASE, "Constantinople")
(
PRE_CANCUN_BC_ETHEREUM_TESTS,
PRE_CANCUN_STATE_ETHEREUM_TESTS,
) = _build_ethereum_test_paths(ETHEREUM_TESTS_BASE, "Cancun")
BC_ETHEREUM_TESTS, STATE_ETHEREUM_TESTS = _build_ethereum_test_paths(
ETHEREUM_TESTS_BASE
)

# EEST test paths
EEST_BC_TESTS, EEST_STATE_TESTS = _build_eest_test_paths(EEST_TESTS_BASE)

ForkConfig = TypedDict(
"ForkConfig",
{
"eels_fork": str,
"blockchain_test_dirs": list[str],
"state_test_dirs": list[str],
},
)


def _create_fork_config(
eels_fork: str, bc_dirs: list, state_dirs: list
) -> ForkConfig:
return {
"eels_fork": eels_fork,
"blockchain_test_dirs": bc_dirs,
"state_test_dirs": state_dirs,
}


PRE_CONSTANTINOPLE_DIRS = (
[PRE_CONSTANTINOPLE_BC_ETHEREUM_TESTS, EEST_BC_TESTS],
[PRE_CONSTANTINOPLE_STATE_ETHEREUM_TESTS, EEST_STATE_TESTS],
)

PRE_CANCUN_DIRS = (
[PRE_CANCUN_BC_ETHEREUM_TESTS, EEST_BC_TESTS],
[PRE_CANCUN_STATE_ETHEREUM_TESTS, EEST_STATE_TESTS],
)

CURRENT_DIRS = (
[BC_ETHEREUM_TESTS, EEST_BC_TESTS],
[STATE_ETHEREUM_TESTS, EEST_STATE_TESTS],
)

FORKS: Dict[str, ForkConfig] = {
**{
json_fork: _create_fork_config(eels_fork, *PRE_CONSTANTINOPLE_DIRS)
for json_fork, eels_fork in [
("Frontier", "frontier"),
("Homestead", "homestead"),
("EIP150", "tangerine_whistle"),
("EIP158", "spurious_dragon"),
("Byzantium", "byzantium"),
("ConstantinopleFix", "constantinople"),
]
},
**{
json_fork: _create_fork_config(eels_fork, *PRE_CANCUN_DIRS)
for json_fork, eels_fork in [
("Istanbul", "istanbul"),
("Berlin", "berlin"),
("London", "london"),
("Paris", "paris"),
("Shanghai", "shanghai"),
]
},
**{
json_fork: _create_fork_config(eels_fork, *CURRENT_DIRS)
for json_fork, eels_fork in [
("Cancun", "cancun"),
("Prague", "prague"),
("Osaka", "osaka"),
]
},
FORKS: Dict[str, TestHardfork] = {
fork.json_test_name: fork for fork in TestHardfork.discover()
}
Loading
Loading