Skip to content

Commit 71d1a9e

Browse files
committed
[isoltest] Add support for generic expectation updates.
1 parent 82ba98a commit 71d1a9e

13 files changed

+274
-40
lines changed

test/CMakeLists.txt

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

6060
set(libsolidity_sources
61+
libsolidity/Builtin.h
62+
libsolidity/TestHook.h
63+
libsolidity/hooks/SmokeHook.h
6164
libsolidity/ABIDecoderTests.cpp
6265
libsolidity/ABIEncoderTests.cpp
6366
libsolidity/ABIJsonTest.cpp

test/libsolidity/Builtin.h

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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 <functional>
18+
#include <map>
19+
#include <optional>
20+
#include <test/libsolidity/util/SoltestErrors.h>
21+
#include <test/libsolidity/util/SoltestTypes.h>
22+
#include <utility>
23+
#include <vector>
24+
#include <memory>
25+
26+
namespace solidity::frontend::test
27+
{
28+
29+
class TestFunctionCall;
30+
31+
class Builtin
32+
{
33+
/// This is the actual implementation of the builtin function.
34+
std::function<std::optional<bytes>(FunctionCall const&)> m_builtin;
35+
36+
public:
37+
Builtin() = default;
38+
virtual ~Builtin() = default;
39+
40+
explicit Builtin(std::function<std::optional<bytes>(FunctionCall const&)> _builtin)
41+
{
42+
m_builtin = std::move(_builtin);
43+
}
44+
45+
virtual std::optional<bytes> builtin(FunctionCall const& _call)
46+
{
47+
if (m_builtin != nullptr)
48+
return m_builtin(_call);
49+
return std::nullopt;
50+
}
51+
};
52+
53+
using Builtins = std::map<std::string, std::map<std::string, std::shared_ptr<Builtin>>>;
54+
55+
} // namespace solidity::frontend::test

test/libsolidity/SemanticTest.cpp

+86-23
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,19 @@
1212
along with solidity. If not, see <http://www.gnu.org/licenses/>.
1313
*/
1414

15-
#include <test/libsolidity/SemanticTest.h>
16-
#include <test/libsolidity/util/BytesUtils.h>
17-
#include <libsolutil/Whiskers.h>
18-
#include <libyul/Exceptions.h>
19-
#include <test/Common.h>
2015
#include <boost/algorithm/string.hpp>
2116
#include <boost/algorithm/string/predicate.hpp>
2217
#include <boost/algorithm/string/trim.hpp>
2318
#include <boost/throw_exception.hpp>
19+
#include <libsolutil/Whiskers.h>
20+
#include <libyul/Exceptions.h>
21+
#include <test/Common.h>
22+
#include <test/libsolidity/SemanticTest.h>
23+
#include <test/libsolidity/util/BytesUtils.h>
24+
25+
#include <test/libsolidity/Builtin.h>
26+
#include <test/libsolidity/TestHook.h>
27+
#include <test/libsolidity/hooks/SmokeHook.h>
2428

2529
#include <algorithm>
2630
#include <cctype>
@@ -49,14 +53,21 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
4953
m_lineOffset(m_reader.lineNumber()),
5054
m_enforceViaYul(enforceViaYul)
5155
{
52-
using namespace std::placeholders;
53-
m_builtins
54-
= {{"smoke",
55-
{
56-
{"test0", {std::bind(&SemanticTest::builtinSmokeTest, this, _1)}},
57-
{"test1", {std::bind(&SemanticTest::builtinSmokeTest, this, _1)}},
58-
{"test2", {std::bind(&SemanticTest::builtinSmokeTest, this, _1)}},
59-
}}};
56+
using namespace placeholders;
57+
auto simpleSmokeBuiltin = make_shared<Builtin>(
58+
bind(&SemanticTest::builtinSmokeTest, this, _1)
59+
);
60+
m_builtins = {
61+
{"smoke", {
62+
{"test0", simpleSmokeBuiltin},
63+
{"test1", simpleSmokeBuiltin},
64+
{"test2", simpleSmokeBuiltin}
65+
}
66+
}
67+
};
68+
m_testHooks = {
69+
make_shared<SmokeHook>()
70+
};
6071

6172
string choice = m_reader.stringSetting("compileViaYul", "default");
6273
if (choice == "also")
@@ -130,7 +141,18 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
130141
return result;
131142
}
132143

133-
TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm)
144+
void SemanticTest::addBuiltin(string _module, string _function, std::shared_ptr<Builtin> _builtin)
145+
{
146+
m_builtins[_module][_function] = _builtin;
147+
}
148+
149+
TestCase::TestResult SemanticTest::runTest(
150+
ostream& _stream,
151+
string const& _linePrefix,
152+
bool _formatted,
153+
bool _compileViaYul,
154+
bool _compileToEwasm
155+
)
134156
{
135157
bool success = true;
136158

@@ -160,14 +182,33 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
160182

161183
bool constructed = false;
162184

185+
// Iterate through the test calls and set the previous call.
186+
TestFunctionCall* previousCall{nullptr};
187+
for (auto& test: m_tests)
188+
{
189+
test.setPreviousCall(previousCall);
190+
test.setTestHooks(&m_testHooks);
191+
previousCall = &test;
192+
}
193+
194+
for (auto& hook: m_testHooks)
195+
hook->beginTestCase();
196+
163197
for (auto& test: m_tests)
164198
{
199+
for (auto& hook: m_testHooks)
200+
hook->beforeFunctionCall(test);
201+
165202
if (constructed)
166203
{
167-
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+
);
168208
soltestAssert(
169209
test.call().kind != FunctionCall::Kind::Constructor,
170-
"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+
);
171212
}
172213
else if (test.call().kind == FunctionCall::Kind::Library)
173214
{
@@ -210,11 +251,11 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
210251
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
211252
else if (test.call().kind == FunctionCall::Kind::Builtin)
212253
{
213-
std::vector<string> builtinPath;
254+
vector<string> builtinPath;
214255
boost::split(builtinPath, test.call().signature, boost::is_any_of("."));
215256
soltestAssert(builtinPath.size() == 2, "");
216257
auto builtin = m_builtins[builtinPath.front()][builtinPath.back()];
217-
std::optional<bytes> builtinOutput{builtin.function(test.call())};
258+
std::optional<bytes> builtinOutput{builtin->builtin(test.call())};
218259
if (builtinOutput.has_value())
219260
{
220261
test.setFailure(false);
@@ -241,11 +282,11 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
241282
bytes expectationOutput;
242283
if (test.call().expectations.builtin)
243284
{
244-
std::vector<string> builtinPath;
285+
vector<string> builtinPath;
245286
boost::split(builtinPath, test.call().expectations.builtin->signature, boost::is_any_of("."));
246-
assert(builtinPath.size() == 2);
287+
soltestAssert(builtinPath.size() == 2, "");
247288
auto builtin = m_builtins[builtinPath.front()][builtinPath.back()];
248-
std::optional<bytes> builtinResult = builtin.function(*test.call().expectations.builtin);
289+
std::optional<bytes> builtinResult = builtin->builtin(*test.call().expectations.builtin);
249290
if (builtinResult.has_value())
250291
expectationOutput = builtinResult.value();
251292
else
@@ -273,8 +314,29 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
273314
test.setRawBytes(std::move(output));
274315
test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName()));
275316
}
317+
318+
for (auto& hook: m_testHooks)
319+
hook->afterFunctionCall(test);
320+
}
321+
322+
// The artificialFunctionCall is an artificially created function call,
323+
// where it's previous call is pointing to the last call of the test.
324+
TestFunctionCall artificialFunctionCall(FunctionCall{});
325+
artificialFunctionCall.setTestHooks(&m_testHooks);
326+
artificialFunctionCall.setPreviousCall(previousCall);
327+
for (auto& hook: m_testHooks)
328+
{
329+
hook->beforeFunctionCall(artificialFunctionCall);
330+
hook->afterFunctionCall(artificialFunctionCall);
276331
}
277332

333+
for (auto& hook: m_testHooks)
334+
hook->endTestCase();
335+
336+
for (auto& test: m_tests)
337+
for (auto& hook: m_testHooks)
338+
success &= hook->verifyFunctionCall(test);
339+
278340
if (!m_runWithYul && _compileViaYul)
279341
{
280342
m_compileViaYulCanBeSet = success;
@@ -391,14 +453,15 @@ void SemanticTest::parseExpectations(istream& _stream)
391453
{
392454
TestFileParser parser{_stream, &this->m_builtins};
393455
auto functionCalls = parser.parseFunctionCalls(m_lineOffset);
394-
std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
456+
move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
395457
}
396458

397459
bool SemanticTest::deploy(
398460
string const& _contractName,
399461
u256 const& _value,
400462
bytes const& _arguments,
401-
map<string, solidity::test::Address> const& _libraries)
463+
map<string, solidity::test::Address> const& _libraries
464+
)
402465
{
403466
auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries);
404467
return !output.empty() && m_transactionSuccessful;

test/libsolidity/SemanticTest.h

+11-6
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@
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/Builtin.h>
22+
#include <test/libsolidity/SolidityExecutionFramework.h>
23+
#include <test/libsolidity/TestHook.h>
24+
#include <test/libsolidity/util/TestFileParser.h>
25+
#include <test/libsolidity/util/TestFunctionCall.h>
2426

2527
#include <iosfwd>
2628
#include <string>
@@ -59,6 +61,8 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict
5961
/// Returns true if deployment was successful, false otherwise.
6062
bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map<std::string, solidity::test::Address> const& _libraries = {});
6163

