From 0397266351e90531fe67f69d69253265d55d321a Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Thu, 18 Jun 2020 12:31:55 +0200 Subject: [PATCH] Implement multi source semantic tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: chriseth Co-authored-by: Kamil ƚliwak --- test/CommonSyntaxTest.cpp | 15 +++--- test/ExecutionFramework.h | 19 ++++--- test/TestCaseReader.cpp | 11 ++-- test/TestCaseReader.h | 22 +++++--- test/libsolidity/ABIEncoderTests.cpp | 2 +- test/libsolidity/SemanticTest.cpp | 53 +++++++++++++++---- test/libsolidity/SemanticTest.h | 3 +- test/libsolidity/SolidityEndToEndTest.cpp | 4 +- .../SolidityExecutionFramework.cpp | 43 +++++++++++---- test/libsolidity/SolidityExecutionFramework.h | 21 +++++--- .../semanticTests/multiSource/import.sol | 12 +++++ test/tools/isoltest.cpp | 4 -- 12 files changed, 146 insertions(+), 63 deletions(-) create mode 100644 test/libsolidity/semanticTests/multiSource/import.sol diff --git a/test/CommonSyntaxTest.cpp b/test/CommonSyntaxTest.cpp index 98a74cb86..ff99d6e24 100644 --- a/test/CommonSyntaxTest.cpp +++ b/test/CommonSyntaxTest.cpp @@ -58,10 +58,10 @@ int parseUnsignedInteger(string::iterator& _it, string::iterator _end) CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): EVMVersionRestrictedTestCase(_filename), + m_sources(m_reader.sources().sources), + m_expectations(parseExpectations(m_reader.stream())), m_evmVersion(_evmVersion) { - m_sources = m_reader.sources(); - m_expectations = parseExpectations(m_reader.stream()); } TestCase::TestResult CommonSyntaxTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) @@ -94,12 +94,10 @@ void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, if (m_sources.empty()) return; - bool outputSourceNames = true; - if (m_sources.size() == 1 && m_sources.begin()->first.empty()) - outputSourceNames = false; + bool outputSourceNames = (m_sources.size() != 1 || !m_sources.begin()->first.empty()); - if (_formatted) - for (auto const& [name, source]: m_sources) + for (auto const& [name, source]: m_sources) + if (_formatted) { if (source.empty()) continue; @@ -139,8 +137,7 @@ void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, } _stream << formatting::RESET; } - else - for (auto const& [name, source]: m_sources) + else { if (outputSourceNames) _stream << _linePrefix << "==== Source: " + name << " ====" << endl; diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index e55a12aa6..72a07ce3e 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -61,22 +61,28 @@ public: virtual ~ExecutionFramework() = default; virtual bytes const& compileAndRunWithoutCheck( - std::string const& _sourceCode, + std::map const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "", - bytes const& _arguments = bytes(), - std::map const& _libraryAddresses = std::map() + bytes const& _arguments = {}, + std::map const& _libraryAddresses = {} ) = 0; bytes const& compileAndRun( std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "", - bytes const& _arguments = bytes(), - std::map const& _libraryAddresses = std::map() + bytes const& _arguments = {}, + std::map const& _libraryAddresses = {} ) { - compileAndRunWithoutCheck(_sourceCode, _value, _contractName, _arguments, _libraryAddresses); + compileAndRunWithoutCheck( + {{"", _sourceCode}}, + _value, + _contractName, + _arguments, + _libraryAddresses + ); BOOST_REQUIRE(m_transactionSuccessful); BOOST_REQUIRE(!m_output.empty()); return m_output; @@ -293,4 +299,3 @@ protected: } // end namespaces - diff --git a/test/TestCaseReader.cpp b/test/TestCaseReader.cpp index 0f49a6401..2f90678ed 100644 --- a/test/TestCaseReader.cpp +++ b/test/TestCaseReader.cpp @@ -37,11 +37,11 @@ TestCaseReader::TestCaseReader(string const& _filename): m_unreadSettings = m_settings; } -string const& TestCaseReader::source() +string const& TestCaseReader::source() const { - if (m_sources.size() != 1) + if (m_sources.sources.size() != 1) BOOST_THROW_EXCEPTION(runtime_error("Expected single source definition, but got multiple sources.")); - return m_sources.begin()->second; + return m_sources.sources.at(m_sources.mainSourceFile); } string TestCaseReader::simpleExpectations() @@ -93,7 +93,7 @@ void TestCaseReader::ensureAllSettingsRead() const ); } -pair, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(istream& _stream) +pair TestCaseReader::parseSourcesAndSettingsWithLineNumber(istream& _stream) { map sources; string currentSourceName; @@ -145,8 +145,9 @@ pair, size_t> TestCaseReader::parseSourcesAndSettingsWithLin else throw runtime_error(string("Expected \"//\" or \"// ---\" to terminate settings and source.")); } + // Register the last source as the main one sources[currentSourceName] = currentSource; - return { sources, lineNumber }; + return {{move(sources), move(currentSourceName)}, lineNumber}; } string TestCaseReader::parseSimpleExpectations(istream& _file) diff --git a/test/TestCaseReader.h b/test/TestCaseReader.h index 5ab226826..9f94f898d 100644 --- a/test/TestCaseReader.h +++ b/test/TestCaseReader.h @@ -23,6 +23,16 @@ namespace solidity::frontend::test { + +/** + * A map for registering source names that also contains the main source name in a test case. + */ +struct SourceMap +{ + std::map sources; + std::string mainSourceFile; +}; + /** * A reader for test case data file, which parses source, settings and (optionally) simple expectations. */ @@ -32,10 +42,10 @@ public: TestCaseReader() = default; explicit TestCaseReader(std::string const& _filename); - std::map const& sources() { return m_sources; } - std::string const& source(); - std::size_t lineNumber() { return m_lineNumber; } - std::map const& settings() { return m_settings; } + SourceMap const& sources() const { return m_sources; } + std::string const& source() const; + std::size_t lineNumber() const { return m_lineNumber; } + std::map const& settings() const { return m_settings; } std::ifstream& stream() { return m_file; } std::string simpleExpectations(); @@ -47,11 +57,11 @@ public: void ensureAllSettingsRead() const; private: - std::pair, std::size_t> parseSourcesAndSettingsWithLineNumber(std::istream& _file); + std::pair parseSourcesAndSettingsWithLineNumber(std::istream& _file); static std::string parseSimpleExpectations(std::istream& _file); std::ifstream m_file; - std::map m_sources; + SourceMap m_sources; std::size_t m_lineNumber = 0; std::map m_settings; std::map m_unreadSettings; ///< tracks which settings are left unread diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp index 2e50fe743..777f991a3 100644 --- a/test/libsolidity/ABIEncoderTests.cpp +++ b/test/libsolidity/ABIEncoderTests.cpp @@ -777,7 +777,7 @@ BOOST_AUTO_TEST_CASE(struct_in_constructor_data_short) NEW_ENCODER( BOOST_CHECK( - compileAndRunWithoutCheck(sourceCode, 0, "C", encodeArgs(0x20, 0x60, 0x03, 0x80, 0x00)).empty() + compileAndRunWithoutCheck({{"", sourceCode}}, 0, "C", encodeArgs(0x20, 0x60, 0x03, 0x80, 0x00)).empty() ); ) } diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 39744fa59..e3b40f1e4 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -42,11 +42,10 @@ namespace fs = boost::filesystem; SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion, bool enforceViaYul): SolidityExecutionFramework(_evmVersion), EVMVersionRestrictedTestCase(_filename), + m_sources(m_reader.sources()), + m_lineOffset(m_reader.lineNumber()), m_enforceViaYul(enforceViaYul) { - m_source = m_reader.source(); - m_lineOffset = m_reader.lineNumber(); - string choice = m_reader.stringSetting("compileViaYul", "false"); if (choice == "also") { @@ -239,12 +238,48 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref return TestResult::Success; } -void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool) const +void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const { - stringstream stream(m_source); - string line; - while (getline(stream, line)) - _stream << _linePrefix << line << endl; + if (m_sources.sources.empty()) + return; + + bool outputNames = (m_sources.sources.size() != 1 || !m_sources.sources.begin()->first.empty()); + + for (auto const& [name, source]: m_sources.sources) + if (_formatted) + { + if (source.empty()) + continue; + + if (outputNames) + _stream << _linePrefix << formatting::CYAN << "==== Source: " << name << " ====" << formatting::RESET << endl; + vector sourceFormatting(source.length(), formatting::RESET); + + _stream << _linePrefix << sourceFormatting.front() << source.front(); + for (size_t i = 1; i < source.length(); i++) + { + if (sourceFormatting[i] != sourceFormatting[i - 1]) + _stream << sourceFormatting[i]; + if (source[i] != '\n') + _stream << source[i]; + else + { + _stream << formatting::RESET << endl; + if (i + 1 < source.length()) + _stream << _linePrefix << sourceFormatting[i]; + } + } + _stream << formatting::RESET; + } + else + { + if (outputNames) + _stream << _linePrefix << "==== Source: " + name << " ====" << endl; + stringstream stream(source); + string line; + while (getline(stream, line)) + _stream << _linePrefix << line << endl; + } } void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) const @@ -276,6 +311,6 @@ void SemanticTest::parseExpectations(istream& _stream) bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments, map const& _libraries) { - auto output = compileAndRunWithoutCheck(m_source, _value, _contractName, _arguments, _libraries); + auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries); return !output.empty() && m_transactionSuccessful; } diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index 870d61d56..646e7e00b 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -58,9 +58,8 @@ public: /// Compiles and deploys currently held source. /// Returns true if deployment was successful, false otherwise. bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map const& _libraries = {}); - private: - std::string m_source; + SourceMap m_sources; std::size_t m_lineOffset; std::vector m_tests; bool m_runWithYul = false; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 83e7ef95d..d449cd27e 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1746,7 +1746,7 @@ BOOST_AUTO_TEST_CASE(internal_constructor) constructor() internal {} } )"; - BOOST_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "C").empty()); + BOOST_CHECK(compileAndRunWithoutCheck({{"", sourceCode}}, 0, "C").empty()); } BOOST_AUTO_TEST_CASE(default_fallback_throws) @@ -3641,7 +3641,7 @@ BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund) } } )"; - ABI_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "A"), encodeArgs()); + ABI_CHECK(compileAndRunWithoutCheck({{"", sourceCode}}, 0, "A"), encodeArgs()); BOOST_CHECK(!m_transactionSuccessful); } diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index 5e4ebe088..29b3b8b43 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -31,22 +31,18 @@ using namespace solidity::frontend; using namespace solidity::frontend::test; using namespace std; -bytes SolidityExecutionFramework::compileContract( - string const& _sourceCode, +bytes SolidityExecutionFramework::multiSourceCompileContract( + map const& _sourceCode, string const& _contractName, map const& _libraryAddresses ) { - // Silence compiler version warning - std::string sourceCode = "pragma solidity >=0.0;\n"; - if ( - solidity::test::CommonOptions::get().useABIEncoderV2 && - _sourceCode.find("pragma experimental ABIEncoderV2;") == std::string::npos - ) - sourceCode += "pragma experimental ABIEncoderV2;\n"; - sourceCode += _sourceCode; + map sourcesWithPreamble = _sourceCode; + for (auto& entry: sourcesWithPreamble) + entry.second = addPreamble(entry.second); + m_compiler.reset(); - m_compiler.setSources({{"", sourceCode}}); + m_compiler.setSources(sourcesWithPreamble); m_compiler.setLibraries(_libraryAddresses); m_compiler.setRevertStringBehaviour(m_revertStrings); m_compiler.setEVMVersion(m_evmVersion); @@ -85,3 +81,28 @@ bytes SolidityExecutionFramework::compileContract( cout << "metadata: " << m_compiler.metadata(contractName) << endl; return obj.bytecode; } + +bytes SolidityExecutionFramework::compileContract( + string const& _sourceCode, + string const& _contractName, + map const& _libraryAddresses +) +{ + return multiSourceCompileContract( + {{"", _sourceCode}}, + _contractName, + _libraryAddresses + ); +} + +string SolidityExecutionFramework::addPreamble(string const& _sourceCode) +{ + // Silence compiler version warning + string preamble = "pragma solidity >=0.0;\n"; + if ( + solidity::test::CommonOptions::get().useABIEncoderV2 && + _sourceCode.find("pragma experimental ABIEncoderV2;") == string::npos + ) + preamble += "pragma experimental ABIEncoderV2;\n"; + return preamble + _sourceCode; +} diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 2410195fa..5be310c87 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -47,14 +47,14 @@ public: {} bytes const& compileAndRunWithoutCheck( - std::string const& _sourceCode, + std::map const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "", - bytes const& _arguments = bytes(), - std::map const& _libraryAddresses = std::map() + bytes const& _arguments = {}, + std::map const& _libraryAddresses = {} ) override { - bytes bytecode = compileContract(_sourceCode, _contractName, _libraryAddresses); + bytes bytecode = multiSourceCompileContract(_sourceCode, _contractName, _libraryAddresses); sendMessage(bytecode + _arguments, true, _value); return m_output; } @@ -62,16 +62,23 @@ public: bytes compileContract( std::string const& _sourceCode, std::string const& _contractName = "", - std::map const& _libraryAddresses = std::map() + std::map const& _libraryAddresses = {} ); + bytes multiSourceCompileContract( + std::map const& _sources, + std::string const& _contractName = "", + std::map const& _libraryAddresses = {} + ); + + /// Returns @param _sourceCode prefixed with the version pragma and the ABIEncoderV2 pragma, + /// the latter only if it is required. + static std::string addPreamble(std::string const& _sourceCode); protected: solidity::frontend::CompilerStack m_compiler; bool m_compileViaYul = false; bool m_showMetadata = false; RevertStrings m_revertStrings = RevertStrings::Default; - }; } // end namespaces - diff --git a/test/libsolidity/semanticTests/multiSource/import.sol b/test/libsolidity/semanticTests/multiSource/import.sol new file mode 100644 index 000000000..2a0f43d53 --- /dev/null +++ b/test/libsolidity/semanticTests/multiSource/import.sol @@ -0,0 +1,12 @@ +==== Source: A ==== +contract A { + function g(uint256 x) public view returns(uint256) { return x + 1; } +} +==== Source: B ==== +import "A"; +contract B is A { + function f(uint256 x) public view returns(uint256) { return x; } +} +// ---- +// f(uint256): 1337 -> 1337 +// g(uint256): 1337 -> 1338 diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 846a54415..07802edf1 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -21,17 +21,13 @@ #include #include #include -#include #include #include -#include #include #include -#include #include -#include #include #include #include