diff --git a/test/libsolidity/semanticTests/smoke_test.sol b/test/libsolidity/semanticTests/smoke_test.sol index 23ad109a0..e4b19bc87 100644 --- a/test/libsolidity/semanticTests/smoke_test.sol +++ b/test/libsolidity/semanticTests/smoke_test.sol @@ -18,9 +18,12 @@ contract C { function balance() payable public returns (uint256) { return address(this).balance; } - function e(uint a) public { + function d(uint a) public { state = a; } + function e() public { + revert("Transaction failed."); + } function f() payable public returns (uint) { return 2; } @@ -88,12 +91,15 @@ contract C { return (["any", "any"], ["any", "any", "any"]); } } +// ==== +// EVMVersion: >homestead // ---- // constructor(), 2 ether: 3 -> // state() -> 3 // balance() -> 2 // _() -> FAILURE -// e(uint256): 4 +// d(uint256): 4 +// e() -> FAILURE, hex"08c379a0", 0x20, 19, "Transaction failed." // f() -> 2 // f(uint256): 3 -> 3, 3 // f(), 1 ether -> 2 diff --git a/test/libsolidity/util/BytesUtils.cpp b/test/libsolidity/util/BytesUtils.cpp index 82d70dcb9..f9b7d06f7 100644 --- a/test/libsolidity/util/BytesUtils.cpp +++ b/test/libsolidity/util/BytesUtils.cpp @@ -202,21 +202,28 @@ string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff) return os.str(); } -string BytesUtils::formatRawBytes(bytes const& _bytes) +string BytesUtils::formatRawBytes(bytes const& _bytes, string _linePrefix, bool _withSignature) { if (_bytes.empty()) - return "[]"; + return _linePrefix + "[]"; stringstream os; auto it = _bytes.begin(); - for (size_t i = 0; i < _bytes.size(); i += 32) + + if (_withSignature) { - bytes byteRange{it, it + 32}; + os << _linePrefix << bytes{it, it + 4} << endl; + it += 4; + } - os << " " << byteRange; + bytes tail{it, _bytes.end()}; + it = tail.begin(); + for (size_t i = 0; i < tail.size(); i += 32) + { + os << _linePrefix << bytes{it, it + 32}; it += 32; - if (it != _bytes.end()) + if (it != tail.end()) os << endl; } @@ -287,11 +294,12 @@ string BytesUtils::formatBytesRange( else os << parameter.rawString; - - it += static_cast(parameter.abiType.size); if (¶meter != &_parameters.back()) os << ", "; + + it += static_cast(parameter.abiType.size); } + return os.str(); } diff --git a/test/libsolidity/util/BytesUtils.h b/test/libsolidity/util/BytesUtils.h index 93ba06cd6..44f9cdd4b 100644 --- a/test/libsolidity/util/BytesUtils.h +++ b/test/libsolidity/util/BytesUtils.h @@ -101,10 +101,14 @@ public: return formatString(_bytes, _bytes.size()); } - /// Returns a string representation of given _bytes. Adds a newline - /// every 32 bytes to increase readability. /// Used to print returned bytes from function calls to the commandline. - static std::string formatRawBytes(bytes const& _bytes); + /// Returns a string representation of given _bytes in ranges of 32 bytes. + /// If _withSignature is true, the first 4 bytes will be formatted separately. + static std::string formatRawBytes( + bytes const& _bytes, + std::string _linePrefix = "", + bool _withSignature = false + ); /// Formats given _bytes with type information passed in _abiType. static std::string formatBytes(bytes const& _bytes, ABIType const& _abiType); diff --git a/test/libsolidity/util/TestFileParserTests.cpp b/test/libsolidity/util/TestFileParserTests.cpp index 72ac9210d..3ef9b6b81 100644 --- a/test/libsolidity/util/TestFileParserTests.cpp +++ b/test/libsolidity/util/TestFileParserTests.cpp @@ -219,6 +219,23 @@ BOOST_AUTO_TEST_CASE(non_existent_call_revert) testFunctionCall(calls.at(0), Mode::MultiLine, "i_am_not_there()", true); } +BOOST_AUTO_TEST_CASE(call_revert_message) +{ + char const* source = R"( + // f() -> FAILURE, hex"08c379a0", 0x20, 6, "Revert" + )"; + auto const calls = parse(source); + BOOST_REQUIRE_EQUAL(calls.size(), 1); + testFunctionCall( + calls.at(0), + Mode::SingleLine, + "f()", + true, + fmt::encodeArgs(), + fromHex("08c379a0") + fmt::encodeDyn(string{"Revert"}) + ); +} + BOOST_AUTO_TEST_CASE(call_expectations_empty_single_line) { char const* source = R"( diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index 272950782..77fa45216 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -101,7 +101,7 @@ string TestFunctionCall::format( { bool const isFailure = m_call.expectations.failure; result = isFailure ? - failure : + formatFailure(_errorReporter, m_call, m_rawBytes, _renderResult, highlight) : formatRawParameters(m_call.expectations.result); if (!result.empty()) AnsiColorized(stream, highlight, {dev::formatting::RED_BACKGROUND}) << ws << result; @@ -111,7 +111,7 @@ string TestFunctionCall::format( bytes output = m_rawBytes; bool const isFailure = m_failure; result = isFailure ? - failure : + formatFailure(_errorReporter, m_call, output, _renderResult, highlight) : matchesExpectation() ? formatRawParameters(m_call.expectations.result) : formatBytesParameters( @@ -122,6 +122,14 @@ string TestFunctionCall::format( highlight ); + if (!matchesExpectation()) + { + _errorReporter.warning( + "The call to \"" + m_call.signature + "\" returned \n" + + BytesUtils::formatRawBytes(output, _linePrefix, isFailure && !output.empty()) + ); + } + if (isFailure) AnsiColorized(stream, highlight, {dev::formatting::RED_BACKGROUND}) << ws << result; else @@ -155,49 +163,97 @@ string TestFunctionCall::formatBytesParameters( bytes const& _bytes, string const& _signature, dev::solidity::test::ParameterList const& _parameters, - bool _highlight + bool _highlight, + bool _failure ) const { using ParameterList = dev::solidity::test::ParameterList; stringstream os; + if (_bytes.empty()) return {}; - _errorReporter.warning("The call to \"" + _signature + "\" returned \n" + BytesUtils::formatRawBytes(_bytes)); - - boost::optional abiParams = ContractABIUtils::parametersFromJsonOutputs( - _errorReporter, - m_contractABI, - _signature - ); - - if (abiParams) + if (_failure) { - boost::optional preferredParams = ContractABIUtils::preferredParameters( - _errorReporter, - _parameters, - abiParams.get(), - _bytes - ); + ParameterList defaultParameters; - if (preferredParams) - { - ContractABIUtils::overwriteParameters(_errorReporter, preferredParams.get(), abiParams.get()); - os << BytesUtils::formatBytesRange(_bytes, preferredParams.get(), _highlight); - } + defaultParameters.push_back(Parameter{bytes(), "", ABIType{ABIType::HexString, ABIType::AlignNone, 4}, FormatInfo{}}); + defaultParameters.push_back(Parameter{bytes(), "", ABIType{ABIType::Hex}, FormatInfo{}}); + defaultParameters.push_back(Parameter{bytes(), "", ABIType{ABIType::UnsignedDec}, FormatInfo{}}); + defaultParameters.push_back(Parameter{bytes(), "", ABIType{ABIType::String}, FormatInfo{}}); + + os << BytesUtils::formatBytesRange(_bytes, defaultParameters, _highlight); + + return os.str(); } else { - ParameterList defaultParameters; - fill_n( - back_inserter(defaultParameters), - ceil(_bytes.size() / 32), - Parameter{bytes(), "", ABIType{ABIType::Hex}, FormatInfo{}} + boost::optional abiParams = ContractABIUtils::parametersFromJsonOutputs( + _errorReporter, + m_contractABI, + _signature ); - ContractABIUtils::overwriteParameters(_errorReporter, defaultParameters, _parameters); - os << BytesUtils::formatBytesRange(_bytes, defaultParameters, _highlight); + + if (abiParams) + { + boost::optional preferredParams = ContractABIUtils::preferredParameters( + _errorReporter, + _parameters, + abiParams.get(), + _bytes + ); + + if (preferredParams) + { + ContractABIUtils::overwriteParameters(_errorReporter, preferredParams.get(), abiParams.get()); + os << BytesUtils::formatBytesRange(_bytes, preferredParams.get(), _highlight); + } + } + else + { + ParameterList defaultParameters; + fill_n( + back_inserter(defaultParameters), + ceil(_bytes.size() / 32), + Parameter{bytes(), "", ABIType{ABIType::UnsignedDec}, FormatInfo{}} + ); + ContractABIUtils::overwriteParameters(_errorReporter, defaultParameters, _parameters); + os << BytesUtils::formatBytesRange(_bytes, defaultParameters, _highlight); + } + return os.str(); } +} + +string TestFunctionCall::formatFailure( + ErrorReporter& _errorReporter, + dev::solidity::test::FunctionCall const& _call, + bytes const& _output, + bool _renderResult, + bool _highlight +) const +{ + using Token = soltest::Token; + + stringstream os; + + os << formatToken(Token::Failure); + + if (!_output.empty()) + os << ", "; + + if (_renderResult) + os << formatBytesParameters( + _errorReporter, + _output, + _call.signature, + _call.expectations.result, + _highlight, + true + ); + else + os << formatRawParameters(_call.expectations.result); + return os.str(); } diff --git a/test/libsolidity/util/TestFunctionCall.h b/test/libsolidity/util/TestFunctionCall.h index 9a49777fd..8edb2afc6 100644 --- a/test/libsolidity/util/TestFunctionCall.h +++ b/test/libsolidity/util/TestFunctionCall.h @@ -95,7 +95,8 @@ private: bytes const& _bytes, std::string const& _signature, ParameterList const& _params, - bool highlight = false + bool highlight = false, + bool failure = false ) const; /// Formats a given _bytes applying the _abiType. @@ -104,6 +105,15 @@ private: ABIType const& _abiType ) const; + /// Formats a FAILURE plus additional parameters, if e.g. a revert message was returned. + std::string formatFailure( + ErrorReporter& _errorReporter, + FunctionCall const& _call, + bytes const& _output, + bool _renderResult, + bool _highlight + ) const; + /// Formats the given parameters using their raw string representation. std::string formatRawParameters( ParameterList const& _params,