1 /*
2 This file is part of solidity.
3 solidity is free software: you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation, either version 3 of the License, or
6 (at your option) any later version.
7 solidity is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11 You should have received a copy of the GNU General Public License
12 along with solidity. If not, see <http://www.gnu.org/licenses/>.
13 */
14
15 #include <test/libsolidity/SemanticTest.h>
16
17 #include <libsolutil/Whiskers.h>
18 #include <libyul/Exceptions.h>
19 #include <test/Common.h>
20 #include <test/libsolidity/util/BytesUtils.h>
21
22 #include <boost/algorithm/string.hpp>
23 #include <boost/algorithm/string/predicate.hpp>
24 #include <boost/algorithm/string/trim.hpp>
25 #include <boost/throw_exception.hpp>
26
27 #include <algorithm>
28 #include <cctype>
29 #include <fstream>
30 #include <functional>
31 #include <memory>
32 #include <optional>
33 #include <stdexcept>
34 #include <string>
35 #include <utility>
36
37 using namespace std;
38 using namespace solidity;
39 using namespace solidity::yul;
40 using namespace solidity::langutil;
41 using namespace solidity::util;
42 using namespace solidity::util::formatting;
43 using namespace solidity::frontend::test;
44 using namespace boost::algorithm;
45 using namespace boost::unit_test;
46 namespace fs = boost::filesystem;
47
SemanticTest(string const & _filename,langutil::EVMVersion _evmVersion,vector<boost::filesystem::path> const & _vmPaths,bool _enforceViaYul,bool _enforceCompileToEwasm,bool _enforceGasCost,u256 _enforceGasCostMinValue)48 SemanticTest::SemanticTest(
49 string const& _filename,
50 langutil::EVMVersion _evmVersion,
51 vector<boost::filesystem::path> const& _vmPaths,
52 bool _enforceViaYul,
53 bool _enforceCompileToEwasm,
54 bool _enforceGasCost,
55 u256 _enforceGasCostMinValue
56 ):
57 SolidityExecutionFramework(_evmVersion, _vmPaths),
58 EVMVersionRestrictedTestCase(_filename),
59 m_sources(m_reader.sources()),
60 m_lineOffset(m_reader.lineNumber()),
61 m_builtins(makeBuiltins()),
62 m_sideEffectHooks(makeSideEffectHooks()),
63 m_enforceViaYul(_enforceViaYul),
64 m_enforceCompileToEwasm(_enforceCompileToEwasm),
65 m_enforceGasCost(_enforceGasCost),
66 m_enforceGasCostMinValue(move(_enforceGasCostMinValue))
67 {
68 static set<string> const compileViaYulAllowedValues{"also", "true", "false", "default"};
69 static set<string> const yulRunTriggers{"also", "true"};
70 static set<string> const legacyRunTriggers{"also", "false", "default"};
71
72 string compileViaYul = m_reader.stringSetting("compileViaYul", "default");
73 if (!contains(compileViaYulAllowedValues, compileViaYul))
74 BOOST_THROW_EXCEPTION(runtime_error("Invalid compileViaYul value: " + compileViaYul + "."));
75 m_testCaseWantsYulRun = contains(yulRunTriggers, compileViaYul);
76 m_testCaseWantsLegacyRun = contains(legacyRunTriggers, compileViaYul);
77
78 // Do not enforce via yul and ewasm, if via yul was explicitly denied.
79 if (compileViaYul == "false")
80 {
81 m_enforceViaYul = false;
82 m_enforceCompileToEwasm = false;
83 }
84
85 string compileToEwasm = m_reader.stringSetting("compileToEwasm", "false");
86 if (compileToEwasm == "also")
87 m_testCaseWantsEwasmRun = true;
88 else if (compileToEwasm == "false")
89 m_testCaseWantsEwasmRun = false;
90 else
91 BOOST_THROW_EXCEPTION(runtime_error("Invalid compileToEwasm value: " + compileToEwasm + "."));
92
93 if (m_testCaseWantsEwasmRun && !m_testCaseWantsYulRun)
94 BOOST_THROW_EXCEPTION(runtime_error("Invalid compileToEwasm value: " + compileToEwasm + ", compileViaYul need to be enabled."));
95
96 // run ewasm tests only if an ewasm evmc vm was defined
97 if (m_testCaseWantsEwasmRun && !m_supportsEwasm)
98 m_testCaseWantsEwasmRun = false;
99
100 m_runWithABIEncoderV1Only = m_reader.boolSetting("ABIEncoderV1Only", false);
101 if (m_runWithABIEncoderV1Only && !solidity::test::CommonOptions::get().useABIEncoderV1)
102 m_shouldRun = false;
103
104 // Sanity check
105 if (m_runWithABIEncoderV1Only && (compileViaYul == "true" || compileViaYul == "also"))
106 BOOST_THROW_EXCEPTION(runtime_error(
107 "ABIEncoderV1Only can not be used with compileViaYul=" + compileViaYul +
108 ", set it to false or omit the flag. The compileViaYul setting ignores the abicoder pragma"
109 " and runs everything with ABICoder V2."
110 ));
111
112 auto revertStrings = revertStringsFromString(m_reader.stringSetting("revertStrings", "default"));
113 soltestAssert(revertStrings, "Invalid revertStrings setting.");
114 m_revertStrings = revertStrings.value();
115
116 m_allowNonExistingFunctions = m_reader.boolSetting("allowNonExistingFunctions", false);
117
118 parseExpectations(m_reader.stream());
119 soltestAssert(!m_tests.empty(), "No tests specified in " + _filename);
120
121 if (m_enforceGasCost)
122 {
123 m_compiler.setMetadataFormat(CompilerStack::MetadataFormat::NoMetadata);
124 m_compiler.setMetadataHash(CompilerStack::MetadataHash::None);
125 }
126 }
127
makeBuiltins()128 map<string, Builtin> SemanticTest::makeBuiltins()
129 {
130 return {
131 {
132 "isoltest_builtin_test",
133 [](FunctionCall const&) -> optional<bytes>
134 {
135 return toBigEndian(u256(0x1234));
136 }
137 },
138 {
139 "isoltest_side_effects_test",
140 [](FunctionCall const& _call) -> optional<bytes>
141 {
142 if (_call.arguments.parameters.empty())
143 return toBigEndian(0);
144 else
145 return _call.arguments.rawBytes();
146 }
147 },
148 {
149 "balance",
150 [this](FunctionCall const& _call) -> optional<bytes>
151 {
152 soltestAssert(_call.arguments.parameters.size() <= 1, "Account address expected.");
153 h160 address;
154 if (_call.arguments.parameters.size() == 1)
155 address = h160(_call.arguments.parameters.at(0).rawString);
156 else
157 address = m_contractAddress;
158 return toBigEndian(balanceAt(address));
159 }
160 },
161 {
162 "storageEmpty",
163 [this](FunctionCall const& _call) -> optional<bytes>
164 {
165 soltestAssert(_call.arguments.parameters.empty(), "No arguments expected.");
166 return toBigEndian(u256(storageEmpty(m_contractAddress) ? 1 : 0));
167 }
168 },
169 {
170 "account",
171 [this](FunctionCall const& _call) -> optional<bytes>
172 {
173 soltestAssert(_call.arguments.parameters.size() == 1, "Account number expected.");
174 size_t accountNumber = static_cast<size_t>(stoi(_call.arguments.parameters.at(0).rawString));
175 // Need to pad it to 32-bytes to workaround limitations in BytesUtils::formatHex.
176 return toBigEndian(h256(ExecutionFramework::setAccount(accountNumber).asBytes(), h256::AlignRight));
177 }
178 },
179 };
180 }
181
makeSideEffectHooks() const182 vector<SideEffectHook> SemanticTest::makeSideEffectHooks() const
183 {
184 using namespace std::placeholders;
185 return {
186 [](FunctionCall const& _call) -> vector<string>
187 {
188 if (_call.signature == "isoltest_side_effects_test")
189 {
190 vector<string> result;
191 for (auto const& argument: _call.arguments.parameters)
192 result.emplace_back(toHex(argument.rawBytes));
193 return result;
194 }
195 return {};
196 },
197 bind(&SemanticTest::eventSideEffectHook, this, _1)
198 };
199 }
200
formatEventParameter(optional<AnnotatedEventSignature> _signature,bool _indexed,size_t _index,bytes const & _data)201 string SemanticTest::formatEventParameter(optional<AnnotatedEventSignature> _signature, bool _indexed, size_t _index, bytes const& _data)
202 {
203 auto isPrintableASCII = [](bytes const& s)
204 {
205 bool zeroes = true;
206 for (auto c: s)
207 {
208 if (static_cast<unsigned>(c) != 0x00)
209 {
210 zeroes = false;
211 if (static_cast<unsigned>(c) <= 0x1f || static_cast<unsigned>(c) >= 0x7f)
212 return false;
213 } else
214 break;
215 }
216 return !zeroes;
217 };
218
219 ABIType abiType(ABIType::Type::Hex);
220 if (isPrintableASCII(_data))
221 abiType = ABIType(ABIType::Type::String);
222 if (_signature.has_value())
223 {
224 vector<string> const& types = _indexed ? _signature->indexedTypes : _signature->nonIndexedTypes;
225 if (_index < types.size())
226 {
227 if (types.at(_index) == "bool")
228 abiType = ABIType(ABIType::Type::Boolean);
229 }
230 }
231 return BytesUtils::formatBytes(_data, abiType);
232 }
233
eventSideEffectHook(FunctionCall const &) const234 vector<string> SemanticTest::eventSideEffectHook(FunctionCall const&) const
235 {
236 vector<string> sideEffects;
237 vector<LogRecord> recordedLogs = ExecutionFramework::recordedLogs();
238 for (LogRecord const& log: recordedLogs)
239 {
240 optional<AnnotatedEventSignature> eventSignature;
241 if (!log.topics.empty())
242 eventSignature = matchEvent(log.topics[0]);
243 stringstream sideEffect;
244 sideEffect << "emit ";
245 if (eventSignature.has_value())
246 sideEffect << eventSignature.value().signature;
247 else
248 sideEffect << "<anonymous>";
249
250 if (m_contractAddress != log.creator)
251 sideEffect << " from 0x" << log.creator;
252
253 vector<string> eventStrings;
254 size_t index{0};
255 for (h256 const& topic: log.topics)
256 {
257 if (!eventSignature.has_value() || index != 0)
258 eventStrings.push_back("#" + formatEventParameter(eventSignature, true, index, topic.asBytes()));
259 ++index;
260 }
261
262 soltestAssert(log.data.size() % 32 == 0, "");
263 for (size_t index = 0; index < log.data.size() / 32; ++index)
264 {
265 auto begin = log.data.begin() + static_cast<long>(index * 32);
266 bytes const& data = bytes{begin, begin + 32};
267 eventStrings.emplace_back(formatEventParameter(eventSignature, false, index, data));
268 }
269
270 if (!eventStrings.empty())
271 sideEffect << ": ";
272 sideEffect << joinHumanReadable(eventStrings);
273 sideEffects.emplace_back(sideEffect.str());
274 }
275 return sideEffects;
276 }
277
matchEvent(util::h256 const & hash) const278 optional<AnnotatedEventSignature> SemanticTest::matchEvent(util::h256 const& hash) const
279 {
280 optional<AnnotatedEventSignature> result;
281 for (string& contractName: m_compiler.contractNames())
282 {
283 ContractDefinition const& contract = m_compiler.contractDefinition(contractName);
284 for (EventDefinition const* event: contract.events())
285 {
286 FunctionTypePointer eventFunctionType = event->functionType(true);
287 if (!event->isAnonymous() && keccak256(eventFunctionType->externalSignature()) == hash)
288 {
289 AnnotatedEventSignature eventInfo;
290 eventInfo.signature = eventFunctionType->externalSignature();
291 for (auto const& param: event->parameters())
292 if (param->isIndexed())
293 eventInfo.indexedTypes.emplace_back(param->type()->toString(true));
294 else
295 eventInfo.nonIndexedTypes.emplace_back(param->type()->toString(true));
296 result = eventInfo;
297 }
298 }
299 }
300 return result;
301 }
302
run(ostream & _stream,string const & _linePrefix,bool _formatted)303 TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)
304 {
305 TestResult result = TestResult::Success;
306
307 if (m_testCaseWantsLegacyRun)
308 result = runTest(_stream, _linePrefix, _formatted, false, false);
309
310 if ((m_testCaseWantsYulRun || m_enforceViaYul) && result == TestResult::Success)
311 result = runTest(_stream, _linePrefix, _formatted, true, false);
312
313 if ((m_testCaseWantsEwasmRun || m_enforceCompileToEwasm) && result == TestResult::Success)
314 {
315 // TODO: Once we have full Ewasm support, we could remove try/catch here.
316 try
317 {
318 result = runTest(_stream, _linePrefix, _formatted, true, true);
319 }
320 catch (...)
321 {
322 if (!m_enforceCompileToEwasm)
323 throw;
324 }
325 }
326 return result;
327 }
328
runTest(ostream & _stream,string const & _linePrefix,bool _formatted,bool _isYulRun,bool _isEwasmRun)329 TestCase::TestResult SemanticTest::runTest(
330 ostream& _stream,
331 string const& _linePrefix,
332 bool _formatted,
333 bool _isYulRun,
334 bool _isEwasmRun)
335 {
336 bool success = true;
337 m_gasCostFailure = false;
338
339 if (_isEwasmRun)
340 {
341 soltestAssert(_isYulRun, "");
342 selectVM(evmc_capabilities::EVMC_CAPABILITY_EWASM);
343 }
344 else
345 selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1);
346
347 reset();
348
349 m_compileViaYul = _isYulRun;
350 if (_isEwasmRun)
351 {
352 soltestAssert(m_compileViaYul, "");
353 m_compileToEwasm = _isEwasmRun;
354 }
355
356 m_canEnableYulRun = false;
357 m_canEnableEwasmRun = false;
358
359 if (_isYulRun)
360 AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul" << (_isEwasmRun ? " (ewasm):" : ":") << endl;
361
362 for (TestFunctionCall& test: m_tests)
363 test.reset();
364
365 map<string, solidity::test::Address> libraries;
366
367 bool constructed = false;
368
369 for (TestFunctionCall& test: m_tests)
370 {
371 if (constructed)
372 {
373 soltestAssert(
374 test.call().kind != FunctionCall::Kind::Library,
375 "Libraries have to be deployed before any other call."
376 );
377 soltestAssert(
378 test.call().kind != FunctionCall::Kind::Constructor,
379 "Constructor has to be the first function call expect for library deployments."
380 );
381 }
382 else if (test.call().kind == FunctionCall::Kind::Library)
383 {
384 soltestAssert(
385 deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful,
386 "Failed to deploy library " + test.call().signature);
387 // For convenience, in semantic tests we assume that an unqualified name like `L` is equivalent to one
388 // with an empty source unit name (`:L`). This is fine because the compiler never uses unqualified
389 // names in the Yul code it produces and does not allow `linkersymbol()` at all in inline assembly.
390 if (test.call().signature.find(':') == string::npos)
391 libraries[":" + test.call().signature] = m_contractAddress;
392 else
393 libraries[test.call().signature] = m_contractAddress;
394 continue;
395 }
396 else
397 {
398 if (test.call().kind == FunctionCall::Kind::Constructor)
399 deploy("", test.call().value.value, test.call().arguments.rawBytes(), libraries);
400 else
401 soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract.");
402 constructed = true;
403 }
404
405 if (test.call().kind == FunctionCall::Kind::Constructor)
406 {
407 if (m_transactionSuccessful == test.call().expectations.failure)
408 success = false;
409 if (success && !checkGasCostExpectation(test, _isYulRun))
410 {
411 success = false;
412 m_gasCostFailure = true;
413 }
414
415 test.setFailure(!m_transactionSuccessful);
416 test.setRawBytes(bytes());
417 }
418 else
419 {
420 bytes output;
421 if (test.call().kind == FunctionCall::Kind::LowLevel)
422 output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
423 else if (test.call().kind == FunctionCall::Kind::Builtin)
424 {
425 optional<bytes> builtinOutput = m_builtins.at(test.call().signature)(test.call());
426 if (builtinOutput.has_value())
427 {
428 m_transactionSuccessful = true;
429 output = builtinOutput.value();
430 }
431 else
432 m_transactionSuccessful = false;
433 }
434 else
435 {
436 soltestAssert(
437 m_allowNonExistingFunctions ||
438 m_compiler.methodIdentifiers(m_compiler.lastContractName(m_sources.mainSourceFile)).isMember(test.call().signature),
439 "The function " + test.call().signature + " is not known to the compiler"
440 );
441
442 output = callContractFunctionWithValueNoEncoding(
443 test.call().signature,
444 test.call().value.value,
445 test.call().arguments.rawBytes()
446 );
447 }
448
449 bool outputMismatch = (output != test.call().expectations.rawBytes());
450 if (!outputMismatch && !checkGasCostExpectation(test, _isYulRun))
451 {
452 success = false;
453 m_gasCostFailure = true;
454 }
455
456 // Pre byzantium, it was not possible to return failure data, so we disregard
457 // output mismatch for those EVM versions.
458 if (test.call().expectations.failure && !m_transactionSuccessful && !m_evmVersion.supportsReturndata())
459 outputMismatch = false;
460 if (m_transactionSuccessful != !test.call().expectations.failure || outputMismatch)
461 success = false;
462
463 test.setFailure(!m_transactionSuccessful);
464 test.setRawBytes(move(output));
465 test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName(m_sources.mainSourceFile)));
466 }
467
468 vector<string> effects;
469 for (SideEffectHook const& hook: m_sideEffectHooks)
470 effects += hook(test.call());
471 test.setSideEffects(move(effects));
472
473 success &= test.call().expectedSideEffects == test.call().actualSideEffects;
474 }
475
476 if (!m_testCaseWantsYulRun && _isYulRun)
477 {
478 m_canEnableYulRun = success;
479 string message = success ?
480 "Test can pass via Yul, but marked with \"compileViaYul: false.\"" :
481 "Test compiles via Yul, but it gives different test results.";
482 AnsiColorized(_stream, _formatted, {BOLD, success ? YELLOW : MAGENTA}) <<
483 _linePrefix << endl <<
484 _linePrefix << message << endl;
485 return TestResult::Failure;
486 }
487
488 // Right now we have sometimes different test results in Yul vs. Ewasm.
489 // The main reason is that Ewasm just returns a failure in some cases.
490 // TODO: If Ewasm support got fully implemented, we could implement this in the same way as above.
491 if (success && !m_testCaseWantsEwasmRun && _isEwasmRun)
492 {
493 // TODO: There is something missing in Ewasm to support other types of revert strings:
494 // for now, we just ignore test-cases that do not use RevertStrings::Default.
495 if (m_revertStrings != RevertStrings::Default)
496 return TestResult::Success;
497
498 m_canEnableEwasmRun = true;
499 AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) <<
500 _linePrefix << endl <<
501 _linePrefix << "Test can pass via Yul (Ewasm), but marked with \"compileToEwasm: false.\"" << endl;
502 return TestResult::Failure;
503 }
504
505 if (!success)
506 {
507 // Ignore failing tests that can't yet get compiled to Ewasm:
508 // if the test run was not successful and enforce compiling to ewasm was set,
509 // but the test case did not want to get run with Ewasm, we just ignore this failure.
510 if (m_enforceCompileToEwasm && !m_testCaseWantsEwasmRun)
511 return TestResult::Success;
512
513 AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
514 for (TestFunctionCall const& test: m_tests)
515 {
516 ErrorReporter errorReporter;
517 _stream << test.format(
518 errorReporter,
519 _linePrefix,
520 TestFunctionCall::RenderMode::ExpectedValuesExpectedGas,
521 _formatted,
522 /* _interactivePrint */ true
523 ) << endl;
524 _stream << errorReporter.format(_linePrefix, _formatted);
525 }
526 _stream << endl;
527 AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
528 for (TestFunctionCall const& test: m_tests)
529 {
530 ErrorReporter errorReporter;
531 _stream << test.format(
532 errorReporter,
533 _linePrefix,
534 m_gasCostFailure ? TestFunctionCall::RenderMode::ExpectedValuesActualGas : TestFunctionCall::RenderMode::ActualValuesExpectedGas,
535 _formatted,
536 /* _interactivePrint */ true
537 ) << endl;
538 _stream << errorReporter.format(_linePrefix, _formatted);
539 }
540 AnsiColorized(_stream, _formatted, {BOLD, RED})
541 << _linePrefix << endl
542 << _linePrefix << "Attention: Updates on the test will apply the detected format displayed." << endl;
543 if (_isYulRun && m_testCaseWantsLegacyRun)
544 {
545 _stream << _linePrefix << endl << _linePrefix;
546 AnsiColorized(_stream, _formatted, {RED_BACKGROUND}) << "Note that the test passed without Yul.";
547 _stream << endl;
548 }
549 else if (!_isYulRun && m_testCaseWantsYulRun)
550 AnsiColorized(_stream, _formatted, {BOLD, YELLOW})
551 << _linePrefix << endl
552 << _linePrefix << "Note that the test also has to pass via Yul." << endl;
553 return TestResult::Failure;
554 }
555
556 return TestResult::Success;
557 }
558
checkGasCostExpectation(TestFunctionCall & io_test,bool _compileViaYul) const559 bool SemanticTest::checkGasCostExpectation(TestFunctionCall& io_test, bool _compileViaYul) const
560 {
561 string setting =
562 (_compileViaYul ? "ir"s : "legacy"s) +
563 (m_optimiserSettings == OptimiserSettings::full() ? "Optimized" : "");
564
565 // We don't check gas if enforce gas cost is not active
566 // or test is run with abi encoder v1 only
567 // or gas used less than threshold for enforcing feature
568 // or the test has used up all available gas (test will fail anyway)
569 // or setting is "ir" and it's not included in expectations
570 // or if the called function is an isoltest builtin e.g. `smokeTest` or `storageEmpty`
571 if (
572 !m_enforceGasCost ||
573 m_gasUsed < m_enforceGasCostMinValue ||
574 m_gasUsed >= InitialGas ||
575 (setting == "ir" && io_test.call().expectations.gasUsed.count(setting) == 0) ||
576 io_test.call().kind == FunctionCall::Kind::Builtin
577 )
578 return true;
579
580 solAssert(!m_runWithABIEncoderV1Only, "");
581
582 io_test.setGasCost(setting, m_gasUsed);
583 return
584 io_test.call().expectations.gasUsed.count(setting) > 0 &&
585 m_gasUsed == io_test.call().expectations.gasUsed.at(setting);
586 }
587
printSource(ostream & _stream,string const & _linePrefix,bool _formatted) const588 void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const
589 {
590 if (m_sources.sources.empty())
591 return;
592
593 bool outputNames = (m_sources.sources.size() - m_sources.externalSources.size() != 1 || !m_sources.sources.begin()->first.empty());
594
595 set<string> externals;
596 for (auto const& [name, path]: m_sources.externalSources)
597 {
598 externals.insert(name);
599 string externalSource;
600 if (name == path)
601 externalSource = name;
602 else
603 externalSource = name + "=" + path.generic_string();
604
605 if (_formatted)
606 _stream << _linePrefix << formatting::CYAN << "==== ExternalSource: " << externalSource << " ===="s << formatting::RESET << endl;
607 else
608 _stream << _linePrefix << "==== ExternalSource: " << externalSource << " ===="s << endl;
609 }
610
611 for (auto const& [name, source]: m_sources.sources)
612 if (externals.find(name) == externals.end())
613 {
614 if (_formatted)
615 {
616 if (source.empty())
617 continue;
618
619 if (outputNames)
620 _stream << _linePrefix << formatting::CYAN << "==== Source: " << name
621 << " ====" << formatting::RESET << endl;
622
623 vector<char const*> sourceFormatting(source.length(), formatting::RESET);
624 _stream << _linePrefix << sourceFormatting.front() << source.front();
625 for (size_t i = 1; i < source.length(); i++)
626 {
627 if (sourceFormatting[i] != sourceFormatting[i - 1])
628 _stream << sourceFormatting[i];
629 if (source[i] != '\n')
630 _stream << source[i];
631 else
632 {
633 _stream << formatting::RESET << endl;
634 if (i + 1 < source.length())
635 _stream << _linePrefix << sourceFormatting[i];
636 }
637 }
638 _stream << formatting::RESET;
639 }
640 else
641 {
642 if (outputNames)
643 _stream << _linePrefix << "==== Source: " + name << " ====" << endl;
644 stringstream stream(source);
645 string line;
646 while (getline(stream, line))
647 _stream << _linePrefix << line << endl;
648 }
649 }
650 }
651
printUpdatedExpectations(ostream & _stream,string const &) const652 void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) const
653 {
654 for (TestFunctionCall const& test: m_tests)
655 _stream << test.format(
656 "",
657 m_gasCostFailure ? TestFunctionCall::RenderMode::ExpectedValuesActualGas : TestFunctionCall::RenderMode::ActualValuesExpectedGas,
658 /* _highlight = */ false
659 ) << endl;
660 }
661
printUpdatedSettings(ostream & _stream,string const & _linePrefix)662 void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePrefix)
663 {
664 auto& settings = m_reader.settings();
665 if (settings.empty() && !m_canEnableYulRun)
666 return;
667
668 _stream << _linePrefix << "// ====" << endl;
669 if (m_canEnableEwasmRun)
670 {
671 soltestAssert(m_canEnableYulRun || m_testCaseWantsYulRun, "");
672 string compileViaYul = m_reader.stringSetting("compileViaYul", "");
673 if (!compileViaYul.empty())
674 _stream << _linePrefix << "// compileViaYul: " << compileViaYul << "\n";
675 _stream << _linePrefix << "// compileToEwasm: also\n";
676 }
677 else if (m_canEnableYulRun)
678 _stream << _linePrefix << "// compileViaYul: also\n";
679
680 for (auto const& [settingName, settingValue]: settings)
681 if (
682 !(settingName == "compileToEwasm" && m_canEnableEwasmRun) &&
683 !(settingName == "compileViaYul" && (m_canEnableYulRun || m_canEnableEwasmRun))
684 )
685 _stream << _linePrefix << "// " << settingName << ": " << settingValue<< endl;
686 }
687
parseExpectations(istream & _stream)688 void SemanticTest::parseExpectations(istream& _stream)
689 {
690 m_tests += TestFileParser{_stream, m_builtins}.parseFunctionCalls(m_lineOffset);
691 }
692
deploy(string const & _contractName,u256 const & _value,bytes const & _arguments,map<string,solidity::test::Address> const & _libraries)693 bool SemanticTest::deploy(
694 string const& _contractName,
695 u256 const& _value,
696 bytes const& _arguments,
697 map<string, solidity::test::Address> const& _libraries
698 )
699 {
700 auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries, m_sources.mainSourceFile);
701 return !output.empty() && m_transactionSuccessful;
702 }
703