64+
void addBuiltin(std::string _module, std::string _function, std::shared_ptr<Builtin> _builtin);
65+
6266
private:
6367
// builtin functions
6468
std::optional<bytes> builtinSmokeTest(FunctionCall const& call);
@@ -74,7 +78,8 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict
7478
bool m_runWithABIEncoderV1Only = false;
7579
bool m_allowNonExistingFunctions = false;
7680
bool m_compileViaYulCanBeSet = false;
77-
BuiltinFunctions m_builtins;
81+
Builtins m_builtins;
82+
TestHooks m_testHooks;
7883
};
7984

8085
}

test/libsolidity/TestHook.h

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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 "Builtin.h"
18+
#include <memory>
19+
#include <vector>
20+
21+
namespace solidity::frontend::test
22+
{
23+
24+
class TestFunctionCall;
25+
26+
class TestHook
27+
{
28+
public:
29+
TestHook() = default;
30+
virtual ~TestHook() = default;
31+
32+
virtual void beginTestCase() = 0;
33+
virtual void beforeFunctionCall(TestFunctionCall const& _call) = 0;
34+
virtual void afterFunctionCall(TestFunctionCall const& _call) = 0;
35+
virtual void endTestCase() = 0;
36+
37+
virtual bool verifyFunctionCall(TestFunctionCall const& _call) = 0;
38+
39+
virtual std::string formatFunctionCall(
40+
const TestFunctionCall& _call,
41+
ErrorReporter& _errorReporter,
42+
std::string const& _linePrefix,
43+
bool const _renderResult,
44+
bool const _highlight
45+
) const = 0;
46+
};
47+
48+
using TestHooks = std::vector<std::shared_ptr<TestHook>>;
49+
50+
} // namespace solidity::frontend::test

