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,31 @@ 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+ for (std::unique_ptr<TestHook> const & hook: m_testHooks)
186+ hook->beginTestCase ();
187+
188+ for (TestFunctionCall& test: m_tests)
153189 {
190+ for (std::unique_ptr<TestHook> const & hook: m_testHooks)
191+ hook->beforeFunctionCall (test);
192+
154193 if (constructed)
155194 {
156- soltestAssert (test.call ().kind != FunctionCall::Kind::Library, " Libraries have to be deployed before any other call." );
195+ soltestAssert (
196+ test.call ().kind != FunctionCall::Kind::Library,
197+ " Libraries have to be deployed before any other call."
198+ );
157199 soltestAssert (
158200 test.call ().kind != FunctionCall::Kind::Constructor,
159- " Constructor has to be the first function call expect for library deployments." );
201+ " Constructor has to be the first function call expect for library deployments."
202+ );
160203 }
161204 else if (test.call ().kind == FunctionCall::Kind::Library)
162205 {
@@ -197,6 +240,18 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
197240 bytes output;
198241 if (test.call ().kind == FunctionCall::Kind::LowLevel)
199242 output = callLowLevel (test.call ().arguments .rawBytes (), test.call ().value .value );
243+ else if (test.call ().kind == FunctionCall::Kind::Builtin)
244+ {
245+ auto builtin = m_builtins[test.call ().signature ];
246+ std::optional<bytes> builtinOutput{builtin (test.call ())};
247+ if (builtinOutput.has_value ())
248+ {
249+ test.setFailure (false );
250+ output = builtinOutput.value ();
251+ }
252+ else
253+ test.setFailure (true );
254+ }
200255 else
201256 {
202257 soltestAssert (
@@ -212,20 +267,49 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
212267 );
213268 }
214269
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 ;
270+ bytes expectationOutput;
271+ if (test.call ().expectations .builtin )
272+ {
273+ auto builtin = m_builtins[test.call ().expectations .builtin ->signature ];
274+ if (std::optional<bytes> builtinResult = builtin (*test.call ().expectations .builtin ))
275+ expectationOutput = builtinResult.value ();
276+ else
277+ test.setFailure (true );
278+ }
279+ else
280+ expectationOutput = test.call ().expectations .rawBytes ();
222281
223- test.setFailure (!m_transactionSuccessful);
282+ bool outputMismatch = (output != expectationOutput);
283+ if (test.call ().kind == FunctionCall::Kind::Builtin)
284+ {
285+ if (outputMismatch)
286+ success = false ;
287+ }
288+ else
289+ {
290+ // Pre byzantium, it was not possible to return failure data, so we disregard
291+ // output mismatch for those EVM versions.
292+ if (test.call ().expectations .failure && !m_transactionSuccessful && !m_evmVersion.supportsReturndata ())
293+ outputMismatch = false ;
294+ if (m_transactionSuccessful != !test.call ().expectations .failure || outputMismatch)
295+ success = false ;
296+ test.setFailure (!m_transactionSuccessful);
297+ }
224298 test.setRawBytes (std::move (output));
225299 test.setContractABI (m_compiler.contractABI (m_compiler.lastContractName ()));
226300 }
301+
302+ for (std::unique_ptr<TestHook> const & hook: m_testHooks)
303+ hook->afterFunctionCall (test);
227304 }
228305
306+ for (std::unique_ptr<TestHook> const & hook: m_testHooks)
307+ hook->endTestCase ();
308+
309+ for (TestFunctionCall& test: m_tests)
310+ for (std::unique_ptr<TestHook> const & hook: m_testHooks)
311+ success &= hook->verifyFunctionCall (test);
312+
229313 if (!m_runWithYul && _compileViaYul)
230314 {
231315 m_compileViaYulCanBeSet = success;
@@ -241,18 +325,18 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
241325 if (!success && (m_runWithYul || !_compileViaYul))
242326 {
243327 AnsiColorized (_stream, _formatted, {BOLD, CYAN}) << _linePrefix << " Expected result:" << endl;
244- for (auto const & test: m_tests)
328+ for (TestFunctionCall const & test: m_tests)
245329 {
246330 ErrorReporter errorReporter;
247- _stream << test.format (errorReporter, _linePrefix, false , _formatted) << endl;
331+ _stream << test.format (errorReporter, _linePrefix, false , _formatted, &m_testHooks ) << endl;
248332 _stream << errorReporter.format (_linePrefix, _formatted);
249333 }
250334 _stream << endl;
251335 AnsiColorized (_stream, _formatted, {BOLD, CYAN}) << _linePrefix << " Obtained result:" << endl;
252- for (auto const & test: m_tests)
336+ for (TestFunctionCall const & test: m_tests)
253337 {
254338 ErrorReporter errorReporter;
255- _stream << test.format (errorReporter, _linePrefix, true , _formatted) << endl;
339+ _stream << test.format (errorReporter, _linePrefix, true , _formatted, &m_testHooks ) << endl;
256340 _stream << errorReporter.format (_linePrefix, _formatted);
257341 }
258342 AnsiColorized (_stream, _formatted, {BOLD, RED})
@@ -320,8 +404,8 @@ void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool
320404
321405void SemanticTest::printUpdatedExpectations (ostream& _stream, string const &) const
322406{
323- for (auto const & test: m_tests)
324- _stream << test.format (" " , true , false ) << endl;
407+ for (TestFunctionCall const & test: m_tests)
408+ _stream << test.format (" " , true , false , &m_testHooks ) << endl;
325409}
326410
327411void SemanticTest::printUpdatedSettings (ostream& _stream, string const & _linePrefix)
@@ -340,13 +424,31 @@ void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePre
340424
341425void SemanticTest::parseExpectations (istream& _stream)
342426{
343- TestFileParser parser{_stream};
427+ TestFileParser parser{_stream, & this -> m_builtins };
344428 auto functionCalls = parser.parseFunctionCalls (m_lineOffset);
345- std:: move (functionCalls.begin (), functionCalls.end (), back_inserter (m_tests));
429+ move (functionCalls.begin (), functionCalls.end (), back_inserter (m_tests));
346430}
347431
348- bool SemanticTest::deploy (string const & _contractName, u256 const & _value, bytes const & _arguments, map<string, solidity::test::Address> const & _libraries)
432+ bool SemanticTest::deploy (
433+ string const & _contractName,
434+ u256 const & _value,
435+ bytes const & _arguments,
436+ map<string, solidity::test::Address> const & _libraries
437+ )
349438{
350439 auto output = compileAndRunWithoutCheck (m_sources.sources , _value, _contractName, _arguments, _libraries);
351440 return !output.empty () && m_transactionSuccessful;
352441}
442+
443+ std::optional<bytes> SemanticTest::builtinSmokeTest (FunctionCall const & call)
444+ {
445+ // This function is only used in test/libsolidity/semanticTests/builtins/smoke.sol.
446+ std::optional<bytes> result;
447+ if (call.arguments .parameters .size () < 3 )
448+ {
449+ result = bytes ();
450+ for (const auto & parameter: call.arguments .parameters )
451+ result.value () += util::toBigEndian (u256{util::fromHex (parameter.rawString )});
452+ }
453+ return result;
454+ }
0 commit comments