Skip to content

Commit a9a6de2

Browse files
committed
[isoltest] Add support for builtin functions and test hooks.
1 parent e75e3fc commit a9a6de2

File tree

14 files changed

+449
-68
lines changed

14 files changed

+449
-68
lines changed

liblangutil/Common.h

Lines changed: 5 additions & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 124 additions & 22 deletions
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,7 +30,9 @@
2530
#include <cctype>
2631
#include <fstream>
2732
#include <memory>
33+
#include <optional>
2834
#include <stdexcept>
35+
#include <utility>
2936

3037
using namespace std;
3138
using namespace solidity;
@@ -47,6 +54,16 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
4754
m_lineOffset(m_reader.lineNumber()),
4855
m_enforceViaYul(enforceViaYul)
4956
{
57+
using namespace placeholders;
58+
59+
auto simpleSmokeBuiltin = bind(&SemanticTest::builtinSmokeTest, this, _1);
60+
addBuiltin("smoke.test0", simpleSmokeBuiltin);
61+
addBuiltin("smoke.test1", simpleSmokeBuiltin);
62+
addBuiltin("smoke.test2", simpleSmokeBuiltin);
63+
addBuiltin("smoke_test3", simpleSmokeBuiltin);
64+
65+
addTestHook(make_unique<SmokeHook>());
66+
5067
string choice = m_reader.stringSetting("compileViaYul", "default");
5168
if (choice == "also")
5269
{
@@ -119,7 +136,23 @@ 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[std::move(_name)] = std::move(_builtin);
142+
}
143+
144+
void SemanticTest::addTestHook(std::unique_ptr<TestHook> _testHook)
145+
{
146+
m_testHooks.emplace_back(std::move(_testHook));
147+
}
148+
149+
TestCase::TestResult SemanticTest::runTest(
150+
ostream& _stream,
151+
string const& _linePrefix,
152+
bool _formatted,
153+
bool _compileViaYul,
154+
bool _compileToEwasm
155+
)
123156
{
124157
bool success = true;
125158

@@ -142,21 +175,31 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
142175
if (_compileViaYul)
143176
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl;
144177

145-
for (auto& test: m_tests)
178+
for (TestFunctionCall& test: m_tests)
146179
test.reset();
147180

148181
map<string, solidity::test::Address> libraries;
149182

150183
bool constructed = false;
151184

152-
for (auto& test: m_tests)
185+
for (std::unique_ptr<TestHook> const& hook: m_testHooks)
186+
hook->beginTestCase();
187+
188+
for (TestFunctionCall& test: m_tests)
153189
{
190+
for (std::unique_ptr<TestHook> const& hook: m_testHooks)
191+
hook->beforeFunctionCall(test);
192+
154193
if (constructed)
155194
{
156-
soltestAssert(test.call().kind != FunctionCall::Kind::Library, "Libraries have to be deployed before any other call.");
195+
soltestAssert(
196+
test.call().kind != FunctionCall::Kind::Library,
197+
"Libraries have to be deployed before any other call."
198+
);
157199
soltestAssert(
158200
test.call().kind != FunctionCall::Kind::Constructor,
159-
"Constructor has to be the first function call expect for library deployments.");
201+
"Constructor has to be the first function call expect for library deployments."
202+
);
160203
}
161204
else if (test.call().kind == FunctionCall::Kind::Library)
162205
{
@@ -197,6 +240,18 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
197240
bytes output;
198241
if (test.call().kind == FunctionCall::Kind::LowLevel)
199242
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
243+
else if (test.call().kind == FunctionCall::Kind::Builtin)
244+
{
245+
auto builtin = m_builtins[test.call().signature];
246+
std::optional<bytes> builtinOutput{builtin(test.call())};
247+
if (builtinOutput.has_value())
248+
{
249+
test.setFailure(false);
250+
output = builtinOutput.value();
251+
}
252+
else
253+
test.setFailure(true);
254+
}
200255
else
201256
{
202257
soltestAssert(
@@ -212,20 +267,49 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
212267
);
213268
}
214269

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;
270+
bytes expectationOutput;
271+
if (test.call().expectations.builtin)
272+
{
273+
auto builtin = m_builtins[test.call().expectations.builtin->signature];
274+
if (std::optional<bytes> builtinResult = builtin(*test.call().expectations.builtin))
275+
expectationOutput = builtinResult.value();
276+
else
277+
test.setFailure(true);
278+
}
279+
else
280+
expectationOutput = test.call().expectations.rawBytes();
222281

223-
test.setFailure(!m_transactionSuccessful);
282+
bool outputMismatch = (output != expectationOutput);
283+
if (test.call().kind == FunctionCall::Kind::Builtin)
284+
{
285+
if (outputMismatch)
286+
success = false;
287+
}
288+
else
289+
{
290+
// Pre byzantium, it was not possible to return failure data, so we disregard
291+
// output mismatch for those EVM versions.
292+
if (test.call().expectations.failure && !m_transactionSuccessful && !m_evmVersion.supportsReturndata())
293+
outputMismatch = false;
294+
if (m_transactionSuccessful != !test.call().expectations.failure || outputMismatch)
295+
success = false;
296+
test.setFailure(!m_transactionSuccessful);
297+
}
224298
test.setRawBytes(std::move(output));
225299
test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName()));
226300
}
301+
302+
for (std::unique_ptr<TestHook> const& hook: m_testHooks)
303+
hook->afterFunctionCall(test);
227304
}
228305