test/libsolidity/hooks/SmokeHook.h

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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 <iostream>
18+
#include <test/libsolidity/TestHook.h>
19+
20+
namespace solidity::frontend::test
21+
{
22+
class SmokeHook: public TestHook
23+
{
24+
public:
25+
SmokeHook() = default;
26+
~SmokeHook() override = default;
27+
28+
void beginTestCase() override {}
29+
void beforeFunctionCall(TestFunctionCall const&) override {}
30+
void afterFunctionCall(TestFunctionCall const&) override {}
31+
void endTestCase() override {}
32+
33+
bool verifyFunctionCall(TestFunctionCall const&) override { return true; }
34+
35+
std::string formatFunctionCall(
36+
TestFunctionCall const&,
37+
ErrorReporter&,
38+
std::string const&,
39+
const bool,
40+
const bool
41+
) const override
42+
{
43+
return "";
44+
}
45+
};
46+
47+
} // namespace solidity::frontend::test

test/libsolidity/semanticTests/builtins/smoke.sol

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ contract ClientReceipt {
1111
// smoke.test2: 2, 3 -> smoke.test2: 2, 3
1212
// smoke.test2: 2, 3, 4 -> FAILURE
1313
// smoke.test2: 2, 3, 4 -> smoke.test2: 2, 3, 4
14+
// smoke.test0 ->

0 commit comments

Comments
 (0)