Skip to content

Commit d4dc5c3

Browse files
committed
Initial generic expectation updates through Behaviour class.
1 parent 58a6fe9 commit d4dc5c3

17 files changed

+632
-41
lines changed

test/CMakeLists.txt

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

6060
set(libsolidity_sources
61+
libsolidity/Builtin.h
62+
libsolidity/Behaviour.h
63+
libsolidity/behaviour/SmokeBehaviour.h
64+
libsolidity/behaviour/LogsBehaviour.h
65+
libsolidity/behaviour/LogsBehaviour.cpp
6166
libsolidity/ABIDecoderTests.cpp
6267
libsolidity/ABIEncoderTests.cpp
6368
libsolidity/ABIJsonTest.cpp

test/ExecutionFramework.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
#include <libsolutil/CommonIO.h>
3131
#include <libsolutil/FunctionSelector.h>
32+
#include <libsolidity/util/SoltestTypes.h>
3233

3334
#include <liblangutil/Exceptions.h>
3435

@@ -79,6 +80,23 @@ void ExecutionFramework::selectVM(evmc_capabilities _cap)
7980
reset();
8081
}
8182

83+
std::vector<solidity::frontend::test::LogRecord> ExecutionFramework::recordedLogs() const {
84+
std::vector<solidity::frontend::test::LogRecord> logs{};
85+
for (size_t logIdx = 0; logIdx < numLogs(); ++logIdx)
86+
{
87+
solidity::frontend::test::LogRecord record;
88+
const auto& data = m_evmcHost->recorded_logs.at(logIdx).data;
89+
record.index = logIdx;
90+
record.data = bytes{data.begin(), data.end()};
91+
record.creator = EVMHost::convertFromEVMC(m_evmcHost->recorded_logs.at(logIdx).creator);
92+
for (size_t topicIdx = 0; topicIdx < numLogTopics(logIdx); ++topicIdx) {
93+
record.topics.emplace_back(EVMHost::convertFromEVMC(m_evmcHost->recorded_logs.at(logIdx).topics.at(topicIdx)));
94+
}
95+
logs.emplace_back(record);
96+
}
97+
return logs;
98+
}
99+
82100
void ExecutionFramework::reset()
83101
{
84102
m_evmcHost->reset();

test/ExecutionFramework.h

+22-13
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@
3939

4040
#include <boost/test/unit_test.hpp>
4141

42+
namespace solidity::frontend::test
43+
{
44+
struct LogRecord;
45+
} // namespace solidity::frontend::test
46+
4247
namespace solidity::test
4348
{
4449
using rational = boost::rational<bigint>;
@@ -51,6 +56,8 @@ class ExecutionFramework
5156
{
5257

5358
public:
59+
std::vector<solidity::frontend::test::LogRecord> recordedLogs() const;
60+
5461
ExecutionFramework();
5562
ExecutionFramework(langutil::EVMVersion _evmVersion, std::vector<boost::filesystem::path> const& _vmPaths);
5663
virtual ~ExecutionFramework() = default;
@@ -240,6 +247,21 @@ class ExecutionFramework
240247
return result;
241248
}
242249

250+
/// @returns the (potentially newly created) _ith address.
251+
util::h160 account(size_t _i);
252+
253+
u256 balanceAt(util::h160 const& _addr);
254+
bool storageEmpty(util::h160 const& _addr);
255+
bool addressHasCode(util::h160 const& _addr);
256+
257+
size_t numLogs() const;
258+
size_t numLogTopics(size_t _logIdx) const;
259+
util::h256 logTopic(size_t _logIdx, size_t _topicIdx) const;
260+
util::h160 logAddress(size_t _logIdx) const;
261+
bytes logData(size_t _logIdx) const;
262+
263+
264+
243265
private:
244266
template <class CppFunction, class... Args>
245267
auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments)
@@ -264,19 +286,6 @@ class ExecutionFramework
264286
size_t currentTimestamp();
265287
size_t blockTimestamp(u256 _number);
266288

267-
/// @returns the (potentially newly created) _ith address.
268-
util::h160 account(size_t _i);
269-
270-
u256 balanceAt(util::h160 const& _addr);
271-
bool storageEmpty(util::h160 const& _addr);
272-
bool addressHasCode(util::h160 const& _addr);
273-
274-
size_t numLogs() const;
275-
size_t numLogTopics(size_t _logIdx) const;
276-
util::h256 logTopic(size_t _logIdx, size_t _topicIdx) const;
277-
util::h160 logAddress(size_t _logIdx) const;
278-
bytes logData(size_t _logIdx) const;
279-
280289
langutil::EVMVersion m_evmVersion;
281290
solidity::frontend::RevertStrings m_revertStrings = solidity::frontend::RevertStrings::Default;
282291
solidity::frontend::OptimiserSettings m_optimiserSettings = solidity::frontend::OptimiserSettings::minimal();

test/libsolidity/Behaviour.h

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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 Behaviour
27+
{
28+
public:
29+
Behaviour() = default;
30+
virtual ~Behaviour() = default;
31+
32+
virtual void begin() {}
33+
virtual void before(TestFunctionCall const& _call) { (void) _call; }
34+
virtual void after(TestFunctionCall const& _call) { (void) _call; }
35+
virtual void end() {}
36+
37+
virtual bool isValid(const TestFunctionCall& _call)
38+
{
39+
(void) _call;
40+
return true;
41+
}
42+
43+
virtual void
44+
printExpectedResult(TestFunctionCall const& _call, std::string const& _indentation, std::ostream& _stream)
45+
{
46+
(void) _call;
47+
(void) _indentation;
48+
(void) _stream;
49+
}
50+
51+
virtual void
52+
printObtainedResult(TestFunctionCall const& _call, std::string const& _indentation, std::ostream& _stream)
53+
{
54+
(void) _call;
55+
(void) _indentation;
56+
(void) _stream;
57+
}
58+
59+
virtual void printToFile(TestFunctionCall const& _call, std::ostream& _stream)
60+
{
61+
(void) _call;
62+
(void) _stream;
63+
}
64+
};
65+
66+
using Behaviours = std::vector<std::shared_ptr<Behaviour>>;
67+
68+
} // namespace solidity::frontend::test

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 = nullptr;
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::optional<bytes>();
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

