Adds error reporter for auto-updates in (i)soltest.

This commit is contained in:
Erik Kundt 2019-04-11 15:42:48 +02:00
parent 39d153b7a1
commit a6cc296cd9
3 changed files with 167 additions and 35 deletions

View File

@ -80,12 +80,19 @@ bool SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _format
{
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
for (auto const& test: m_tests)
_stream << test.format(_linePrefix, false, _formatted) << endl;
{
ErrorReporter errorReporter;
_stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl;
_stream << errorReporter.format(_linePrefix, _formatted);
}
_stream << endl;
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
for (auto const& test: m_tests)
_stream << test.format(_linePrefix, true, _formatted) << endl;
{
ErrorReporter errorReporter;
_stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl;
_stream << errorReporter.format(_linePrefix, _formatted);
}
AnsiColorized(_stream, _formatted, {BOLD, RED}) << _linePrefix << endl << _linePrefix
<< "Attention: Updates on the test will apply the detected format displayed." << endl;
return false;

View File

@ -22,12 +22,18 @@ using namespace solidity;
using namespace dev::solidity::test;
using namespace std;
string TestFunctionCall::format(string const& _linePrefix, bool const _renderResult, bool const _highlight) const
string TestFunctionCall::format(
ErrorReporter& _errorReporter,
string const& _linePrefix,
bool const _renderResult,
bool const _highlight
) const
{
using namespace soltest;
using Token = soltest::Token;
stringstream _stream;
stringstream stream;
bool highlight = !matchesExpectation() && _highlight;
auto formatOutput = [&](bool const _singleLine)
@ -42,16 +48,16 @@ string TestFunctionCall::format(string const& _linePrefix, bool const _renderRes
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;
stream << _linePrefix << newline << ws << m_call.signature;
if (m_call.value > u256(0))
_stream << comma << ws << m_call.value << ws << ether;
stream << comma << ws << m_call.value << ws << ether;
if (!m_call.arguments.rawBytes().empty())
{
string output = formatRawParameters(m_call.arguments.parameters, _linePrefix);
_stream << colon;
stream << colon;
if (_singleLine)
_stream << ws;
_stream << output;
stream << ws;
stream << output;
}
@ -60,18 +66,18 @@ string TestFunctionCall::format(string const& _linePrefix, bool const _renderRes
if (_singleLine)
{
if (!m_call.arguments.comment.empty())
_stream << ws << comment << m_call.arguments.comment << comment;
_stream << ws << arrow << ws;
stream << ws << comment << m_call.arguments.comment << comment;
stream << ws << arrow << ws;
}
else
{
_stream << endl << _linePrefix << newline << ws;
stream << endl << _linePrefix << newline << ws;
if (!m_call.arguments.comment.empty())
{
_stream << comment << m_call.arguments.comment << comment;
_stream << endl << _linePrefix << newline << ws;
stream << comment << m_call.arguments.comment << comment;
stream << endl << _linePrefix << newline << ws;
}
_stream << arrow << ws;
stream << arrow << ws;
}
/// Format either the expected output or the actual result output
@ -92,47 +98,49 @@ string TestFunctionCall::format(string const& _linePrefix, bool const _renderRes
failure :
matchesExpectation() ?
formatRawParameters(m_call.expectations.result) :
formatBytesParameters(output, m_call.expectations.result);
formatBytesParameters(_errorReporter, output, m_call.expectations.result);
}
AnsiColorized(_stream, highlight, {dev::formatting::RED_BACKGROUND}) << 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;
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;
stream << endl << _linePrefix << newline << ws;
stream << comment << m_call.expectations.comment << comment;
}
}
};
if (m_call.displayMode == FunctionCall::DisplayMode::SingleLine)
formatOutput(true);
else
formatOutput(false);
return _stream.str();
formatOutput(m_call.displayMode == FunctionCall::DisplayMode::SingleLine);
return stream.str();
}
string TestFunctionCall::formatBytesParameters(bytes const& _bytes, dev::solidity::test::ParameterList const& _params) const
string TestFunctionCall::formatBytesParameters(
ErrorReporter& _errorReporter,
bytes const& _bytes,
dev::solidity::test::ParameterList const& _params
) const
{
stringstream resultStream;
if (_bytes.empty())
return {};
auto sizeFold = [](size_t const _a, Parameter const& _b) { return _a + _b.abiType.size; };
size_t encodingSize = std::accumulate(_params.begin(), _params.end(), size_t{0}, sizeFold);
soltestAssert(
encodingSize == _bytes.size(),
"Encoding does not match byte range: the call returned " +
to_string(_bytes.size()) + " bytes, but " +
to_string(encodingSize) + " bytes were expected."
);
if (encodingSize != _bytes.size())
_errorReporter.warning(
"Encoding does not match byte range. The call returned " +
to_string(_bytes.size()) + " bytes, but " +
to_string(encodingSize) + " bytes were expected."
);
auto it = _bytes.begin();
for (auto const& param: _params)

View File

@ -19,6 +19,7 @@
#include <libsolidity/ast/Types.h>
#include <liblangutil/Exceptions.h>
#include <libdevcore/AnsiColorized.h>
#include <libdevcore/CommonData.h>
#include <iosfwd>
@ -34,6 +35,99 @@ namespace solidity
namespace test
{
/**
* Representation of a notice, warning or error that can occur while
* formatting and therefore updating an interactive function call test.
*/
struct FormatError
{
enum Type
{
Notice,
Warning,
Error
};
explicit FormatError(Type _type, std::string _message):
type(_type),
message(std::move(_message))
{}
Type type;
std::string message;
};
using FormatErrors = std::vector<FormatError>;
/**
* Utility class that collects notices, warnings and errors and is able
* to format them for ANSI colorized output during the interactive update
* process in isoltest.
* It's purpose is to help users of isoltest to automatically
* update test files but always keep track of what is happening.
*/
class ErrorReporter
{
public:
explicit ErrorReporter() {}
/// Adds a new FormatError of type Notice with the given message.
void notice(std::string _notice)
{
m_errors.push_back(FormatError{FormatError::Notice, std::move(_notice)});
}
/// Adds a new FormatError of type Warning with the given message.
void warning(std::string _warning)
{
m_errors.push_back(FormatError{FormatError::Warning, std::move(_warning)});
}
/// Adds a new FormatError of type Error with the given message.
void error(std::string _error)
{
m_errors.push_back(FormatError{FormatError::Error, std::move(_error)});
}
/// Prints all errors depending on their type using ANSI colorized output.
/// It will be used to print notices, warnings and errors during the
/// interactive update process.
std::string format(std::string const& _linePrefix, bool _formatted)
{
std::stringstream os;
for (auto const& error: m_errors)
{
switch (error.type)
{
case FormatError::Notice:
AnsiColorized(
os,
_formatted,
{formatting::WHITE}
) << _linePrefix << "Notice: " << error.message << std::endl;
break;
case FormatError::Warning:
AnsiColorized(
os,
_formatted,
{formatting::YELLOW}
) << _linePrefix << "Warning: " << error.message << std::endl;
break;
case FormatError::Error:
AnsiColorized(
os,
_formatted,
{formatting::RED}
) << _linePrefix << "Error: " << error.message << std::endl;
break;
}
}
return os.str();
}
private:
FormatErrors m_errors;
};
/**
* Represents a function call and the result it returned. It stores the call
* representation itself, the actual byte result (if any) and a string representation
@ -51,7 +145,25 @@ public:
/// the actual result is used.
/// If _highlight is false, it's formatted without colorized highlighting. If it's true, AnsiColorized is
/// used to apply a colorized highlighting.
std::string format(std::string const& _linePrefix = "", bool const _renderResult = false, bool const _highlight = false) const;
/// Reports warnings and errors to the error reporter.
std::string format(
ErrorReporter& _errorReporter,
std::string const& _linePrefix = "",
bool const _renderResult = false,
bool const _highlight = false
) const;
/// Overloaded version that passes an error reporter which is never used outside
/// of this function.
std::string format(
std::string const& _linePrefix = "",
bool const _renderResult = false,
bool const _highlight = false
) const
{
ErrorReporter reporter;
return format(reporter, _linePrefix, _renderResult, _highlight);
}
/// Resets current results in case the function was called and the result
/// stored already (e.g. if test case was updated via isoltest).
@ -65,7 +177,12 @@ private:
/// Tries to format the given `bytes`, applying the detected ABI types that have be set for each parameter.
/// Throws if there's a mismatch in the size of `bytes` and the desired formats that are specified
/// in the ABI type.
std::string formatBytesParameters(bytes const& _bytes, ParameterList const& _params) const;
/// Reports warnings and errors to the error reporter.
std::string formatBytesParameters(
ErrorReporter& _errorReporter,
bytes const& _bytes,
ParameterList const& _params
) const;
/// Formats the given parameters using their raw string representation.
std::string formatRawParameters(ParameterList const& _params, std::string const& _linePrefix = "") const;