306+
for (std::unique_ptr<TestHook> const& hook: m_testHooks)
307+
hook->endTestCase();
308+
309+
for (TestFunctionCall& test: m_tests)
310+
for (std::unique_ptr<TestHook> const& hook: m_testHooks)
311+
success &= hook->verifyFunctionCall(test);
312+
229313
if (!m_runWithYul && _compileViaYul)
230314
{
231315
m_compileViaYulCanBeSet = success;
@@ -241,18 +325,18 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
241325
if (!success && (m_runWithYul || !_compileViaYul))
242326
{
243327
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
244-
for (auto const& test: m_tests)
328+
for (TestFunctionCall const& test: m_tests)
245329
{
246330
ErrorReporter errorReporter;
247-
_stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl;
331+
_stream << test.format(errorReporter, _linePrefix, false, _formatted, &m_testHooks) << endl;
248332
_stream << errorReporter.format(_linePrefix, _formatted);
249333
}
250334
_stream << endl;
251335
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
252-
for (auto const& test: m_tests)
336+
for (TestFunctionCall const& test: m_tests)
253337
{
254338
ErrorReporter errorReporter;
255-
_stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl;
339+
_stream << test.format(errorReporter, _linePrefix, true, _formatted, &m_testHooks) << endl;
256340
_stream << errorReporter.format(_linePrefix, _formatted);
257341
}
258342
AnsiColorized(_stream, _formatted, {BOLD, RED})
@@ -320,8 +404,8 @@ void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool
320404

321405
void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) const
322406
{
323-
for (auto const& test: m_tests)
324-
_stream << test.format("", true, false) << endl;
407+
for (TestFunctionCall const& test: m_tests)
408+
_stream << test.format("", true, false, &m_testHooks) << endl;
325409
}
326410

327411
void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePrefix)
@@ -340,13 +424,31 @@ void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePre
340424

341425
void SemanticTest::parseExpectations(istream& _stream)
342426
{
343-
TestFileParser parser{_stream};
427+
TestFileParser parser{_stream, &this->m_builtins};
344428
auto functionCalls = parser.parseFunctionCalls(m_lineOffset);
345-
std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
429+
move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
346430
}
347431

348-
bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments, map<string, solidity::test::Address> const& _libraries)
432+
bool SemanticTest::deploy(
433+
string const& _contractName,
434+
u256 const& _value,
435+
bytes const& _arguments,
436+
map<string, solidity::test::Address> const& _libraries
437+
)
349438
{
350439
auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries);
351440
return !output.empty() && m_transactionSuccessful;
352441
}
442+
443+
std::optional<bytes> SemanticTest::builtinSmokeTest(FunctionCall const& call)
444+
{
445+
// This function is only used in test/libsolidity/semanticTests/builtins/smoke.sol.
446+
std::optional<bytes> result;
447+
if (call.arguments.parameters.size() < 3)
448+
{
449+
result = bytes();
450+
for (const auto& parameter: call.arguments.parameters)
451+
result.value() += util::toBigEndian(u256{util::fromHex(parameter.rawString)});
452+
}
453+
return result;
454+
}

test/libsolidity/SemanticTest.h

Lines changed: 15 additions & 5 deletions
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,14 @@ 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+
void addTestHook(std::unique_ptr<TestHook> _testHook);
65+
6166
private:
67+
// builtin functions
68+
std::optional<bytes> builtinSmokeTest(FunctionCall const& call);
69+
6270
TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm);
6371
SourceMap m_sources;
6472
std::size_t m_lineOffset;
@@ -70,6 +78,8 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict
7078
bool m_runWithABIEncoderV1Only = false;
7179
bool m_allowNonExistingFunctions = false;
7280
bool m_compileViaYulCanBeSet = false;
81+
Builtins m_builtins{};
82+
std::vector<std::unique_ptr<TestHook>> m_testHooks{};
7383
};
7484

7585
}

0 commit comments

Comments
 (0)