diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index cdb4e3625..ceb1db2ba 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -78,15 +78,34 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref for (auto& test: m_tests) test.reset(); + map libraries; + + bool constructed = false; + for (auto& test: m_tests) { - if (&test == &m_tests.front()) - if (test.call().isConstructor) - deploy("", test.call().value, test.call().arguments.rawBytes()); - else - soltestAssert(deploy("", 0, bytes()), "Failed to deploy contract."); + if (constructed) + { + soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call."); + soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call expect for library deployments."); + } + else if (test.call().isLibrary) + { + soltestAssert( + deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful, + "Failed to deploy library " + test.call().signature + ); + libraries[test.call().signature] = m_contractAddress; + continue; + } else - soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call."); + { + if (test.call().isConstructor) + deploy("", test.call().value, test.call().arguments.rawBytes(), libraries); + else + soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract."); + constructed = true; + } if (test.call().isConstructor) { @@ -171,8 +190,8 @@ void SemanticTest::parseExpectations(istream& _stream) std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests)); } -bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments) +bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments, map const& _libraries) { - auto output = compileAndRunWithoutCheck(m_source, _value, _contractName, _arguments); + auto output = compileAndRunWithoutCheck(m_source, _value, _contractName, _arguments, _libraries); return !output.empty() && m_transactionSuccessful; } diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index f7891633d..71e03518b 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -60,7 +60,7 @@ 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); + bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map const& _libraries = {}); private: std::string m_source; diff --git a/test/libsolidity/semanticTests/libraries/stub.sol b/test/libsolidity/semanticTests/libraries/stub.sol new file mode 100644 index 000000000..8bae3df27 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/stub.sol @@ -0,0 +1,13 @@ +library L { + function f(uint256 v) external returns (uint256) { return v*v; } +} +contract C { + function g(uint256 v) external returns (uint256) { + return L.f(v); + } +} +// ---- +// library: L +// g(uint256): 1 -> 1 +// g(uint256): 2 -> 4 +// g(uint256): 4 -> 16 diff --git a/test/libsolidity/semanticTests/libraries/stub_internal.sol b/test/libsolidity/semanticTests/libraries/stub_internal.sol new file mode 100644 index 000000000..075b8dbb2 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/stub_internal.sol @@ -0,0 +1,12 @@ +library L { + function f(uint256 v) internal returns (uint256) { return v*v; } +} +contract C { + function g(uint256 v) external returns (uint256) { + return L.f(v); + } +} +// ---- +// g(uint256): 1 -> 1 +// g(uint256): 2 -> 4 +// g(uint256): 4 -> 16 diff --git a/test/libsolidity/util/SoltestTypes.h b/test/libsolidity/util/SoltestTypes.h index 275b1e657..d945ee022 100644 --- a/test/libsolidity/util/SoltestTypes.h +++ b/test/libsolidity/util/SoltestTypes.h @@ -58,6 +58,7 @@ namespace test K(Boolean, "boolean", 0) \ /* special keywords */ \ K(Left, "left", 0) \ + K(Library, "library", 0) \ K(Right, "right", 0) \ K(Failure, "FAILURE", 0) \ @@ -268,6 +269,8 @@ struct FunctionCall /// Marks this function call as "short-handed", meaning /// no `->` declared. bool omitsArrow = true; + /// Marks a library deployment call. + bool isLibrary = false; }; } diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index f834a532b..77330f51c 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -75,44 +75,56 @@ vector TestFileParser::parseFunctionCalls(siz try { - tie(call.signature, call.useCallWithoutSignature) = parseFunctionSignature(); - if (accept(Token::Comma, true)) - call.value = parseFunctionCallValue(); - if (accept(Token::Colon, true)) - call.arguments = parseFunctionCallArguments(); - - if (accept(Token::Newline, true)) + if (accept(Token::Library, true)) { - call.displayMode = FunctionCall::DisplayMode::MultiLine; - m_lineNumber++; - } - - call.arguments.comment = parseComment(); - - if (accept(Token::Newline, true)) - { - call.displayMode = FunctionCall::DisplayMode::MultiLine; - m_lineNumber++; - } - - if (accept(Token::Arrow, true)) - { - call.omitsArrow = false; - call.expectations = parseFunctionCallExpectations(); - if (accept(Token::Newline, true)) - m_lineNumber++; + expect(Token::Colon); + call.signature = m_scanner.currentLiteral(); + expect(Token::Identifier); + call.isLibrary = true; + call.expectations.failure = false; } else { - call.expectations.failure = false; - call.displayMode = FunctionCall::DisplayMode::SingleLine; + tie(call.signature, call.useCallWithoutSignature) = parseFunctionSignature(); + if (accept(Token::Comma, true)) + call.value = parseFunctionCallValue(); + if (accept(Token::Colon, true)) + call.arguments = parseFunctionCallArguments(); + + if (accept(Token::Newline, true)) + { + call.displayMode = FunctionCall::DisplayMode::MultiLine; + m_lineNumber++; + } + + call.arguments.comment = parseComment(); + + if (accept(Token::Newline, true)) + { + call.displayMode = FunctionCall::DisplayMode::MultiLine; + m_lineNumber++; + } + + if (accept(Token::Arrow, true)) + { + call.omitsArrow = false; + call.expectations = parseFunctionCallExpectations(); + if (accept(Token::Newline, true)) + m_lineNumber++; + } + else + { + call.expectations.failure = false; + call.displayMode = FunctionCall::DisplayMode::SingleLine; + } + + call.expectations.comment = parseComment(); + + if (call.signature == "constructor()") + call.isConstructor = true; + } - call.expectations.comment = parseComment(); - - if (call.signature == "constructor()") - call.isConstructor = true; - calls.emplace_back(std::move(call)); } catch (Error const& _e) @@ -456,6 +468,7 @@ void TestFileParser::Scanner::scanNextToken() if (_literal == "false") return TokenDesc{Token::Boolean, _literal}; if (_literal == "ether") return TokenDesc{Token::Ether, _literal}; if (_literal == "left") return TokenDesc{Token::Left, _literal}; + if (_literal == "library") return TokenDesc{Token::Library, _literal}; if (_literal == "right") return TokenDesc{Token::Right, _literal}; if (_literal == "hex") return TokenDesc{Token::Hex, _literal}; if (_literal == "FAILURE") return TokenDesc{Token::Failure, _literal}; diff --git a/test/libsolidity/util/TestFileParserTests.cpp b/test/libsolidity/util/TestFileParserTests.cpp index 3ef9b6b81..2323609b5 100644 --- a/test/libsolidity/util/TestFileParserTests.cpp +++ b/test/libsolidity/util/TestFileParserTests.cpp @@ -57,7 +57,9 @@ void testFunctionCall( u256 _value = 0, string _argumentComment = "", string _expectationComment = "", - vector _rawArguments = vector{} + vector _rawArguments = vector{}, + bool _isConstructor = false, + bool _isLibrary = false ) { BOOST_REQUIRE_EQUAL(_call.expectations.failure, _failure); @@ -79,6 +81,9 @@ void testFunctionCall( ++index; } } + + BOOST_REQUIRE_EQUAL(_call.isConstructor, _isConstructor); + BOOST_REQUIRE_EQUAL(_call.isLibrary, _isLibrary); } BOOST_AUTO_TEST_SUITE(TestFileParserTest) @@ -883,6 +888,51 @@ BOOST_AUTO_TEST_CASE(call_unexpected_character) BOOST_REQUIRE_THROW(parse(source), langutil::Error); } +BOOST_AUTO_TEST_CASE(constructor) +{ + char const* source = R"( + // constructor() + )"; + auto const calls = parse(source); + BOOST_REQUIRE_EQUAL(calls.size(), 1); + testFunctionCall( + calls.at(0), + Mode::SingleLine, + "constructor()", + false, + {}, + {}, + 0, + "", + "", + {}, + true + ); +} + +BOOST_AUTO_TEST_CASE(library) +{ + char const* source = R"( + // library: L + )"; + auto const calls = parse(source); + BOOST_REQUIRE_EQUAL(calls.size(), 1); + testFunctionCall( + calls.at(0), + Mode::SingleLine, + "L", + false, + {}, + {}, + 0, + "", + "", + {}, + false, + true + ); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index 1e52c1932..5fcfef08b 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -55,6 +55,12 @@ string TestFunctionCall::format( string newline = formatToken(Token::Newline); string failure = formatToken(Token::Failure); + if (m_call.isLibrary) + { + stream << _linePrefix << newline << ws << "library:" << ws << m_call.signature; + return; + } + /// Formats the function signature. This is the same independent from the display-mode. stream << _linePrefix << newline << ws << m_call.signature; if (m_call.value > u256(0))