Skip to content

Commit febe7a3

Browse files
committed
[isoltest] Add support for builtin functions and test hooks.
1 parent 5c6633f commit febe7a3

14 files changed

+418
-86
lines changed

liblangutil/Common.h

+5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ inline bool isIdentifierPart(char c)
4545
return isIdentifierStart(c) || isDecimalDigit(c);
4646
}
4747

48+
inline bool isIdentifierPartWithDot(char c)
49+
{
50+
return isIdentifierPart(c) || c == '.';
51+
}
52+
4853
inline int hexValue(char c)
4954
{
5055
if (c >= '0' && c <= '9')

test/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ set(liblangutil_sources
5858
detect_stray_source_files("${liblangutil_sources}" "liblangutil/")
5959

6060
set(libsolidity_sources
61+
libsolidity/TestHook.h
62+
libsolidity/hooks/SmokeHook.h
6163
libsolidity/ABIDecoderTests.cpp
6264
libsolidity/ABIEncoderTests.cpp
6365
libsolidity/ABIJsonTest.cpp

test/libsolidity/SemanticTest.cpp

+136-19
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@
1313
*/
1414

1515
#include <test/libsolidity/SemanticTest.h>
16+
1617
#include <libsolutil/Whiskers.h>
1718
#include <libyul/Exceptions.h>
1819
#include <test/Common.h>
20+
#include <test/libsolidity/util/BytesUtils.h>
21+
#include <test/libsolidity/hooks/SmokeHook.h>
22+
#include <test/libsolidity/TestHook.h>
23+
1924
#include <boost/algorithm/string.hpp>
2025
#include <boost/algorithm/string/predicate.hpp>
2126
#include <boost/algorithm/string/trim.hpp>
@@ -25,6 +30,7 @@
2530
#include <cctype>
2631
#include <fstream>
2732
#include <memory>
33+
#include <optional>
2834
#include <stdexcept>
2935

3036
using namespace std;
@@ -47,6 +53,17 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
4753
m_lineOffset(m_reader.lineNumber()),
4854
m_enforceViaYul(enforceViaYul)
4955
{
56+
using namespace placeholders;
57+
auto simpleSmokeBuiltin = bind(&SemanticTest::builtinSmokeTest, this, _1);
58+
m_builtins = {
59+
{"smoke.test0", simpleSmokeBuiltin},
60+
{"smoke.test1", simpleSmokeBuiltin},
61+
{"smoke.test2", simpleSmokeBuiltin},
62+
};
63+
m_testHooks = {
64+
make_shared<SmokeHook>()
65+
};
66+
5067
string choice = m_reader.stringSetting("compileViaYul", "default");
5168
if (choice == "also")
5269
{
@@ -119,7 +136,18 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
119136
return result;
120137
}
121138

122-
TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm)
139+
void SemanticTest::addBuiltin(string _name, Builtin _builtin)
140+
{
141+
m_builtins[_name] = _builtin;
142+
}
143+
144+
TestCase::TestResult SemanticTest::runTest(
145+
ostream& _stream,
146+
string const& _linePrefix,
147+
bool _formatted,
148+
bool _compileViaYul,
149+
bool _compileToEwasm
150+
)
123151
{
124152
bool success = true;
125153

@@ -142,21 +170,40 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
142170
if (_compileViaYul)
143171
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl;
144172

145-
for (auto& test: m_tests)
173+
for (TestFunctionCall& test: m_tests)
146174
test.reset();
147175

148176
map<string, solidity::test::Address> libraries;
149177

150178
bool constructed = false;
151179

152-
for (auto& test: m_tests)
180+
// Iterate through the test calls and set the previous call.
181+
TestFunctionCall* previousCall{nullptr};
182+
for (TestFunctionCall& test: m_tests)
183+
{
184+
test.setPreviousCall(previousCall);
185+
test.setTestHooks(&m_testHooks);
186+
previousCall = &test;
187+
}
188+
189+
for (std::shared_ptr<TestHook>& hook: m_testHooks)
190+
hook->beginTestCase();
191+
192+
for (TestFunctionCall& test: m_tests)
153193
{
194+
for (std::shared_ptr<TestHook>& hook: m_testHooks)
195+
hook->beforeFunctionCall(test);
196+
154197
if (constructed)
155198
{
156-
soltestAssert(test.call().kind != FunctionCall::Kind::Library, "Libraries have to be deployed before any other call.");
199+
soltestAssert(
200+
test.call().kind != FunctionCall::Kind::Library,
201+
"Libraries have to be deployed before any other call."
202+
);
157203
soltestAssert(
158204
test.call().kind != FunctionCall::Kind::Constructor,
159-
"Constructor has to be the first function call expect for library deployments.");
205+
"Constructor has to be the first function call expect for library deployments."
206+
);
160207
}
161208
else if (test.call().kind == FunctionCall::Kind::Library)
162209
{
@@ -197,6 +244,18 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
197244
bytes output;
198245
if (test.call().kind == FunctionCall::Kind::LowLevel)
199246
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
247+
else if (test.call().kind == FunctionCall::Kind::Builtin)
248+
{
249+
auto builtin = m_builtins[test.call().signature];
250+
std::optional<bytes> builtinOutput{builtin(test.call())};
251+
if (builtinOutput.has_value())
252+
{
253+
test.setFailure(false);
254+
output = builtinOutput.value();
255+
}
256+
else
257+
test.setFailure(true);
258+
}
200259
else
201260
{
202261
soltestAssert(
@@ -212,20 +271,60 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
212271
);
213272
}
214273

215-
bool outputMismatch = (output != test.call().expectations.rawBytes());
216-
// Pre byzantium, it was not possible to return failure data, so we disregard
217-
// output mismatch for those EVM versions.
218-
if (test.call().expectations.failure && !m_transactionSuccessful && !m_evmVersion.supportsReturndata())
219-
outputMismatch = false;
220-
if (m_transactionSuccessful != !test.call().expectations.failure || outputMismatch)
221-
success = false;
274+
bytes expectationOutput;
275+
if (test.call().expectations.builtin)
276+
{
277+
auto builtin = m_builtins[test.call().expectations.builtin->signature];
278+
if (std::optional<bytes> builtinResult = builtin(*test.call().expectations.builtin))
279+
expectationOutput = builtinResult.value();
280+
else
281+
test.setFailure(true);
282+
}
283+
else
284+
expectationOutput = test.call().expectations.rawBytes();
222285

223-
test.setFailure(!m_transactionSuccessful);
286+
bool outputMismatch = (output != expectationOutput);
287+
if (test.call().kind == FunctionCall::Kind::Builtin)
288+
{
289+
if (outputMismatch)
290+
success = false;
291+
}
292+
else
293+
{
294+
// Pre byzantium, it was not possible to return failure data, so we disregard
295+
// output mismatch for those EVM versions.
296+
if (test.call().expectations.failure && !m_transactionSuccessful && !m_evmVersion.supportsReturndata())
297+
outputMismatch = false;
298+
if (m_transactionSuccessful != !test.call().expectations.failure || outputMismatch)
299+
success = false;
300+
test.setFailure(!m_transactionSuccessful);
301+
}
224302
test.setRawBytes(std::move(output));
225303
test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName()));
226304
}
305+
306+
for (std::shared_ptr<TestHook>& hook: m_testHooks)
307+
hook->afterFunctionCall(test);
308+
}
309+
310+
// The artificialFunctionCall is an artificially created function call,
311+
// where it's previous call is pointing to the last call of the test.
312+
TestFunctionCall artificialFunctionCall(FunctionCall{});
313+
artificialFunctionCall.setTestHooks(&m_testHooks);
314+
artificialFunctionCall.setPreviousCall(previousCall);
315+
for (std::shared_ptr<TestHook>& hook: m_testHooks)
316+
{
317+
hook->beforeFunctionCall(artificialFunctionCall);
318+
hook->afterFunctionCall(artificialFunctionCall);
227319
}
228320

321+
for (std::shared_ptr<TestHook>& hook: m_testHooks)
322+
hook->endTestCase();
323+
324+
for (TestFunctionCall& test: m_tests)
325+
for (std::shared_ptr<TestHook>& hook: m_testHooks)
326+
success &= hook->verifyFunctionCall(test);
327+
229328
if (!m_runWithYul && _compileViaYul)
230329
{
231330
m_compileViaYulCanBeSet = success;
@@ -241,15 +340,15 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
241340
if (!success && (m_runWithYul || !_compileViaYul))
242341
{
243342
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
244-
for (auto const& test: m_tests)
343+
for (TestFunctionCall const& test: m_tests)
245344
{
246345
ErrorReporter errorReporter;
247346
_stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl;
248347
_stream << errorReporter.format(_linePrefix, _formatted);
249348
}
250349
_stream << endl;
251350
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
252-
for (auto const& test: m_tests)
351+
for (TestFunctionCall const& test: m_tests)
253352
{
254353
ErrorReporter errorReporter;
255354
_stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl;
@@ -320,7 +419,7 @@ void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool
320419

321420
void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) const
322421
{
323-
for (auto const& test: m_tests)
422+
for (TestFunctionCall const& test: m_tests)
324423
_stream << test.format("", true, false) << endl;
325424
}
326425

@@ -340,13 +439,31 @@ void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePre
340439

341440
void SemanticTest::parseExpectations(istream& _stream)
342441
{
343-
TestFileParser parser{_stream};
442+
TestFileParser parser{_stream, &this->m_builtins};
344443
auto functionCalls = parser.parseFunctionCalls(m_lineOffset);
345-
std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
444+
move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
346445
}
347446

348-
bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments, map<string, solidity::test::Address> const& _libraries)
447+
bool SemanticTest::deploy(
448+
string const& _contractName,
449+
u256 const& _value,
450+
bytes const& _arguments,
451+
map<string, solidity::test::Address> const& _libraries
452+
)
349453
{
350454
auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries);
351455
return !output.empty() && m_transactionSuccessful;
352456
}
457+
458+
std::optional<bytes> SemanticTest::builtinSmokeTest(FunctionCall const& call)
459+
{
460+
// This function is only used in test/libsolidity/semanticTests/builtins/smoke.sol.
461+
std::optional<bytes> result;
462+
if (call.arguments.parameters.size() < 3)
463+
{
464+
result = bytes();
465+
for (const auto& parameter: call.arguments.parameters)
466+
result.value() += util::toBigEndian(u256{util::fromHex(parameter.rawString)});
467+
}
468+
return result;
469+
}

test/libsolidity/SemanticTest.h

+14-5
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414

1515
#pragma once
1616

17-
#include <test/libsolidity/util/TestFileParser.h>
18-
#include <test/libsolidity/util/TestFunctionCall.h>
19-
#include <test/libsolidity/SolidityExecutionFramework.h>
20-
#include <test/libsolidity/AnalysisFramework.h>
21-
#include <test/TestCase.h>
2217
#include <liblangutil/Exceptions.h>
2318
#include <libsolutil/AnsiColorized.h>
19+
#include <test/TestCase.h>
20+
#include <test/libsolidity/AnalysisFramework.h>
21+
#include <test/libsolidity/SolidityExecutionFramework.h>
22+
#include <test/libsolidity/TestHook.h>
23+
#include <test/libsolidity/util/TestFileParser.h>
24+
#include <test/libsolidity/util/TestFunctionCall.h>
2425

2526
#include <iosfwd>
2627
#include <string>
@@ -58,7 +59,13 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict
5859
/// Compiles and deploys currently held source.
5960
/// Returns true if deployment was successful, false otherwise.
6061
bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map<std::string, solidity::test::Address> const& _libraries = {});
62+
63+
void addBuiltin(std::string _name, Builtin _builtin);
64+
6165
private:
66+
// builtin functions
67+
std::optional<bytes> builtinSmokeTest(FunctionCall const& call);
68+
6269
TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm);
6370
SourceMap m_sources;
6471
std::size_t m_lineOffset;
@@ -70,6 +77,8 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict
7077
bool m_runWithABIEncoderV1Only = false;
7178
bool m_allowNonExistingFunctions = false;
7279
bool m_compileViaYulCanBeSet = false;
80+
Builtins m_builtins;
81+
TestHooks m_testHooks;
7382
};
7483

