mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[isoltest] Support FAILURE with reason.
This commit is contained in:
parent
fd7215dad0
commit
30483acc42
@ -18,9 +18,12 @@ contract C {
|
|||||||
function balance() payable public returns (uint256) {
|
function balance() payable public returns (uint256) {
|
||||||
return address(this).balance;
|
return address(this).balance;
|
||||||
}
|
}
|
||||||
function e(uint a) public {
|
function d(uint a) public {
|
||||||
state = a;
|
state = a;
|
||||||
}
|
}
|
||||||
|
function e() public {
|
||||||
|
revert("Transaction failed.");
|
||||||
|
}
|
||||||
function f() payable public returns (uint) {
|
function f() payable public returns (uint) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
@ -88,12 +91,15 @@ contract C {
|
|||||||
return (["any", "any"], ["any", "any", "any"]);
|
return (["any", "any"], ["any", "any", "any"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// EVMVersion: >homestead
|
||||||
// ----
|
// ----
|
||||||
// constructor(), 2 ether: 3 ->
|
// constructor(), 2 ether: 3 ->
|
||||||
// state() -> 3
|
// state() -> 3
|
||||||
// balance() -> 2
|
// balance() -> 2
|
||||||
// _() -> FAILURE
|
// _() -> FAILURE
|
||||||
// e(uint256): 4
|
// d(uint256): 4
|
||||||
|
// e() -> FAILURE, hex"08c379a0", 0x20, 19, "Transaction failed."
|
||||||
// f() -> 2
|
// f() -> 2
|
||||||
// f(uint256): 3 -> 3, 3
|
// f(uint256): 3 -> 3, 3
|
||||||
// f(), 1 ether -> 2
|
// f(), 1 ether -> 2
|
||||||
|
@ -202,21 +202,28 @@ string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff)
|
|||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
string BytesUtils::formatRawBytes(bytes const& _bytes)
|
string BytesUtils::formatRawBytes(bytes const& _bytes, string _linePrefix, bool _withSignature)
|
||||||
{
|
{
|
||||||
if (_bytes.empty())
|
if (_bytes.empty())
|
||||||
return "[]";
|
return _linePrefix + "[]";
|
||||||
|
|
||||||
stringstream os;
|
stringstream os;
|
||||||
auto it = _bytes.begin();
|
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;
|
it += 32;
|
||||||
if (it != _bytes.end())
|
if (it != tail.end())
|
||||||
os << endl;
|
os << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,11 +294,12 @@ string BytesUtils::formatBytesRange(
|
|||||||
else
|
else
|
||||||
os << parameter.rawString;
|
os << parameter.rawString;
|
||||||
|
|
||||||
|
|
||||||
it += static_cast<long>(parameter.abiType.size);
|
|
||||||
if (¶meter != &_parameters.back())
|
if (¶meter != &_parameters.back())
|
||||||
os << ", ";
|
os << ", ";
|
||||||
|
|
||||||
|
it += static_cast<long>(parameter.abiType.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,10 +101,14 @@ public:
|
|||||||
return formatString(_bytes, _bytes.size());
|
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.
|
/// 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.
|
/// Formats given _bytes with type information passed in _abiType.
|
||||||
static std::string formatBytes(bytes const& _bytes, ABIType const& _abiType);
|
static std::string formatBytes(bytes const& _bytes, ABIType const& _abiType);
|
||||||
|
@ -219,6 +219,23 @@ BOOST_AUTO_TEST_CASE(non_existent_call_revert)
|
|||||||
testFunctionCall(calls.at(0), Mode::MultiLine, "i_am_not_there()", true);
|
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)
|
BOOST_AUTO_TEST_CASE(call_expectations_empty_single_line)
|
||||||
{
|
{
|
||||||
char const* source = R"(
|
char const* source = R"(
|
||||||
|
@ -101,7 +101,7 @@ string TestFunctionCall::format(
|
|||||||
{
|
{
|
||||||
bool const isFailure = m_call.expectations.failure;
|
bool const isFailure = m_call.expectations.failure;
|
||||||
result = isFailure ?
|
result = isFailure ?
|
||||||
failure :
|
formatFailure(_errorReporter, m_call, m_rawBytes, _renderResult, highlight) :
|
||||||
formatRawParameters(m_call.expectations.result);
|
formatRawParameters(m_call.expectations.result);
|
||||||
if (!result.empty())
|
if (!result.empty())
|
||||||
AnsiColorized(stream, highlight, {dev::formatting::RED_BACKGROUND}) << ws << result;
|
AnsiColorized(stream, highlight, {dev::formatting::RED_BACKGROUND}) << ws << result;
|
||||||
@ -111,7 +111,7 @@ string TestFunctionCall::format(
|
|||||||
bytes output = m_rawBytes;
|
bytes output = m_rawBytes;
|
||||||
bool const isFailure = m_failure;
|
bool const isFailure = m_failure;
|
||||||
result = isFailure ?
|
result = isFailure ?
|
||||||
failure :
|
formatFailure(_errorReporter, m_call, output, _renderResult, highlight) :
|
||||||
matchesExpectation() ?
|
matchesExpectation() ?
|
||||||
formatRawParameters(m_call.expectations.result) :
|
formatRawParameters(m_call.expectations.result) :
|
||||||
formatBytesParameters(
|
formatBytesParameters(
|
||||||
@ -122,6 +122,14 @@ string TestFunctionCall::format(
|
|||||||
highlight
|
highlight
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!matchesExpectation())
|
||||||
|
{
|
||||||
|
_errorReporter.warning(
|
||||||
|
"The call to \"" + m_call.signature + "\" returned \n" +
|
||||||
|
BytesUtils::formatRawBytes(output, _linePrefix, isFailure && !output.empty())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isFailure)
|
if (isFailure)
|
||||||
AnsiColorized(stream, highlight, {dev::formatting::RED_BACKGROUND}) << ws << result;
|
AnsiColorized(stream, highlight, {dev::formatting::RED_BACKGROUND}) << ws << result;
|
||||||
else
|
else
|
||||||
@ -155,49 +163,97 @@ string TestFunctionCall::formatBytesParameters(
|
|||||||
bytes const& _bytes,
|
bytes const& _bytes,
|
||||||
string const& _signature,
|
string const& _signature,
|
||||||
dev::solidity::test::ParameterList const& _parameters,
|
dev::solidity::test::ParameterList const& _parameters,
|
||||||
bool _highlight
|
bool _highlight,
|
||||||
|
bool _failure
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
using ParameterList = dev::solidity::test::ParameterList;
|
using ParameterList = dev::solidity::test::ParameterList;
|
||||||
|
|
||||||
stringstream os;
|
stringstream os;
|
||||||
|
|
||||||
if (_bytes.empty())
|
if (_bytes.empty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
_errorReporter.warning("The call to \"" + _signature + "\" returned \n" + BytesUtils::formatRawBytes(_bytes));
|
if (_failure)
|
||||||
|
|
||||||
boost::optional<ParameterList> abiParams = ContractABIUtils::parametersFromJsonOutputs(
|
|
||||||
_errorReporter,
|
|
||||||
m_contractABI,
|
|
||||||
_signature
|
|
||||||
);
|
|
||||||
|
|
||||||
if (abiParams)
|
|
||||||
{
|
{
|
||||||
boost::optional<ParameterList> preferredParams = ContractABIUtils::preferredParameters(
|
ParameterList defaultParameters;
|
||||||
_errorReporter,
|
|
||||||
_parameters,
|
|
||||||
abiParams.get(),
|
|
||||||
_bytes
|
|
||||||
);
|
|
||||||
|
|
||||||
if (preferredParams)
|
defaultParameters.push_back(Parameter{bytes(), "", ABIType{ABIType::HexString, ABIType::AlignNone, 4}, FormatInfo{}});
|
||||||
{
|
defaultParameters.push_back(Parameter{bytes(), "", ABIType{ABIType::Hex}, FormatInfo{}});
|
||||||
ContractABIUtils::overwriteParameters(_errorReporter, preferredParams.get(), abiParams.get());
|
defaultParameters.push_back(Parameter{bytes(), "", ABIType{ABIType::UnsignedDec}, FormatInfo{}});
|
||||||
os << BytesUtils::formatBytesRange(_bytes, preferredParams.get(), _highlight);
|
defaultParameters.push_back(Parameter{bytes(), "", ABIType{ABIType::String}, FormatInfo{}});
|
||||||
}
|
|
||||||
|
os << BytesUtils::formatBytesRange(_bytes, defaultParameters, _highlight);
|
||||||
|
|
||||||
|
return os.str();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ParameterList defaultParameters;
|
boost::optional<ParameterList> abiParams = ContractABIUtils::parametersFromJsonOutputs(
|
||||||
fill_n(
|
_errorReporter,
|
||||||
back_inserter(defaultParameters),
|
m_contractABI,
|
||||||
ceil(_bytes.size() / 32),
|
_signature
|
||||||
Parameter{bytes(), "", ABIType{ABIType::Hex}, FormatInfo{}}
|
|
||||||
);
|
);
|
||||||
ContractABIUtils::overwriteParameters(_errorReporter, defaultParameters, _parameters);
|
|
||||||
os << BytesUtils::formatBytesRange(_bytes, defaultParameters, _highlight);
|
if (abiParams)
|
||||||
|
{
|
||||||
|
boost::optional<ParameterList> 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();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,8 @@ private:
|
|||||||
bytes const& _bytes,
|
bytes const& _bytes,
|
||||||
std::string const& _signature,
|
std::string const& _signature,
|
||||||
ParameterList const& _params,
|
ParameterList const& _params,
|
||||||
bool highlight = false
|
bool highlight = false,
|
||||||
|
bool failure = false
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
/// Formats a given _bytes applying the _abiType.
|
/// Formats a given _bytes applying the _abiType.
|
||||||
@ -104,6 +105,15 @@ private:
|
|||||||
ABIType const& _abiType
|
ABIType const& _abiType
|
||||||
) const;
|
) 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.
|
/// Formats the given parameters using their raw string representation.
|
||||||
std::string formatRawParameters(
|
std::string formatRawParameters(
|
||||||
ParameterList const& _params,
|
ParameterList const& _params,
|
||||||
|
Loading…
Reference in New Issue
Block a user