Skip to content

Commit afa9744

Browse files
committed
Migrate ethdebug CLI tests to isolest
1 parent 2ca6163 commit afa9744

21 files changed

Lines changed: 965 additions & 0 deletions

test/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ set(libsolidity_sources
7979
libsolidity/ASTJSONTest.h
8080
libsolidity/ErrorCheck.cpp
8181
libsolidity/ErrorCheck.h
82+
libsolidity/EthdebugTest.cpp
83+
libsolidity/EthdebugTest.h
8284
libsolidity/FunctionDependencyGraphTest.cpp
8385
libsolidity/FunctionDependencyGraphTest.h
8486
libsolidity/GasCosts.cpp
@@ -87,6 +89,8 @@ set(libsolidity_sources
8789
libsolidity/GasTest.h
8890
libsolidity/Imports.cpp
8991
libsolidity/InlineAssembly.cpp
92+
libsolidity/JSONExpectationTest.cpp
93+
libsolidity/JSONExpectationTest.h
9094
libsolidity/LibSolc.cpp
9195
libsolidity/Metadata.cpp
9296
libsolidity/MemoryGuardTest.cpp

test/InteractiveTests.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <test/libsolidity/ABIJsonTest.h>
2323
#include <test/libsolidity/ASTJSONTest.h>
2424
#include <test/libsolidity/ASTPropertyTest.h>
25+
#include <test/libsolidity/EthdebugTest.h>
2526
#include <libsolidity/FunctionDependencyGraphTest.h>
2627
#include <test/libsolidity/GasTest.h>
2728
#include <test/libsolidity/MemoryGuardTest.h>
@@ -93,6 +94,7 @@ Testsuite const g_interactiveTestsuites[] = {
9394
{"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create},
9495
{"JSON Natspec", "libsolidity", "natspecJSON", false, false, &NatspecJSONTest::create},
9596
{"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create},
97+
{"Ethdebug", "libsolidity", "ethdebugTests", false, false, &EthdebugTest::create},
9698
{"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create},
9799
{"Memory Guard", "libsolidity", "memoryGuardTests", false, false, &MemoryGuardTest::create},
98100
{"AST Properties", "libsolidity", "astPropertyTests", false, false, &ASTPropertyTest::create},

test/libsolidity/EthdebugTest.cpp

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
This file is part of solidity.
3+
4+
solidity is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
solidity is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with solidity. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
// SPDX-License-Identifier: GPL-3.0
18+
19+
#include <test/libsolidity/EthdebugTest.h>
20+
#include <test/Common.h>
21+
22+
#include <liblangutil/DebugInfoSelection.h>
23+
24+
#include <libsolidity/interface/OptimiserSettings.h>
25+
26+
#include <boost/throw_exception.hpp>
27+
28+
#include <algorithm>
29+
#include <stdexcept>
30+
31+
using namespace solidity;
32+
using namespace solidity::frontend;
33+
using namespace solidity::frontend::test;
34+
using namespace solidity::langutil;
35+
using namespace std::string_literals;
36+
37+
EthdebugTest::EthdebugTest(std::string const& _filename):
38+
JSONExpectationTest(_filename)
39+
{
40+
m_optimise = m_reader.boolSetting("optimize", false);
41+
m_optimiseYul = m_reader.boolSetting("optimize-yul", false);
42+
m_useSSACFG = m_reader.boolSetting("compileViaSSACFG", false);
43+
44+
auto const revertStrings = revertStringsFromString(m_reader.stringSetting("revertStrings", "default"));
45+
if (!revertStrings)
46+
BOOST_THROW_EXCEPTION(std::runtime_error("Invalid revertStrings setting."));
47+
m_revertStrings = *revertStrings;
48+
}
49+
50+
void EthdebugTest::setupCompiler(CompilerStack& _compiler)
51+
{
52+
AnalysisFramework::setupCompiler(_compiler);
53+
54+
_compiler.setViaIR(true);
55+
_compiler.setExperimental(true);
56+
if (m_useSSACFG)
57+
_compiler.setViaSSACFG(true);
58+
59+
DebugInfoSelection selection = DebugInfoSelection::Default();
60+
selection.enable("ethdebug");
61+
_compiler.selectDebugInfo(selection);
62+
63+
_compiler.setMetadataFormat(CompilerStack::MetadataFormat::NoMetadata);
64+
_compiler.setRevertStringBehaviour(m_revertStrings);
65+
66+
OptimiserSettings settings = m_optimise ? OptimiserSettings::standard() : OptimiserSettings::minimal();
67+
if (m_optimiseYul)
68+
settings.runYulOptimiser = true;
69+
_compiler.setOptimiserSettings(settings);
70+
}
71+
72+
JSONExpectationTest::PathParts EthdebugTest::splitPath(std::string_view _path) const
73+
{
74+
if (_path.empty())
75+
BOOST_THROW_EXCEPTION(std::runtime_error("Empty path in expectation"));
76+
if (_path[0] == ' ' || _path[0] == '\t')
77+
BOOST_THROW_EXCEPTION(std::runtime_error(
78+
"Path must not have leading whitespace: "s.append(_path)
79+
));
80+
81+
if (_path[0] == '(')
82+
return JSONExpectationTest::splitPath(_path);
83+
84+
if (_path[0] == '.')
85+
{
86+
auto const secondDot = _path.find('.', 1);
87+
if (secondDot == std::string_view::npos)
88+
return {std::string_view{}, _path.substr(1), std::string_view{}};
89+
return {
90+
std::string_view{},
91+
_path.substr(1, secondDot - 1),
92+
_path.substr(secondDot + 1)
93+
};
94+
}
95+
96+
auto const colonPos = _path.find(':');
97+
size_t const searchStart = colonPos == std::string_view::npos ? 0 : colonPos + 1;
98+
99+
auto const outputDot = _path.find('.', searchStart);
100+
if (outputDot == std::string_view::npos)
101+
return {_path, std::string_view{}, std::string_view{}};
102+
103+
auto const pathDot = _path.find('.', outputDot + 1);
104+
if (pathDot == std::string_view::npos)
105+
return {
106+
_path.substr(0, outputDot),
107+
_path.substr(outputDot + 1),
108+
std::string_view{}
109+
};
110+
return {
111+
_path.substr(0, outputDot),
112+
_path.substr(outputDot + 1, pathDot - outputDot - 1),
113+
_path.substr(pathDot + 1)
114+
};
115+
}
116+
117+
std::optional<Json> EthdebugTest::fetchOutput(
118+
std::string_view _qualifiedContract,
119+
std::string_view _outputName
120+
) const
121+
{
122+
if (compiler().state() < CompilerStack::State::CompilationSuccessful)
123+
return std::nullopt;
124+
125+
if (_qualifiedContract.empty())
126+
{
127+
if (_outputName == "compilation")
128+
return compiler().ethdebugCompilation();
129+
if (_outputName == "resources")
130+
return compiler().ethdebug();
131+
return std::nullopt;
132+
}
133+
134+
if (auto const resolved = resolveContractName(_qualifiedContract))
135+
{
136+
if (_outputName == "creation")
137+
return compiler().ethdebug(*resolved);
138+
if (_outputName == "runtime")
139+
return compiler().ethdebugRuntime(*resolved);
140+
if (_outputName == "contract")
141+
{
142+
Json const creation = compiler().ethdebug(*resolved);
143+
if (creation.is_null() || !creation.contains("contract"))
144+
return std::nullopt;
145+
return creation["contract"];
146+
}
147+
}
148+
return std::nullopt;
149+
}
150+
151+
std::optional<std::string> EthdebugTest::resolveContractName(std::string_view _name) const
152+
{
153+
auto const& contractNames = compiler().contractNames();
154+
155+
auto const isMatch = [&](std::string const& contractName) {
156+
return contractName == _name || compiler().contractDefinition(contractName).name() == _name;
157+
};
158+
159+
if (std::ranges::count_if(contractNames, isMatch) != 1)
160+
return std::nullopt;
161+
return *std::ranges::find_if(contractNames, isMatch);
162+
}

test/libsolidity/EthdebugTest.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
This file is part of solidity.
3+
4+
solidity is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
solidity is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with solidity. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
// SPDX-License-Identifier: GPL-3.0
18+
19+
#pragma once
20+
21+
#include <test/libsolidity/JSONExpectationTest.h>
22+
23+
#include <libsolidity/interface/DebugSettings.h>
24+
25+
namespace solidity::frontend::test
26+
{
27+
/// Isoltest test case for asserting against the compiler's ethdebug JSON output.
28+
///
29+
/// Settings (in the `// ====` block):
30+
/// - `EVMVersion: <constraint>` — gates whether the test runs (inherited).
31+
/// - `optimize: <bool>` — enable optimizer (default: false).
32+
/// - `optimize-yul: <bool>` — additionally run the Yul optimizer (default: false).
33+
/// - `compileViaSSACFG: <bool>` — route codegen through the SSA-CFG (default: false).
34+
/// - `revertStrings: default|strip|debug|verboseDebug`
35+
/// — revert string behavior (default: default).
36+
///
37+
/// Compilation is always performed with `viaIR=true`, `experimental=true`, ethdebug
38+
/// debug info enabled, and metadata stripped, so the output is stable across builds.
39+
///
40+
/// Scope keys exposed to expectations:
41+
/// - Globals: `.resources`, `.compilation`.
42+
/// - Per contract: `Contract.creation`, `Contract.runtime`, `Contract.contract`.
43+
/// - Source-qualified per contract (when needed to disambiguate same-named
44+
/// contracts in different sources): `source.sol:Contract.creation`, etc.
45+
class EthdebugTest: public JSONExpectationTest
46+
{
47+
public:
48+
static std::unique_ptr<TestCase> create(Config const& _config)
49+
{
50+
return std::make_unique<EthdebugTest>(_config.filename);
51+
}
52+
53+
EthdebugTest(std::string const& _filename);
54+
55+
protected:
56+
void setupCompiler(CompilerStack& _compiler) override;
57+
PathParts splitPath(std::string_view _path) const override;
58+
std::optional<Json> fetchOutput(
59+
std::string_view _qualifiedContract,
60+
std::string_view _outputName
61+
) const override;
62+
63+
private:
64+
std::optional<std::string> resolveContractName(std::string_view _name) const;
65+
66+
bool m_optimise = false;
67+
bool m_optimiseYul = false;
68+
bool m_useSSACFG = false;
69+
RevertStrings m_revertStrings = RevertStrings::Default;
70+
};
71+
72+
}

0 commit comments

Comments
 (0)