7584
}

test/libsolidity/TestHook.h

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
This file is part of solidity.
3+
solidity is free software: you can redistribute it and/or modify
4+
it under the terms of the GNU General Public License as published by
5+
the Free Software Foundation, either version 3 of the License, or
6+
(at your option) any later version.
7+
solidity is distributed in the hope that it will be useful,
8+
but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
GNU General Public License for more details.
11+
You should have received a copy of the GNU General Public License
12+
along with solidity. If not, see <http://www.gnu.org/licenses/>.
13+
*/
14+
15+
#pragma once
16+
17+
#include <test/libsolidity/util/SoltestErrors.h>
18+
19+
#include <memory>
20+
#include <vector>
21+
22+
namespace solidity::frontend::test
23+
{
24+
25+
class TestFunctionCall;
26+
27+
class TestHook
28+
{
29+
public:
30+
TestHook() = default;
31+
virtual ~TestHook() = default;
32+
33+
virtual void beginTestCase() = 0;
34+
virtual void beforeFunctionCall(TestFunctionCall const& _call) = 0;
35+
virtual void afterFunctionCall(TestFunctionCall const& _call) = 0;
36+
virtual void endTestCase() = 0;
37+
38+
virtual bool verifyFunctionCall(TestFunctionCall const& _call) = 0;
39+
40+
virtual std::string formatFunctionCall(
41+
const TestFunctionCall& _call,
42+
ErrorReporter& _errorReporter,
43+
std::string const& _linePrefix,
44+
bool const _renderResult,
45+
bool const _highlight
46+
) const = 0;
47+
};
48+
49+
using TestHooks = std::vector<std::shared_ptr<TestHook>>;
50+
51+
} // namespace solidity::frontend::test

0 commit comments

Comments
 (0)