Skip to content

Test ethdebug program output against corresponding schema #16009

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: eof_source_locations_unoptimized
Choose a base branch
from
Open
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
18 changes: 18 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,8 @@ jobs:
chk_pylint:
<<: *base_ubuntu2404_small
steps:
- install_python3:
packages: pyyaml jsonschema pytest
- checkout
- run: pylint --version
- run:
Expand Down Expand Up @@ -1534,6 +1536,19 @@ jobs:
- reports/externalTests/
- matrix_notify_failure_unless_pr

t_ethdebug_output_validity:
<<: *base_node_small
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- install_python3:
packages: pyyaml jsonschema pytest
- run:
name: Ethdebug validity tests
command: |
pytest test/ethdebugSchemaTests --solc-binary-path=/tmp/workspace/solc/solc-static-linux -v

c_ext_benchmarks:
<<: *base_node_small
steps:
Expand Down Expand Up @@ -1927,6 +1942,9 @@ workflows:
#- t_ext: *job_native_compile_ext_chainlink
#- t_ext: *job_native_compile_ext_bleeps

- t_ethdebug_output_validity:
<<: *requires_b_ubu_static

- c_ext_benchmarks:
<<: *requires_nothing
requires:
Expand Down
49 changes: 49 additions & 0 deletions test/ethdebugSchemaTests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import shutil
import subprocess
from pathlib import Path

import pytest
import referencing
import yaml
from referencing.jsonschema import DRAFT202012


def pytest_addoption(parser):
parser.addoption("--solc-binary-path", type=Path, required=True, help="Path to the solidity compiler binary.")


@pytest.fixture
def solc_path(request):
solc_path = request.config.getoption("--solc-binary-path")
assert solc_path.is_file()
assert solc_path.exists()
return solc_path


@pytest.fixture(scope="module")
def ethdebug_clone_dir(tmpdir_factory):
temporary_dir = Path(tmpdir_factory.mktemp("data"))
yield temporary_dir
shutil.rmtree(temporary_dir)


@pytest.fixture(scope="module")
def ethdebug_schema_repository(ethdebug_clone_dir):
process = subprocess.run(
["git", "clone", "https://github.com/ethdebug/format.git", ethdebug_clone_dir],
encoding='utf8',
capture_output=True,
check=True
)
assert process.returncode == 0

registry = referencing.Registry()
for path in (ethdebug_clone_dir / "schemas").rglob("*.yaml"):
with open(path, "r", encoding="utf8") as f:
schema = yaml.safe_load(f)
if "$id" in schema:
resource = referencing.Resource.from_contents(schema, DRAFT202012)
registry = resource @ registry
else:
raise ValueError(f"Schema did not define an $id: {path}")
return registry
27 changes: 27 additions & 0 deletions test/ethdebugSchemaTests/input_file.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"language": "Solidity",
"sources": {
"a.sol": {
"content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }"
},
"b.sol": {
"content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function b(uint x) public pure { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }"
}
},
"settings": {
"viaIR": true,
"debug": {
"debugInfo": [
"ethdebug"
]
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode.ethdebug",
"evm.deployedBytecode.ethdebug"
]
}
}
}
}
29 changes: 29 additions & 0 deletions test/ethdebugSchemaTests/input_file_eof.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"language": "Solidity",
"sources": {
"a.sol": {
"content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }"
},
"b.sol": {
"content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function b(uint x) public pure { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }"
}
},
"settings": {
"eofVersion": 1,
"evmVersion": "osaka",
"viaIR": true,
"debug": {
"debugInfo": [
"ethdebug"
]
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode.ethdebug",
"evm.deployedBytecode.ethdebug"
]
}
}
}
}
51 changes: 51 additions & 0 deletions test/ethdebugSchemaTests/test_ethdebug_schema_conformity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env python3

import json
import subprocess
from pathlib import Path

import jsonschema
import pytest


def get_nested_value(dictionary, *keys):
for key in keys:
dictionary = dictionary[key]
return dictionary


@pytest.fixture(params=["input_file.json", "input_file_eof.json"])
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be nice to have this already implemented in a way that it will dynamically pick files and test it.. e.g. the test will search for all json files defined in a specific directory and will then verify whether ethdebug output is correct

Copy link
Member Author

Choose a reason for hiding this comment

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

this is already possible by adding more stuff to the params kwarg. depending on what you want to test, you can rglob json files from some directory ahead of test time and add them there to the list

def solc_output(request, solc_path):
testfile_dir = Path(__file__).parent
with open(testfile_dir / request.param, "r", encoding="utf8") as f:
source = json.load(f)

process = subprocess.run(
[solc_path, "--standard-json"],
input=json.dumps(source),
encoding='utf8',
capture_output=True,
check=True,
)
assert process.returncode == 0
return json.loads(process.stdout)


@pytest.mark.parametrize("output_selection", ["evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"], ids=str)
def test_program_schema(
output_selection,
ethdebug_schema_repository,
solc_output
):
validator = jsonschema.Draft202012Validator(
schema={"$ref": "schema:ethdebug/format/program"},
registry=ethdebug_schema_repository
)
assert "contracts" in solc_output
for contract in solc_output["contracts"].keys():
contract_output = solc_output["contracts"][contract]
assert len(contract_output) > 0
for source in contract_output.keys():
source_output = contract_output[source]
ethdebug_data = get_nested_value(source_output, *(output_selection.split(".")))
validator.validate(ethdebug_data)