1313*/
1414
1515#include < test/libsolidity/SemanticTest.h>
16+
1617#include < libsolutil/Whiskers.h>
1718#include < libyul/Exceptions.h>
1819#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+
1924#include < boost/algorithm/string.hpp>
2025#include < boost/algorithm/string/predicate.hpp>
2126#include < boost/algorithm/string/trim.hpp>
2530#include < cctype>
2631#include < fstream>
2732#include < memory>
33+ #include < optional>
2834#include < stdexcept>
2935
3036using namespace std ;
@@ -47,6 +53,17 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
4753 m_lineOffset(m_reader.lineNumber()),
4854 m_enforceViaYul(enforceViaYul)
4955{
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+
5067 string choice = m_reader.stringSetting (" compileViaYul" , " default" );
5168 if (choice == " also" )
5269 {
@@ -119,7 +136,18 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
119136 return result;
120137}
121138
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+ )
123151{
124152 bool success = true ;
125153
@@ -142,21 +170,40 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
142170 if (_compileViaYul)
143171 AnsiColorized (_stream, _formatted, {BOLD, CYAN}) << _linePrefix << " Running via Yul:" << endl;
144172
145- for (auto & test: m_tests)
173+ for (TestFunctionCall & test: m_tests)
146174 test.reset ();
147175
148176 map<string, solidity::test::Address> libraries;
149177
150178 bool constructed = false ;
151179
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)
153193 {
194+ for (std::shared_ptr<TestHook>& hook: m_testHooks)
195+ hook->beforeFunctionCall (test);
196+
154197 if (constructed)
155198 {
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+ );
157203 soltestAssert (
158204 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+ );
160207 }
161208 else if (test.call ().kind == FunctionCall::Kind::Library)
162209 {
@@ -197,6 +244,18 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
197244 bytes output;
198245 if (test.call ().kind == FunctionCall::Kind::LowLevel)
199246 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+ }
200259 else
201260 {
202261 soltestAssert (
@@ -212,20 +271,60 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
212271 );
213272 }
214273
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 ();
222285
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+ }
224302 test.setRawBytes (std::move (output));
225303 test.setContractABI (m_compiler.contractABI (m_compiler.lastContractName ()));
226304 }
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);
227319 }
228320
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+
229328 if (!m_runWithYul && _compileViaYul)
230329 {
231330 m_compileViaYulCanBeSet = success;
@@ -241,15 +340,15 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
241340 if (!success && (m_runWithYul || !_compileViaYul))
242341 {
243342 AnsiColorized (_stream, _formatted, {BOLD, CYAN}) << _linePrefix << " Expected result:" << endl;
244- for (auto const & test: m_tests)
343+ for (TestFunctionCall const & test: m_tests)
245344 {
246345 ErrorReporter errorReporter;
247346 _stream << test.format (errorReporter, _linePrefix, false , _formatted) << endl;
248347 _stream << errorReporter.format (_linePrefix, _formatted);
249348 }
250349 _stream << endl;
251350 AnsiColorized (_stream, _formatted, {BOLD, CYAN}) << _linePrefix << " Obtained result:" << endl;
252- for (auto const & test: m_tests)
351+ for (TestFunctionCall const & test: m_tests)
253352 {
254353 ErrorReporter errorReporter;
255354 _stream << test.format (errorReporter, _linePrefix, true , _formatted) << endl;
@@ -320,7 +419,7 @@ void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool
320419
321420void SemanticTest::printUpdatedExpectations (ostream& _stream, string const &) const
322421{
323- for (auto const & test: m_tests)
422+ for (TestFunctionCall const & test: m_tests)
324423 _stream << test.format (" " , true , false ) << endl;
325424}
326425
@@ -340,13 +439,31 @@ void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePre
340439
341440void SemanticTest::parseExpectations (istream& _stream)
342441{
343- TestFileParser parser{_stream};
442+ TestFileParser parser{_stream, & this -> m_builtins };
344443 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));
346445}
347446
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+ )
349453{
350454 auto output = compileAndRunWithoutCheck (m_sources.sources , _value, _contractName, _arguments, _libraries);
351455 return !output.empty () && m_transactionSuccessful;
352456}
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