Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
29 changes: 29 additions & 0 deletions tests/json_infra/helpers/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,30 @@
"""Helpers to load tests from JSON files."""

import json
from functools import lru_cache
from typing import Dict


@lru_cache(maxsize=100)
def load_json_file(test_file: str) -> Dict:
"""
Load and cache a JSON fixture file.

Uses LRU (Least Recently Used) cache to avoid re-reading the same
JSON files multiple times during test execution. This is especially
important when running tests in parallel, as multiple test cases
from the same file would otherwise cause redundant file I/O.

The cache is bounded to 100 files, which provides a good balance
between memory usage and performance. When the cache is full, the
least recently accessed file will be evicted.

Args:
test_file: Path to the JSON fixture file

Returns:
Parsed JSON data as a dictionary

"""
with open(test_file, "r") as fp:
return json.load(fp)
50 changes: 27 additions & 23 deletions tests/json_infra/helpers/load_blockchain_tests.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Helpers to load and run blockchain tests from JSON files."""

import importlib
import json
import os.path
from glob import glob
from typing import Any, Dict, Generator
Expand All @@ -19,6 +18,7 @@
from ethereum_spec_tools.evm_tools.loaders.fixture_loader import Load

from .. import FORKS
from . import load_json_file
from .exceptional_test_patterns import exceptional_blockchain_test_patterns


Expand All @@ -34,8 +34,7 @@ def run_blockchain_st_test(test_case: Dict, load: Load) -> None:
test_file = test_case["test_file"]
test_key = test_case["test_key"]

with open(test_file, "r") as fp:
data = json.load(fp)
data = load_json_file(test_file)

json_data = data[test_key]

Expand Down Expand Up @@ -140,27 +139,26 @@ def load_json_fixture(test_file: str, json_fork: str) -> Generator:
# Ex: Extract "world.json" from "path/to/file/world.json"
# Extract the filename without the extension. Ex: Extract "world" from
# "world.json"
with open(test_file, "r") as fp:
data = json.load(fp)
data = load_json_file(test_file)

# Search tests by looking at the `network` attribute
found_keys = []
for key, test in data.items():
if "network" not in test:
continue
# Search tests by looking at the `network` attribute
found_keys = []
for key, test in data.items():
if "network" not in test:
continue

if test["network"] == json_fork:
found_keys.append(key)
if test["network"] == json_fork:
found_keys.append(key)

if not any(found_keys):
raise NoTestsFoundError
if not any(found_keys):
raise NoTestsFoundError

for _key in found_keys:
yield {
"test_file": test_file,
"test_key": _key,
"json_fork": json_fork,
}
for _key in found_keys:
yield {
"test_file": test_file,
"test_key": _key,
"json_fork": json_fork,
}


def fetch_blockchain_tests(
Expand Down Expand Up @@ -202,18 +200,24 @@ def fetch_blockchain_tests(
+ ")"
)
_test_case["eels_fork"] = eels_fork

# Build marks list with xdist_group for all tests
marks = [pytest.mark.xdist_group(_test_case["test_file"])]

if any(
x.search(_identifier) for x in test_patterns.expected_fail
):
continue
elif any(x.search(_identifier) for x in test_patterns.slow):
yield pytest.param(_test_case, marks=pytest.mark.slow)
marks.append(pytest.mark.slow)
yield pytest.param(_test_case, marks=marks)
elif any(
x.search(_identifier) for x in test_patterns.big_memory
):
yield pytest.param(_test_case, marks=pytest.mark.bigmem)
marks.append(pytest.mark.bigmem)
yield pytest.param(_test_case, marks=marks)
else:
yield _test_case
yield pytest.param(_test_case, marks=marks)
except NoTestsFoundError:
# file doesn't contain tests for the given fork
continue
Expand Down
12 changes: 8 additions & 4 deletions tests/json_infra/helpers/load_state_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from ethereum_spec_tools.evm_tools.t8n import T8N

from .. import FORKS
from . import load_json_file
from .exceptional_test_patterns import exceptional_state_test_patterns

parser = create_parser()
Expand Down Expand Up @@ -53,10 +54,14 @@ def fetch_state_tests(json_fork: str) -> Generator:
"json_fork": json_fork,
}

# Build marks list with xdist_group for all tests
marks = [pytest.mark.xdist_group(test_case_dict["test_file"])]

if any(x.search(test_case.key) for x in test_patterns.slow):
yield pytest.param(test_case_dict, marks=pytest.mark.slow)
marks.append(pytest.mark.slow)
yield pytest.param(test_case_dict, marks=marks)
else:
yield test_case_dict
yield pytest.param(test_case_dict, marks=marks)


def idfn(test_case: Dict) -> str:
Expand All @@ -79,8 +84,7 @@ def run_state_test(test_case: Dict[str, str]) -> None:
test_key = test_case["test_key"]
index = test_case["index"]
json_fork = test_case["json_fork"]
with open(test_file) as f:
tests = json.load(f)
tests = load_json_file(test_file)

env = tests[test_key]["env"]
try:
Expand Down
2 changes: 2 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ commands =
--cov-branch \
--ignore-glob='tests/json_infra/fixtures/*' \
--basetemp="{temp_dir}/pytest" \
--dist=loadgroup \
tests/json_infra

[testenv:py3]
Expand Down Expand Up @@ -105,6 +106,7 @@ commands =
--ignore-glob='eest_tests/*' \
--basetemp="{temp_dir}/pytest" \
--optimized \
--dist=loadgroup \
tests/json_infra

[testenv:doc]
Expand Down
Loading