Skip to content

Commit 37bf423

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

File tree

14 files changed

+455
-60
lines changed

14 files changed

+455
-60
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: 130 additions & 19 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,40 @@ 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+
// Iterate through the test calls and set the previous call.
186+
TestFunctionCall* previousCall{nullptr};
187+
for (TestFunctionCall& test: m_tests)
153188
{
189+
test.setPreviousCall(previousCall);
190+
test.setTestHooks(&m_testHooks);
191+
previousCall = &test;
192+
}
193+
194+
for (std::unique_ptr<TestHook> const& hook: m_testHooks)
195+
hook->beginTestCase();
196+
197+
for (TestFunctionCall& test: m_tests)
198+
{
199+
for (std::unique_ptr<TestHook> const& hook: m_testHooks)
200+
hook->beforeFunctionCall(test);
201+
154202
if (constructed)
155203
{
156-
soltestAssert(test.call().kind != FunctionCall::Kind::Library, "Libraries have to be deployed before any other call.");
204+
soltestAssert(
205+
test.call().kind != FunctionCall::Kind::Library,
206+
"Libraries have to be deployed before any other call."
207+
);
157208
soltestAssert(
158209
test.call().kind != FunctionCall::Kind::Constructor,
159-
"Constructor has to be the first function call expect for library deployments.");
210+
"Constructor has to be the first function call expect for library deployments."
211+
);
160212
}
161213
else if (test.call().kind == FunctionCall::Kind::Library)
162214
{
@@ -197,6 +249,18 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
197249
bytes output;
198250
if (test.call().kind == FunctionCall::Kind::LowLevel)
199251
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
252+
else if (test.call().kind == FunctionCall::Kind::Builtin)
253+
{
254+
auto builtin = m_builtins[test.call().signature];
255+
std::optional<bytes> builtinOutput{builtin(test.call())};
256+
if (builtinOutput.has_value())
257+
{
258+
test.setFailure(false);
259+
output = builtinOutput.value();
260+
}
261+
else
262+
test.setFailure(true);
263+
}
200264
else
201265
{
202266
soltestAssert(
@@ -212,20 +276,49 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
212276
);
213277
}
214278

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;
279+
bytes expectationOutput;
280+
if (test.call().expectations.builtin)
281+
{
282+
auto builtin = m_builtins[test.call().expectations.builtin->signature];
283+
if (std::optional<bytes> builtinResult = builtin(*test.call().expectations.builtin))
284+
expectationOutput = builtinResult.value();
285+
else
286+
test.setFailure(true);
287+
}
288+
else
289+
expectationOutput = test.call().expectations.rawBytes();
222290

223-
test.setFailure(!m_transactionSuccessful);
291+
bool outputMismatch = (output != expectationOutput);
292+
if (test.call().kind == FunctionCall::Kind::Builtin)
293+
{
294+
if (outputMismatch)
295+
success = false;
296+
}
297+
else
298+
{
299+
// Pre byzantium, it was not possible to return failure data, so we disregard
300+
// output mismatch for those EVM versions.
301+
if (test.call().expectations.failure && !m_transactionSuccessful && !m_evmVersion.supportsReturndata())
302+
outputMismatch = false;
303+
if (m_transactionSuccessful != !test.call().expectations.failure || outputMismatch)
304+
success = false;
305+
test.setFailure(!m_transactionSuccessful);
306+
}
224307
test.setRawBytes(std::move(output));
225308
test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName()));
226309
}
310+
311+
for (std::unique_ptr<TestHook> const& hook: m_testHooks)
312+
hook->afterFunctionCall(test);
227313
}
228314

315+
for (std::unique_ptr<TestHook> const& hook: m_testHooks)
316+
hook->endTestCase();
317+
318+
for (TestFunctionCall& test: m_tests)
319+
for (std::unique_ptr<TestHook> const& hook: m_testHooks)
320+
success &= hook->verifyFunctionCall(test);
321+
229322
if (!m_runWithYul && _compileViaYul)
230323
{
231324
m_compileViaYulCanBeSet = success;
@@ -241,15 +334,15 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
241334
if (!success && (m_runWithYul || !_compileViaYul))
242335
{
243336
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
244-
for (auto const& test: m_tests)
337+
for (TestFunctionCall const& test: m_tests)
245338
{
246339
ErrorReporter errorReporter;
247340
_stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl;
248341
_stream << errorReporter.format(_linePrefix, _formatted);
249342
}
250343
_stream << endl;
251344
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
252-
for (auto const& test: m_tests)
345+
for (TestFunctionCall const& test: m_tests)
253346
{
254347
ErrorReporter errorReporter;
255348
_stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl;
@@ -320,7 +413,7 @@ void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool
320413

321414
void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) const
322415
{
323-
for (auto const& test: m_tests)
416+
for (TestFunctionCall const& test: m_tests)
324417
_stream << test.format("", true, false) << endl;
325418
}
326419

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

341434
void SemanticTest::parseExpectations(istream& _stream)
342435
{
343-
TestFileParser parser{_stream};
436+
TestFileParser parser{_stream, &this->m_builtins};
344437
auto functionCalls = parser.parseFunctionCalls(m_lineOffset);
345-
std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
438+
move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
346439
}
347440

348-
bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments, map<string, solidity::test::Address> const& _libraries)
441+
bool SemanticTest::deploy(
442+
string const& _contractName,
443+
u256 const& _value,
444+
bytes const& _arguments,
445+
map<string, solidity::test::Address> const& _libraries
446+
)
349447
{
350448
auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries);
351449
return !output.empty() && m_transactionSuccessful;
352450
}
451+
452+
std::optional<bytes> SemanticTest::builtinSmokeTest(FunctionCall const& call)
453+
{
454+
// This function is only used in test/libsolidity/semanticTests/builtins/smoke.sol.
455+
std::optional<bytes> result;
456+
if (call.arguments.parameters.size() < 3)
457+
{
458+
result = bytes();
459+
for (const auto& parameter: call.arguments.parameters)
460+
result.value() += util::toBigEndian(u256{util::fromHex(parameter.rawString)});
461+
}
462+
return result;
463+
}

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)