1818#include < test/libsolidity/util/ContractABIUtils.h>
1919
2020#include < libsolutil/AnsiColorized.h>
21+ #include < libsolutil/JSON.h>
2122
23+ #include < boost/algorithm/string/predicate.hpp>
2224#include < boost/algorithm/string/replace.hpp>
2325
2426#include < optional>
@@ -32,6 +34,46 @@ using namespace std;
3234
3335using Token = soltest::Token;
3436
37+ TestFunctionCall::EventInformation
38+ TestFunctionCall::findEventSignature (solidity::test::ExecutionFramework::log_record const & log) const
39+ {
40+ for (const auto & entity: m_contractABI)
41+ {
42+ EventInformation eventInfo;
43+ if (entity[" type" ].isString () && entity[" type" ].asString () == " event" )
44+ {
45+ string name{entity[" name" ].asString ()};
46+ bool anonymous{entity[" anonymous" ].asBool ()};
47+ vector<string> argumentTypes;
48+ for (const auto & input: entity[" inputs" ])
49+ {
50+ string type{input[" internalType" ].asString ()};
51+ argumentTypes.emplace_back (type);
52+ if (input[" indexed" ].asBool ())
53+ eventInfo.indexedTypes .push_back (type);
54+ else
55+ eventInfo.nonIndexedTypes .push_back (type);
56+ }
57+ if (!name.empty () && !anonymous)
58+ {
59+ std::string signature{name};
60+ signature += " (" ;
61+ for (auto & arg: argumentTypes)
62+ signature += arg + " ," ;
63+ if (!argumentTypes.empty ())
64+ signature = signature.substr (0 , signature.length () - 1 );
65+ signature += " )" ;
66+ if (!log.topics .empty () && log.topics [0 ] == util::keccak256 (signature))
67+ {
68+ eventInfo.signature = signature;
69+ return eventInfo;
70+ }
71+ }
72+ }
73+ }
74+ return {};
75+ }
76+
3577string TestFunctionCall::format (
3678 ErrorReporter& _errorReporter,
3779 string const & _linePrefix,
@@ -41,7 +83,7 @@ string TestFunctionCall::format(
4183{
4284 stringstream stream;
4385
44- bool highlight = !matchesExpectation () && _highlight;
86+ bool highlight = ( !matchesExpectation () || hasUnconsumedLogs () ) && _highlight;
4587
4688 auto formatOutput = [&](bool const _singleLine)
4789 {
@@ -93,7 +135,6 @@ string TestFunctionCall::format(
93135 if (!m_call.arguments .parameters .at (0 ).format .newline )
94136 stream << ws;
95137 stream << output;
96-
97138 }
98139
99140 // / Formats comments on the function parameters and the arrow taking
@@ -116,15 +157,60 @@ string TestFunctionCall::format(
116157 stream << endl << _linePrefix << newline << ws;
117158 if (!m_call.arguments .comment .empty ())
118159 {
119- stream << comment << m_call.arguments .comment << comment;
120- stream << endl << _linePrefix << newline << ws;
160+ stream << comment << m_call.arguments .comment << comment;
161+ stream << endl << _linePrefix << newline << ws;
121162 }
122163 stream << arrow;
123164 }
124165
125166 // / Format either the expected output or the actual result output
126167 string result;
127168 auto & builtin = m_call.expectations .builtin ;
169+ if (hasUnconsumedLogs ())
170+ {
171+ for (auto & log: unconsumedLogs ())
172+ {
173+ EventInformation eventInfo = findEventSignature (log);
174+ string signature{eventInfo.signature };
175+ stream << std::endl
176+ << " // logs.expectEvent(uint256,string): " << log.index << " , "
177+ << " \" " + signature + " \" -> " ;
178+
179+ vector<u256> topicsAndData;
180+ for (auto & topic: log.topics )
181+ if ((topic != *log.topics .begin () && !signature.empty ()) || signature.empty ())
182+ topicsAndData.emplace_back (u256{topic});
183+
184+ assert (log.data .size () % 32 == 0 );
185+
186+ if (eventInfo.signature .empty ())
187+ {
188+ for (long current = 0 ; current < static_cast <long >(log.data .size () / 32 ); ++current)
189+ {
190+ bytes parameter{log.data .begin () + current * 32 , log.data .begin () + current * 32 + 32 };
191+ topicsAndData.emplace_back (util::fromBigEndian<u256>(parameter));
192+ }
193+ }
194+ else
195+ {
196+ long current = 0 ;
197+ for (auto & type: eventInfo.nonIndexedTypes )
198+ {
199+ // todo: use type information to improve decoding.
200+ (void ) type;
201+ bytes parameter{log.data .begin () + current, log.data .begin () + current + 32 };
202+ current += 32 ;
203+ topicsAndData.emplace_back (util::fromBigEndian<u256>(parameter));
204+ }
205+ }
206+ for (auto & element: topicsAndData)
207+ {
208+ stream << util::toCompactHexWithPrefix (element);
209+ if (&element != &*topicsAndData.rbegin ())
210+ stream << " , " ;
211+ }
212+ }
213+ }
128214 if (!_renderResult)
129215 {
130216 if (builtin)
@@ -152,6 +238,13 @@ string TestFunctionCall::format(
152238 if (m_calledNonExistingFunction)
153239 _errorReporter.warning (" The function \" " + m_call.signature + " \" is not known to the compiler." );
154240
241+ if (m_consumedLogs.size () != m_rawLogs.size ())
242+ {
243+ stringstream str;
244+ str << " The function \" " << m_call.signature << " \" produced " << m_rawLogs.size () <<" log(s), but only " << m_consumedLogs.size () << " log(s) where consumed." ;
245+ _errorReporter.error (str.str ());
246+ }
247+
155248 if (builtin)
156249 _errorReporter.warning (" The expectation \" " + builtin->signature + " : " + formatRawParameters (builtin->arguments .parameters ) + " \" will be replaced with the actual value returned by the test." );
157250
0 commit comments