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>
35+ #include < utility>
2936
3037using namespace std ;
3138using namespace solidity ;
@@ -47,6 +54,16 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
4754 m_lineOffset(m_reader.lineNumber()),
4855 m_enforceViaYul(enforceViaYul)
4956{
57+ using namespace placeholders ;
58+
59+ auto simpleSmokeBuiltin = bind (&SemanticTest::builtinSmokeTest, this , _1);
60+ addBuiltin (" smoke.test0" , simpleSmokeBuiltin);
61+ addBuiltin (" smoke.test1" , simpleSmokeBuiltin);
62+ addBuiltin (" smoke.test2" , simpleSmokeBuiltin);
63+ addBuiltin (" smoke_test3" , simpleSmokeBuiltin);
64+
65+ addTestHook (make_unique<SmokeHook>());
66+
5067 string choice = m_reader.stringSetting (" compileViaYul" , " default" );
5168 if (choice == " also" )
5269 {
@@ -119,7 +136,23 @@ 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[std::move (_name)] = std::move (_builtin);
142+ }
143+
144+ void SemanticTest::addTestHook (std::unique_ptr<TestHook> _testHook)
145+ {
146+ m_testHooks.emplace_back (std::move (_testHook));
147+ }
148+
149+ TestCase::TestResult SemanticTest::runTest (
150+ ostream& _stream,
151+ string const & _linePrefix,
152+ bool _formatted,
153+ bool _compileViaYul,
154+ bool _compileToEwasm
155+ )
123156{
124157 bool success = true ;
125158
@@ -142,21 +175,40 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
142175 if (_compileViaYul)
143176 AnsiColorized (_stream, _formatted, {BOLD, CYAN}) << _linePrefix << " Running via Yul:" << endl;
144177
145- for (auto & test: m_tests)
178+ for (TestFunctionCall & test: m_tests)
146179 test.reset ();
147180
148181 map<string, solidity::test::Address> libraries;
149182
150183 bool constructed = false ;
151184
152- for (auto & test: m_tests)
185+ // Iterate through the test calls and set the previous call.
186+ TestFunctionCall* previousCall{nullptr };
187+ for (TestFunctionCall& test: m_tests)
153188 {
189+ test.setPreviousCall (previousCall);
190+ test.setTestHooks (&m_testHooks);
191+ previousCall = &test;
192+ }
193+
194+ for (std::unique_ptr<TestHook> const & hook: m_testHooks)
195+ hook->beginTestCase ();
196+
197+ for (TestFunctionCall& test: m_tests)
198+ {
199+ for (std::unique_ptr<TestHook> const & hook: m_testHooks)
200+ hook->beforeFunctionCall (test);
201+
154202 if (constructed)
155203 {
156- 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."
207+ );
157208 soltestAssert (
158209 test.call ().kind != FunctionCall::Kind::Constructor,
159- " Constructor has to be the first function call expect for library deployments." );
210+ " Constructor has to be the first function call expect for library deployments."
211+ );
160212 }
161213 else if (test.call ().kind == FunctionCall::Kind::Library)
162214 {
@@ -197,6 +249,18 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
197249 bytes output;
198250 if (test.call ().kind == FunctionCall::Kind::LowLevel)
199251 output = callLowLevel (test.call ().arguments .rawBytes (), test.call ().value .value );
252+ else if (test.call ().kind == FunctionCall::Kind::Builtin)
253+ {
254+ auto builtin = m_builtins[test.call ().signature ];
255+ std::optional<bytes> builtinOutput{builtin (test.call ())};
256+ if (builtinOutput.has_value ())
257+ {
258+ test.setFailure (false );
259+ output = builtinOutput.value ();
260+ }
261+ else
262+ test.setFailure (true );
263+ }
200264 else
201265 {
202266 soltestAssert (
@@ -212,20 +276,49 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
212276 );
213277 }
214278
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 ;
279+ bytes expectationOutput;
280+ if (test.call ().expectations .builtin )
281+ {
282+ auto builtin = m_builtins[test.call ().expectations .builtin ->signature ];
283+ if (std::optional<bytes> builtinResult = builtin (*test.call ().expectations .builtin ))
284+ expectationOutput = builtinResult.value ();
285+ else
286+ test.setFailure (true );
287+ }
288+ else
289+ expectationOutput = test.call ().expectations .rawBytes ();
222290
223- test.setFailure (!m_transactionSuccessful);
291+ bool outputMismatch = (output != expectationOutput);
292+ if (test.call ().kind == FunctionCall::Kind::Builtin)
293+ {
294+ if (outputMismatch)
295+ success = false ;
296+ }
297+ else
298+ {
299+ // Pre byzantium, it was not possible to return failure data, so we disregard
300+ // output mismatch for those EVM versions.
301+ if (test.call ().expectations .failure && !m_transactionSuccessful && !m_evmVersion.supportsReturndata ())
302+ outputMismatch = false ;
303+ if (m_transactionSuccessful != !test.call ().expectations .failure || outputMismatch)
304+ success = false ;
305+ test.setFailure (!m_transactionSuccessful);
306+ }
224307 test.setRawBytes (std::move (output));
225308 test.setContractABI (m_compiler.contractABI (m_compiler.lastContractName ()));
226309 }
310+
311+ for (std::unique_ptr<TestHook> const & hook: m_testHooks)
312+ hook->afterFunctionCall (test);
227313 }
228314
315+ for (std::unique_ptr<TestHook> const & hook: m_testHooks)
316+ hook->endTestCase ();
317+
318+ for (TestFunctionCall& test: m_tests)
319+ for (std::unique_ptr<TestHook> const & hook: m_testHooks)
320+ success &= hook->verifyFunctionCall (test);
321+
229322 if (!m_runWithYul && _compileViaYul)
230323 {
231324 m_compileViaYulCanBeSet = success;
@@ -241,15 +334,15 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
241334 if (!success && (m_runWithYul || !_compileViaYul))
242335 {
243336 AnsiColorized (_stream, _formatted, {BOLD, CYAN}) << _linePrefix << " Expected result:" << endl;
244- for (auto const & test: m_tests)
337+ for (TestFunctionCall const & test: m_tests)
245338 {
246339 ErrorReporter errorReporter;
247340 _stream << test.format (errorReporter, _linePrefix, false , _formatted) << endl;
248341 _stream << errorReporter.format (_linePrefix, _formatted);
249342 }
250343 _stream << endl;
251344 AnsiColorized (_stream, _formatted, {BOLD, CYAN}) << _linePrefix << " Obtained result:" << endl;
252- for (auto const & test: m_tests)
345+ for (TestFunctionCall const & test: m_tests)
253346 {
254347 ErrorReporter errorReporter;
255348 _stream << test.format (errorReporter, _linePrefix, true , _formatted) << endl;
@@ -320,7 +413,7 @@ void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool
320413
321414void SemanticTest::printUpdatedExpectations (ostream& _stream, string const &) const
322415{
323- for (auto const & test: m_tests)
416+ for (TestFunctionCall const & test: m_tests)
324417 _stream << test.format (" " , true , false ) << endl;
325418}
326419
@@ -340,13 +433,31 @@ void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePre
340433
341434void SemanticTest::parseExpectations (istream& _stream)
342435{
343- TestFileParser parser{_stream};
436+ TestFileParser parser{_stream, & this -> m_builtins };
344437 auto functionCalls = parser.parseFunctionCalls (m_lineOffset);
345- std:: move (functionCalls.begin (), functionCalls.end (), back_inserter (m_tests));
438+ move (functionCalls.begin (), functionCalls.end (), back_inserter (m_tests));
346439}
347440
348- bool SemanticTest::deploy (string const & _contractName, u256 const & _value, bytes const & _arguments, map<string, solidity::test::Address> const & _libraries)
441+ bool SemanticTest::deploy (
442+ string const & _contractName,
443+ u256 const & _value,
444+ bytes const & _arguments,
445+ map<string, solidity::test::Address> const & _libraries
446+ )
349447{
350448 auto output = compileAndRunWithoutCheck (m_sources.sources , _value, _contractName, _arguments, _libraries);
351449 return !output.empty () && m_transactionSuccessful;
352450}
451+
452+ std::optional<bytes> SemanticTest::builtinSmokeTest (FunctionCall const & call)
453+ {
454+ // This function is only used in test/libsolidity/semanticTests/builtins/smoke.sol.
455+ std::optional<bytes> result;
456+ if (call.arguments .parameters .size () < 3 )
457+ {
458+ result = bytes ();
459+ for (const auto & parameter: call.arguments .parameters )
460+ result.value () += util::toBigEndian (u256{util::fromHex (parameter.rawString )});
461+ }
462+ return result;
463+ }
0 commit comments