/* This file is part of solidity. solidity is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. solidity is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with solidity. If not, see . */ #include #include #include #include using namespace dev; using namespace solidity; using namespace dev::solidity::test; using namespace std; string TestFunctionCall::format(string const& _linePrefix, bool const _renderResult, bool const _highlight) const { using namespace soltest; using Token = soltest::Token; stringstream _stream; bool highlight = !matchesExpectation() && _highlight; auto formatOutput = [&](bool const _singleLine) { string ws = " "; string arrow = formatToken(Token::Arrow); string colon = formatToken(Token::Colon); string comma = formatToken(Token::Comma); string comment = formatToken(Token::Comment); string ether = formatToken(Token::Ether); string newline = formatToken(Token::Newline); string failure = formatToken(Token::Failure); /// 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)) _stream << comma << ws << m_call.value << ws << ether; if (!m_call.arguments.rawBytes().empty()) { string output = formatRawParameters(m_call.arguments.parameters, _linePrefix); _stream << colon << output; } /// Formats comments on the function parameters and the arrow taking /// the display-mode into account. if (_singleLine) { if (!m_call.arguments.comment.empty()) _stream << ws << comment << m_call.arguments.comment << comment; _stream << ws << arrow << ws; } else { _stream << endl << _linePrefix << newline << ws; if (!m_call.arguments.comment.empty()) { _stream << comment << m_call.arguments.comment << comment; _stream << endl << _linePrefix << newline << ws; } _stream << arrow << ws; } /// Format either the expected output or the actual result output string result; if (!_renderResult) { bytes output = m_call.expectations.rawBytes(); bool const isFailure = m_call.expectations.failure; result = isFailure ? failure : formatBytesParameters(output, m_call.expectations.result); } else { bytes output = m_rawBytes; bool const isFailure = m_failure; result = isFailure ? failure : formatBytesParameters(output, m_call.expectations.result); } AnsiColorized(_stream, highlight, {dev::formatting::RED_BACKGROUND}) << result; /// Format comments on expectations taking the display-mode into account. if (_singleLine) { if (!m_call.expectations.comment.empty()) _stream << ws << comment << m_call.expectations.comment << comment; } else { if (!m_call.expectations.comment.empty()) { _stream << endl << _linePrefix << newline << ws; _stream << comment << m_call.expectations.comment << comment; } } }; if (m_call.displayMode == FunctionCall::DisplayMode::SingleLine) formatOutput(true); else formatOutput(false); // _stream << endl; return _stream.str(); } string TestFunctionCall::formatBytesParameters(bytes const& _bytes, ParameterList const& _params) const { stringstream resultStream; if (_bytes.empty()) return {}; auto it = _bytes.begin(); for (auto const& param: _params) { long offset = static_cast(param.abiType.size); auto offsetIter = it + offset; soltestAssert(offsetIter <= _bytes.end(), "Byte range can not be extended past the end of given bytes."); bytes byteRange{it, offsetIter}; switch (param.abiType.type) { case ABIType::UnsignedDec: // Check if the detected type was wrong and if this could // be signed. If an unsigned was detected in the expectations, // but the actual result returned a signed, it would be formatted // incorrectly. soltestAssert(param.abiType.align == ABIType::AlignRight, "Unsigned decimals must be right-aligned."); if (*byteRange.begin() & 0x80) resultStream << u2s(fromBigEndian(byteRange)); else resultStream << fromBigEndian(byteRange); break; case ABIType::SignedDec: soltestAssert(param.abiType.align == ABIType::AlignRight, "Signed decimals must be right-aligned."); if (*byteRange.begin() & 0x80) resultStream << u2s(fromBigEndian(byteRange)); else resultStream << fromBigEndian(byteRange); break; case ABIType::Boolean: { soltestAssert(param.abiType.align == ABIType::AlignRight, "Booleans must be right-aligned."); u256 result = fromBigEndian(byteRange); if (result == 0) resultStream << "false"; else resultStream << "true"; break; } case ABIType::Hex: soltestAssert(param.abiType.align == ABIType::AlignLeft, "Hex numbers must be left-aligned."); byteRange.erase( std::remove(byteRange.begin(), byteRange.end(), 0), byteRange.end() ); resultStream << toHex(byteRange, HexPrefix::Add); break; case ABIType::Failure: break; case ABIType::None: break; } it += offset; if (it != _bytes.end() && !(param.abiType.type == ABIType::None)) resultStream << ", "; } soltestAssert(it == _bytes.end(), "Parameter encoding too short for the given byte range."); return resultStream.str(); } string TestFunctionCall::formatRawParameters(ParameterList const& _params, std::string const& _linePrefix) const { stringstream resultStream; for (auto const& param: _params) { if (param.format.newline) resultStream << endl << _linePrefix << "//"; resultStream << " " << param.rawString; if (¶m != &_params.back()) resultStream << ","; } return resultStream.str(); } void TestFunctionCall::reset() { m_rawBytes = bytes{}; m_failure = true; } bool TestFunctionCall::matchesExpectation() const { return m_failure == m_call.expectations.failure && m_rawBytes == m_call.expectations.rawBytes(); }