13
13
*/
14
14
15
15
#include < test/libsolidity/SemanticTest.h>
16
+
16
17
#include < libsolutil/Whiskers.h>
17
18
#include < libyul/Exceptions.h>
18
19
#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
+
19
24
#include < boost/algorithm/string.hpp>
20
25
#include < boost/algorithm/string/predicate.hpp>
21
26
#include < boost/algorithm/string/trim.hpp>
25
30
#include < cctype>
26
31
#include < fstream>
27
32
#include < memory>
33
+ #include < optional>
28
34
#include < stdexcept>
29
35
30
36
using namespace std ;
@@ -47,6 +53,17 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
47
53
m_lineOffset(m_reader.lineNumber()),
48
54
m_enforceViaYul(enforceViaYul)
49
55
{
56
+ using namespace placeholders ;
57
+ auto simpleSmokeBuiltin = bind (&SemanticTest::builtinSmokeTest, this , _1);
58
+ m_builtins = {
59
+ {" smoke.test0" , simpleSmokeBuiltin},
60
+ {" smoke.test1" , simpleSmokeBuiltin},
61
+ {" smoke.test2" , simpleSmokeBuiltin},
62
+ };
63
+ m_testHooks = {
64
+ make_shared<SmokeHook>()
65
+ };
66
+
50
67
string choice = m_reader.stringSetting (" compileViaYul" , " default" );
51
68
if (choice == " also" )
52
69
{
@@ -119,7 +136,18 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
119
136
return result;
120
137
}
121
138
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[_name] = _builtin;
142
+ }
143
+
144
+ TestCase::TestResult SemanticTest::runTest (
145
+ ostream& _stream,
146
+ string const & _linePrefix,
147
+ bool _formatted,
148
+ bool _compileViaYul,
149
+ bool _compileToEwasm
150
+ )
123
151
{
124
152
bool success = true ;
125
153
@@ -142,21 +170,40 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
142
170
if (_compileViaYul)
143
171
AnsiColorized (_stream, _formatted, {BOLD, CYAN}) << _linePrefix << " Running via Yul:" << endl;
144
172
145
- for (auto & test: m_tests)
173
+ for (TestFunctionCall & test: m_tests)
146
174
test.reset ();
147
175
148
176
map<string, solidity::test::Address> libraries;
149
177
150
178
bool constructed = false ;
151
179
152
- for (auto & test: m_tests)
180
+ // Iterate through the test calls and set the previous call.
181
+ TestFunctionCall* previousCall{nullptr };
182
+ for (TestFunctionCall& test: m_tests)
183
+ {
184
+ test.setPreviousCall (previousCall);
185
+ test.setTestHooks (&m_testHooks);
186
+ previousCall = &test;
187
+ }
188
+
189
+ for (std::shared_ptr<TestHook>& hook: m_testHooks)
190
+ hook->beginTestCase ();
191
+
192
+ for (TestFunctionCall& test: m_tests)
153
193
{
194
+ for (std::shared_ptr<TestHook>& hook: m_testHooks)
195
+ hook->beforeFunctionCall (test);
196
+
154
197
if (constructed)
155
198
{
156
- soltestAssert (test.call ().kind != FunctionCall::Kind::Library, " Libraries have to be deployed before any other call." );
199
+ soltestAssert (
200
+ test.call ().kind != FunctionCall::Kind::Library,
201
+ " Libraries have to be deployed before any other call."
202
+ );
157
203
soltestAssert (
158
204
test.call ().kind != FunctionCall::Kind::Constructor,
159
- " Constructor has to be the first function call expect for library deployments." );
205
+ " Constructor has to be the first function call expect for library deployments."
206
+ );
160
207
}
161
208
else if (test.call ().kind == FunctionCall::Kind::Library)
162
209
{
@@ -197,6 +244,18 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
197
244
bytes output;
198
245
if (test.call ().kind == FunctionCall::Kind::LowLevel)
199
246
output = callLowLevel (test.call ().arguments .rawBytes (), test.call ().value .value );
247
+ else if (test.call ().kind == FunctionCall::Kind::Builtin)
248
+ {
249
+ auto builtin = m_builtins[test.call ().signature ];
250
+ std::optional<bytes> builtinOutput{builtin (test.call ())};
251
+ if (builtinOutput.has_value ())
252
+ {
253
+ test.setFailure (false );
254
+ output = builtinOutput.value ();
255
+ }
256
+ else
257
+ test.setFailure (true );
258
+ }
200
259
else
201
260
{
202
261
soltestAssert (
@@ -212,20 +271,60 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
212
271
);
213
272
}
214
273
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 ;
274
+ bytes expectationOutput;
275
+ if (test.call ().expectations .builtin )
276
+ {
277
+ auto builtin = m_builtins[test.call ().expectations .builtin ->signature ];
278
+ if (std::optional<bytes> builtinResult = builtin (*test.call ().expectations .builtin ))
279
+ expectationOutput = builtinResult.value ();
280
+ else
281
+ test.setFailure (true );
282
+ }
283
+ else
284
+ expectationOutput = test.call ().expectations .rawBytes ();
222
285
223
- test.setFailure (!m_transactionSuccessful);
286
+ bool outputMismatch = (output != expectationOutput);
287
+ if (test.call ().kind == FunctionCall::Kind::Builtin)
288
+ {
289
+ if (outputMismatch)
290
+ success = false ;
291
+ }
292
+ else
293
+ {
294
+ // Pre byzantium, it was not possible to return failure data, so we disregard
295
+ // output mismatch for those EVM versions.
296
+ if (test.call ().expectations .failure && !m_transactionSuccessful && !m_evmVersion.supportsReturndata ())
297
+ outputMismatch = false ;
298
+ if (m_transactionSuccessful != !test.call ().expectations .failure || outputMismatch)
299
+ success = false ;
300
+ test.setFailure (!m_transactionSuccessful);
301
+ }
224
302
test.setRawBytes (std::move (output));
225
303
test.setContractABI (m_compiler.contractABI (m_compiler.lastContractName ()));
226
304
}
305
+
306
+ for (std::shared_ptr<TestHook>& hook: m_testHooks)
307
+ hook->afterFunctionCall (test);
308
+ }
309
+
310
+ // The artificialFunctionCall is an artificially created function call,
311
+ // where it's previous call is pointing to the last call of the test.
312
+ TestFunctionCall artificialFunctionCall (FunctionCall{});
313
+ artificialFunctionCall.setTestHooks (&m_testHooks);
314
+ artificialFunctionCall.setPreviousCall (previousCall);
315
+ for (std::shared_ptr<TestHook>& hook: m_testHooks)
316
+ {
317
+ hook->beforeFunctionCall (artificialFunctionCall);
318
+ hook->afterFunctionCall (artificialFunctionCall);
227
319
}
228
320
321
+ for (std::shared_ptr<TestHook>& hook: m_testHooks)
322
+ hook->endTestCase ();
323
+
324
+ for (TestFunctionCall& test: m_tests)
325
+ for (std::shared_ptr<TestHook>& hook: m_testHooks)
326
+ success &= hook->verifyFunctionCall (test);
327
+
229
328
if (!m_runWithYul && _compileViaYul)
230
329
{
231
330
m_compileViaYulCanBeSet = success;
@@ -241,15 +340,15 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
241
340
if (!success && (m_runWithYul || !_compileViaYul))
242
341
{
243
342
AnsiColorized (_stream, _formatted, {BOLD, CYAN}) << _linePrefix << " Expected result:" << endl;
244
- for (auto const & test: m_tests)
343
+ for (TestFunctionCall const & test: m_tests)
245
344
{
246
345
ErrorReporter errorReporter;
247
346
_stream << test.format (errorReporter, _linePrefix, false , _formatted) << endl;
248
347
_stream << errorReporter.format (_linePrefix, _formatted);
249
348
}
250
349
_stream << endl;
251
350
AnsiColorized (_stream, _formatted, {BOLD, CYAN}) << _linePrefix << " Obtained result:" << endl;
252
- for (auto const & test: m_tests)
351
+ for (TestFunctionCall const & test: m_tests)
253
352
{
254
353
ErrorReporter errorReporter;
255
354
_stream << test.format (errorReporter, _linePrefix, true , _formatted) << endl;
@@ -320,7 +419,7 @@ void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool
320
419
321
420
void SemanticTest::printUpdatedExpectations (ostream& _stream, string const &) const
322
421
{
323
- for (auto const & test: m_tests)
422
+ for (TestFunctionCall const & test: m_tests)
324
423
_stream << test.format (" " , true , false ) << endl;
325
424
}
326
425
@@ -340,13 +439,31 @@ void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePre
340
439
341
440
void SemanticTest::parseExpectations (istream& _stream)
342
441
{
343
- TestFileParser parser{_stream};
442
+ TestFileParser parser{_stream, & this -> m_builtins };
344
443
auto functionCalls = parser.parseFunctionCalls (m_lineOffset);
345
- std:: move (functionCalls.begin (), functionCalls.end (), back_inserter (m_tests));
444
+ move (functionCalls.begin (), functionCalls.end (), back_inserter (m_tests));
346
445
}
347
446
348
- bool SemanticTest::deploy (string const & _contractName, u256 const & _value, bytes const & _arguments, map<string, solidity::test::Address> const & _libraries)
447
+ bool SemanticTest::deploy (
448
+ string const & _contractName,
449
+ u256 const & _value,
450
+ bytes const & _arguments,
451
+ map<string, solidity::test::Address> const & _libraries
452
+ )
349
453
{
350
454
auto output = compileAndRunWithoutCheck (m_sources.sources , _value, _contractName, _arguments, _libraries);
351
455
return !output.empty () && m_transactionSuccessful;
352
456
}
457
+
458
+ std::optional<bytes> SemanticTest::builtinSmokeTest (FunctionCall const & call)
459
+ {
460
+ // This function is only used in test/libsolidity/semanticTests/builtins/smoke.sol.
461
+ std::optional<bytes> result;
462
+ if (call.arguments .parameters .size () < 3 )
463
+ {
464
+ result = bytes ();
465
+ for (const auto & parameter: call.arguments .parameters )
466
+ result.value () += util::toBigEndian (u256{util::fromHex (parameter.rawString )});
467
+ }
468
+ return result;
469
+ }
0 commit comments