+76-16
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,20 @@
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/Behaviour.h>
26+
#include <test/libsolidity/Builtin.h>
27+
#include <test/libsolidity/behaviour/LogsBehaviour.h>
28+
#include <test/libsolidity/behaviour/SmokeBehaviour.h>
2429

2530
#include <algorithm>
2631
#include <cctype>
@@ -50,13 +55,24 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
5055
m_enforceViaYul(enforceViaYul)
5156
{
5257
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-
}}};
58+
// clang-format off
59+
auto simpleSmokeBuiltin = std::make_shared<Builtin>(
60+
std::bind(&SemanticTest::builtinSmokeTest, this, _1)
61+
);
62+
m_builtins =
63+
{
64+
{"smoke",
65+
{{"test0", simpleSmokeBuiltin},
66+
{"test1", simpleSmokeBuiltin},
67+
{"test2", simpleSmokeBuiltin}}
68+
}
69+
};
70+
m_behaviours =
71+
{
72+
std::make_shared<SmokeBehaviour>(),
73+
std::make_shared<LogsBehaviour>(this)
74+
};
75+
// clang-format on
6076

6177
string choice = m_reader.stringSetting("compileViaYul", "default");
6278
if (choice == "also")
@@ -130,7 +146,13 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
130146
return result;
131147
}
132148

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

@@ -160,11 +182,28 @@ 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.setBehaviours(&m_behaviours);
191+
previousCall = &test;
192+
}
193+
194+
for (auto& behaviour: m_behaviours)
195+
behaviour->begin();
196+
163197
for (auto& test: m_tests)
164198
{
199+
for (auto& behaviour: m_behaviours)
200+
behaviour->before(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.");
168207
soltestAssert(
169208
test.call().kind != FunctionCall::Kind::Constructor,
170209
"Constructor has to be the first function call expect for library deployments.");
@@ -214,7 +253,7 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
214253
boost::split(builtinPath, test.call().signature, boost::is_any_of("."));
215254
soltestAssert(builtinPath.size() == 2, "");
216255
auto builtin = m_builtins[builtinPath.front()][builtinPath.back()];
217-
std::optional<bytes> builtinOutput{builtin.function(test.call())};
256+
std::optional<bytes> builtinOutput{builtin->builtin(test.call())};
218257
if (builtinOutput.has_value())
219258
{
220259
test.setFailure(false);
@@ -245,7 +284,7 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
245284
boost::split(builtinPath, test.call().expectations.builtin->signature, boost::is_any_of("."));
246285
assert(builtinPath.size() == 2);
247286
auto builtin = m_builtins[builtinPath.front()][builtinPath.back()];
248-
std::optional<bytes> builtinResult = builtin.function(*test.call().expectations.builtin);
287+
std::optional<bytes> builtinResult = builtin->builtin(*test.call().expectations.builtin);
249288
if (builtinResult.has_value())
250289
expectationOutput = builtinResult.value();
251290
else
@@ -273,8 +312,29 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
273312
test.setRawBytes(std::move(output));
274313
test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName()));
275314
}
315+
316+
for (auto& behaviour: m_behaviours)
317+
behaviour->after(test);
318+
}
319+
320+
// The artificialFunctionCall is an artificially created function call,
321+
// where it's previous call is pointing to the last call of the test.
322+
TestFunctionCall artificialFunctionCall(FunctionCall{});
323+
artificialFunctionCall.setBehaviours(&m_behaviours);
324+
artificialFunctionCall.setPreviousCall(previousCall);
325+
for (auto& behaviour: m_behaviours)
326+
{
327+
behaviour->before(artificialFunctionCall);
328+
behaviour->after(artificialFunctionCall);
276329
}
277330

331+
for (auto& behaviour: m_behaviours)
332+
behaviour->end();
333+
334+
for (auto& test: m_tests)
335+
for (auto& behaviour: m_behaviours)
336+
success &= behaviour->isValid(test);
337+
278338
if (!m_runWithYul && _compileViaYul)
279339
{
280340
m_compileViaYulCanBeSet = success;

0 commit comments

Comments
 (0)