Skip to content

Commit 2c3218b

Browse files
committed
Basic expectation generation, if events where not consumed.
1 parent 3847073 commit 2c3218b

File tree

4 files changed

+75
-8
lines changed

4 files changed

+75
-8
lines changed

test/ExecutionFramework.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ class ExecutionFramework
5151
{
5252

5353
public:
54-
/// LOG record.
5554
struct log_record
5655
{
56+
size_t index;
5757
/// The address of the account which created the log.
5858
util::h160 creator;
5959

@@ -302,6 +302,7 @@ class ExecutionFramework
302302
{
303303
log_record record;
304304
const auto& data = m_evmcHost->recorded_logs.at(logIdx).data;
305+
record.index = logIdx;
305306
record.data = bytes{data.begin(), data.end()};
306307
record.creator = EVMHost::convertFromEVMC(m_evmcHost->recorded_logs.at(logIdx).creator);
307308
for (size_t topicIdx = 0; topicIdx < numLogTopics(logIdx); ++topicIdx) {

test/libsolidity/SemanticTest.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
282282

283283
TestFunctionCall fakeLastTestFunctionCall(FunctionCall{});
284284
fakeLastTestFunctionCall.setPreviousCall(previousCall);
285-
success = checkLogs(fakeLastTestFunctionCall);
285+
success &= checkLogs(fakeLastTestFunctionCall);
286286

287287
if (success && !m_runWithYul && _compileViaYul)
288288
{
@@ -428,7 +428,9 @@ bool SemanticTest::checkLogs(TestFunctionCall& _call)
428428
// Only non-builtins are able to produce logs.
429429
// So lets search from the current call up to the first non-builtin.
430430
while (producer != nullptr && producer->call().kind == FunctionCall::Kind::Builtin)
431-
if (producer->previousCall() != nullptr)
431+
if (producer->previousCall() == nullptr)
432+
break;
433+
else
432434
{
433435
// On the way up to the producer we track all builtins that where on the way.
434436
// Only builtins can consume logs, we store them in the consumers vector.
@@ -450,8 +452,8 @@ bool SemanticTest::checkLogs(TestFunctionCall& _call)
450452
for (auto& logIdx: m_touchedLogs[&producer->call()])
451453
producer->consumedLogs().insert(logIdx);
452454

453-
std::cout << producer->consumedLogs().size() << " / " << producer->logs().size() << std::endl;
454-
return producer->consumedLogs().size() == producer->logs().size();
455+
// It is ok if some builtins consumed log events.
456+
return producer->consumedLogs().size() >= producer->logs().size();
455457
}
456458
return true;
457459
}

test/libsolidity/util/TestFunctionCall.cpp

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <test/libsolidity/util/ContractABIUtils.h>
1919

2020
#include <libsolutil/AnsiColorized.h>
21+
#include <libsolutil/JSON.h>
2122

2223
#include <boost/algorithm/string/replace.hpp>
2324

@@ -32,6 +33,32 @@ using namespace std;
3233

3334
using Token = soltest::Token;
3435

36+
string TestFunctionCall::findEventSignature(solidity::test::ExecutionFramework::log_record const& log) const
37+
{
38+
for (auto& entity: m_contractABI)
39+
if (entity["type"].isString() && entity["type"].asString() == "event")
40+
{
41+
string name{entity["name"].asString()};
42+
bool anonymous{entity["anonymous"].asBool()};
43+
vector<string> argumentTypes;
44+
for (auto& input: entity["inputs"])
45+
argumentTypes.emplace_back(input["internalType"].asString());
46+
if (!name.empty() && !anonymous)
47+
{
48+
std::string signature{name};
49+
signature += "(";
50+
for (auto& arg: argumentTypes)
51+
signature += arg + ",";
52+
if (!argumentTypes.empty())
53+
signature = signature.substr(0, signature.length() - 1);
54+
signature += ")";
55+
if (!log.topics.empty() && log.topics[0] == util::keccak256(signature))
56+
return signature;
57+
}
58+
}
59+
return "";
60+
}
61+
3562
string TestFunctionCall::format(
3663
ErrorReporter& _errorReporter,
3764
string const& _linePrefix,
@@ -41,7 +68,7 @@ string TestFunctionCall::format(
4168
{
4269
stringstream stream;
4370

44-
bool highlight = !matchesExpectation() && _highlight;
71+
bool highlight = (!matchesExpectation() || hasUnconsumedLogs()) && _highlight;
4572

4673
auto formatOutput = [&](bool const _singleLine)
4774
{
@@ -93,7 +120,6 @@ string TestFunctionCall::format(
93120
if (!m_call.arguments.parameters.at(0).format.newline)
94121
stream << ws;
95122
stream << output;
96-
97123
}
98124

99125
/// Formats comments on the function parameters and the arrow taking
@@ -134,6 +160,23 @@ string TestFunctionCall::format(
134160
result += ": ";
135161
result += formatRawParameters(builtin->arguments.parameters);
136162
}
163+
else if (hasUnconsumedLogs())
164+
{
165+
stringstream str;
166+
for (auto& log: unconsumedLogs())
167+
{
168+
string signature{findEventSignature(log)};
169+
str << std::endl
170+
<< " // logs.expectEvent(uint256,string): " << log.index << ", "
171+
<< "\"" + signature + "\" -> ";
172+
for (auto& topic : log.topics)
173+
if ((topic != *log.topics.begin() && !signature.empty()) || signature.empty())
174+
str << util::toCompactHexWithPrefix(u256{topic}) << ", ";
175+
if (!log.data.empty())
176+
str << util::toHex(log.data, HexPrefix::Add);
177+
}
178+
result += str.str();
179+
}
137180
else
138181
{
139182
bool const isFailure = m_call.expectations.failure;
@@ -152,6 +195,13 @@ string TestFunctionCall::format(
152195
if (m_calledNonExistingFunction)
153196
_errorReporter.warning("The function \"" + m_call.signature + "\" is not known to the compiler.");
154197

198+
if (m_consumedLogs.size() != m_rawLogs.size())
199+
{
200+
stringstream str;
201+
str << "The function \"" << m_call.signature << "\" produced " << m_rawLogs.size() <<" log(s), but only " << m_consumedLogs.size() << " log(s) where consumed.";
202+
_errorReporter.error(str.str());
203+
}
204+
155205
if (builtin)
156206
_errorReporter.warning("The expectation \"" + builtin->signature + ": " + formatRawParameters(builtin->arguments.parameters) + "\" will be replaced with the actual value returned by the test.");
157207

test/libsolidity/util/TestFunctionCall.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class TestFunctionCall
8282
void setFailure(const bool _failure) { m_failure = _failure; }
8383
void setRawBytes(const bytes _rawBytes) { m_rawBytes = _rawBytes; }
8484
void setContractABI(Json::Value _contractABI) { m_contractABI = std::move(_contractABI); }
85-
std::vector<solidity::test::ExecutionFramework::log_record> logs()
85+
std::vector<solidity::test::ExecutionFramework::log_record> logs() const
8686
{
8787
return m_rawLogs;
8888
}
@@ -94,6 +94,18 @@ class TestFunctionCall
9494
{
9595
return m_consumedLogs;
9696
}
97+
bool hasUnconsumedLogs() const
98+
{
99+
return m_consumedLogs.size() < m_rawLogs.size();
100+
}
101+
std::vector<solidity::test::ExecutionFramework::log_record> unconsumedLogs() const
102+
{
103+
std::vector<solidity::test::ExecutionFramework::log_record> result;
104+
for (auto& log: m_rawLogs)
105+
if (m_consumedLogs.find(log.index) == m_consumedLogs.end())
106+
result.emplace_back(m_rawLogs[log.index]);
107+
return result;
108+
}
97109
void setPreviousCall(TestFunctionCall* _call)
98110
{
99111
m_previousCall = _call;
@@ -104,6 +116,8 @@ class TestFunctionCall
104116
}
105117

106118
private:
119+
std::string findEventSignature(solidity::test::ExecutionFramework::log_record const& log) const;
120+
107121
/// Tries to format the given `bytes`, applying the detected ABI types that have be set for each parameter.
108122
/// Throws if there's a mismatch in the size of `bytes` and the desired formats that are specified
109123
/// in the ABI type.

0 commit comments

Comments
 (0)