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; AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
for (auto const& test: m_tests) 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; _stream << endl;
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
for (auto const& test: m_tests) 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 AnsiColorized(_stream, _formatted, {BOLD, RED}) << _linePrefix << endl << _linePrefix
<< "Attention: Updates on the test will apply the detected format displayed." << endl; << "Attention: Updates on the test will apply the detected format displayed." << endl;
return false; return false;

View File

@ -22,12 +22,18 @@ using namespace solidity;
using namespace dev::solidity::test; using namespace dev::solidity::test;
using namespace std; 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 namespace soltest;
using Token = soltest::Token; using Token = soltest::Token;
stringstream _stream; stringstream stream;
bool highlight = !matchesExpectation() && _highlight; bool highlight = !matchesExpectation() && _highlight;
auto formatOutput = [&](bool const _singleLine) auto formatOutput = [&](bool const _singleLine)
@ -42,16 +48,16 @@ string TestFunctionCall::format(string const& _linePrefix, bool const _renderRes
string failure = formatToken(Token::Failure); string failure = formatToken(Token::Failure);
/// Formats the function signature. This is the same independent from the display-mode. /// 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)) 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()) if (!m_call.arguments.rawBytes().empty())
{ {
string output = formatRawParameters(m_call.arguments.parameters, _linePrefix); string output = formatRawParameters(m_call.arguments.parameters, _linePrefix);
_stream << colon; stream << colon;
if (_singleLine) if (_singleLine)
_stream << ws; stream << ws;
_stream << output; stream << output;
} }
@ -60,18 +66,18 @@ string TestFunctionCall::format(string const& _linePrefix, bool const _renderRes
if (_singleLine) if (_singleLine)
{ {
if (!m_call.arguments.comment.empty()) if (!m_call.arguments.comment.empty())
_stream << ws << comment << m_call.arguments.comment << comment; stream << ws << comment << m_call.arguments.comment << comment;
_stream << ws << arrow << ws; stream << ws << arrow << ws;
} }
else else
{ {
_stream << endl << _linePrefix << newline << ws; stream << endl << _linePrefix << newline << ws;
if (!m_call.arguments.comment.empty()) if (!m_call.arguments.comment.empty())
{ {
_stream << comment << m_call.arguments.comment << comment; stream << comment << m_call.arguments.comment << comment;
_stream << endl << _linePrefix << newline << ws; stream << endl << _linePrefix << newline << ws;
} }
_stream << arrow << ws; stream << arrow << ws;
} }
/// Format either the expected output or the actual result output /// Format either the expected output or the actual result output
@ -92,44 +98,46 @@ string TestFunctionCall::format(string const& _linePrefix, bool const _renderRes
failure : failure :
matchesExpectation() ? matchesExpectation() ?
formatRawParameters(m_call.expectations.result) : 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. /// Format comments on expectations taking the display-mode into account.
if (_singleLine) if (_singleLine)
{ {
if (!m_call.expectations.comment.empty()) if (!m_call.expectations.comment.empty())
_stream << ws << comment << m_call.expectations.comment << comment; stream << ws << comment << m_call.expectations.comment << comment;
} }
else else
{ {
if (!m_call.expectations.comment.empty()) if (!m_call.expectations.comment.empty())
{ {
_stream << endl << _linePrefix << newline << ws; stream << endl << _linePrefix << newline << ws;
_stream << comment << m_call.expectations.comment << comment; stream << comment << m_call.expectations.comment << comment;
} }
} }
}; };
if (m_call.displayMode == FunctionCall::DisplayMode::SingleLine) formatOutput(m_call.displayMode == FunctionCall::DisplayMode::SingleLine);
formatOutput(true); return stream.str();
else
formatOutput(false);
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; stringstream resultStream;
if (_bytes.empty()) if (_bytes.empty())
return {}; return {};
auto sizeFold = [](size_t const _a, Parameter const& _b) { return _a + _b.abiType.size; }; 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); size_t encodingSize = std::accumulate(_params.begin(), _params.end(), size_t{0}, sizeFold);
soltestAssert( if (encodingSize != _bytes.size())
encodingSize == _bytes.size(), _errorReporter.warning(
"Encoding does not match byte range: the call returned " + "Encoding does not match byte range. The call returned " +
to_string(_bytes.size()) + " bytes, but " + to_string(_bytes.size()) + " bytes, but " +
to_string(encodingSize) + " bytes were expected." to_string(encodingSize) + " bytes were expected."
); );

View File

@ -19,6 +19,7 @@
#include <libsolidity/ast/Types.h> #include <libsolidity/ast/Types.h>
#include <liblangutil/Exceptions.h> #include <liblangutil/Exceptions.h>
#include <libdevcore/AnsiColorized.h>
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>
#include <iosfwd> #include <iosfwd>
@ -34,6 +35,99 @@ namespace solidity
namespace test 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 * 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 * representation itself, the actual byte result (if any) and a string representation
@ -51,7 +145,25 @@ public:
/// the actual result is used. /// the actual result is used.
/// If _highlight is false, it's formatted without colorized highlighting. If it's true, AnsiColorized is /// If _highlight is false, it's formatted without colorized highlighting. If it's true, AnsiColorized is
/// used to apply a colorized highlighting. /// 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 /// Resets current results in case the function was called and the result
/// stored already (e.g. if test case was updated via isoltest). /// 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. /// 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 /// Throws if there's a mismatch in the size of `bytes` and the desired formats that are specified
/// in the ABI type. /// 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. /// Formats the given parameters using their raw string representation.
std::string formatRawParameters(ParameterList const& _params, std::string const& _linePrefix = "") const; std::string formatRawParameters(ParameterList const& _params, std::string const& _linePrefix = "") const;