Adding debug info on gas costs when updating gas values.

Co-authored-by: Kamil Śliwak <kamil.sliwak@codepoets.it>
This commit is contained in:
Djordje Mijovic 2021-02-04 19:18:09 +01:00
parent 14a3731182
commit 2b14efbbcc
4 changed files with 78 additions and 32 deletions

View File

@ -294,7 +294,13 @@ TestCase::TestResult SemanticTest::runTest(
for (TestFunctionCall const& test: m_tests) for (TestFunctionCall const& test: m_tests)
{ {
ErrorReporter errorReporter; ErrorReporter errorReporter;
_stream << test.format(errorReporter, _linePrefix, false, _formatted, false) << endl; _stream << test.format(
errorReporter,
_linePrefix,
TestFunctionCall::RenderMode::ExpectedValuesExpectedGas,
_formatted,
/* _interactivePrint */ true
) << endl;
_stream << errorReporter.format(_linePrefix, _formatted); _stream << errorReporter.format(_linePrefix, _formatted);
} }
_stream << endl; _stream << endl;
@ -302,7 +308,13 @@ TestCase::TestResult SemanticTest::runTest(
for (TestFunctionCall const& test: m_tests) for (TestFunctionCall const& test: m_tests)
{ {
ErrorReporter errorReporter; ErrorReporter errorReporter;
_stream << test.format(errorReporter, _linePrefix, !m_gasCostFailure, _formatted, m_gasCostFailure) << endl; _stream << test.format(
errorReporter,
_linePrefix,
m_gasCostFailure ? TestFunctionCall::RenderMode::ExpectedValuesActualGas : TestFunctionCall::RenderMode::ActualValuesExpectedGas,
_formatted,
/* _interactivePrint */ true
) << endl;
_stream << errorReporter.format(_linePrefix, _formatted); _stream << errorReporter.format(_linePrefix, _formatted);
} }
AnsiColorized(_stream, _formatted, {BOLD, RED}) AnsiColorized(_stream, _formatted, {BOLD, RED})
@ -399,9 +411,8 @@ void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) con
for (TestFunctionCall const& test: m_tests) for (TestFunctionCall const& test: m_tests)
_stream << test.format( _stream << test.format(
"", "",
/* _renderResult = */ !m_gasCostFailure, m_gasCostFailure ? TestFunctionCall::RenderMode::ExpectedValuesActualGas : TestFunctionCall::RenderMode::ActualValuesExpectedGas,
/* _highlight = */ false, /* _highlight = */ false
/* _renderGasCostResult */ m_gasCostFailure
) << endl; ) << endl;
} }

View File

@ -35,9 +35,9 @@ using Token = soltest::Token;
string TestFunctionCall::format( string TestFunctionCall::format(
ErrorReporter& _errorReporter, ErrorReporter& _errorReporter,
string const& _linePrefix, string const& _linePrefix,
bool const _renderResult, RenderMode _renderMode,
bool const _highlight, bool const _highlight,
bool const _renderGasCostResult bool const _interactivePrint
) const ) const
{ {
stringstream stream; stringstream stream;
@ -66,9 +66,12 @@ string TestFunctionCall::format(
stream << _linePrefix << newline << ws << "storage" << colon << ws; stream << _linePrefix << newline << ws << "storage" << colon << ws;
soltestAssert(m_rawBytes.size() == 1, ""); soltestAssert(m_rawBytes.size() == 1, "");
soltestAssert(m_call.expectations.rawBytes().size() == 1, ""); soltestAssert(m_call.expectations.rawBytes().size() == 1, "");
bool isEmpty = _renderResult ? m_rawBytes.front() == 0 : m_call.expectations.rawBytes().front() == 0; bool isEmpty =
_renderMode == RenderMode::ActualValuesExpectedGas ?
m_rawBytes.front() == 0 :
m_call.expectations.rawBytes().front() == 0;
string output = isEmpty ? "empty" : "nonempty"; string output = isEmpty ? "empty" : "nonempty";
if (_renderResult && !matchesExpectation()) if (_renderMode == RenderMode::ActualValuesExpectedGas && !matchesExpectation())
AnsiColorized(stream, highlight, {util::formatting::RED_BACKGROUND}) << output; AnsiColorized(stream, highlight, {util::formatting::RED_BACKGROUND}) << output;
else else
stream << output; stream << output;
@ -105,7 +108,7 @@ string TestFunctionCall::format(
if (m_call.omitsArrow) if (m_call.omitsArrow)
{ {
if (_renderResult && (m_failure || !matchesExpectation())) if (_renderMode == RenderMode::ActualValuesExpectedGas && (m_failure || !matchesExpectation()))
stream << ws << arrow; stream << ws << arrow;
} }
else else
@ -124,11 +127,11 @@ string TestFunctionCall::format(
/// Format either the expected output or the actual result output /// Format either the expected output or the actual result output
string result; string result;
if (!_renderResult) if (_renderMode != RenderMode::ActualValuesExpectedGas)
{ {
bool const isFailure = m_call.expectations.failure; bool const isFailure = m_call.expectations.failure;
result = isFailure ? result = isFailure ?
formatFailure(_errorReporter, m_call, m_rawBytes, _renderResult, highlight) : formatFailure(_errorReporter, m_call, m_rawBytes, /* _renderResult */ false, highlight) :
formatRawParameters(m_call.expectations.result); formatRawParameters(m_call.expectations.result);
if (!result.empty()) if (!result.empty())
AnsiColorized(stream, highlight, {util::formatting::RED_BACKGROUND}) << ws << result; AnsiColorized(stream, highlight, {util::formatting::RED_BACKGROUND}) << ws << result;
@ -141,7 +144,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 ?
formatFailure(_errorReporter, m_call, output, _renderResult, highlight) : formatFailure(_errorReporter, m_call, output, _renderMode == RenderMode::ActualValuesExpectedGas, highlight) :
matchesExpectation() ? matchesExpectation() ?
formatRawParameters(m_call.expectations.result) : formatRawParameters(m_call.expectations.result) :
formatBytesParameters( formatBytesParameters(
@ -205,7 +208,7 @@ string TestFunctionCall::format(
} }
} }
stream << formatGasExpectations(_linePrefix, _renderGasCostResult); stream << formatGasExpectations(_linePrefix, _renderMode == RenderMode::ExpectedValuesActualGas, _interactivePrint);
}; };
formatOutput(m_call.displayMode == FunctionCall::DisplayMode::SingleLine); formatOutput(m_call.displayMode == FunctionCall::DisplayMode::SingleLine);
@ -321,12 +324,35 @@ string TestFunctionCall::formatRawParameters(
return os.str(); return os.str();
} }
string TestFunctionCall::formatGasExpectations(string const& _linePrefix, bool const _renderGasCostResult) const string TestFunctionCall::formatGasExpectations(
string const& _linePrefix,
bool _useActualCost,
bool _showDifference
) const
{ {
stringstream os; stringstream os;
for (auto const& [runType, gasUsed]: (_renderGasCostResult ? m_gasCosts : m_call.expectations.gasUsed)) for (auto const& [runType, gasUsed]: (_useActualCost ? m_gasCosts : m_call.expectations.gasUsed))
if (!runType.empty()) if (!runType.empty())
{
bool differentResults =
m_gasCosts.count(runType) > 0 &&
m_call.expectations.gasUsed.count(runType) > 0 &&
m_gasCosts.at(runType) != m_call.expectations.gasUsed.at(runType);
s256 difference = 0;
if (differentResults)
difference =
static_cast<s256>(m_gasCosts.at(runType)) -
static_cast<s256>(m_call.expectations.gasUsed.at(runType));
int percent = 0;
if (differentResults)
percent = static_cast<int>(
100.0 * (static_cast<double>(difference) / static_cast<double>(m_call.expectations.gasUsed.at(runType)))
);
os << endl << _linePrefix << "// gas " << runType << ": " << (gasUsed.str()); os << endl << _linePrefix << "// gas " << runType << ": " << (gasUsed.str());
if (_showDifference && differentResults && _useActualCost)
os << " [" << showpos << difference << " (" << percent << "%)]";
}
return os.str(); return os.str();
} }

View File

@ -42,37 +42,45 @@ namespace solidity::frontend::test
class TestFunctionCall class TestFunctionCall
{ {
public: public:
enum class RenderMode
{
ExpectedValuesExpectedGas,
ActualValuesExpectedGas,
ExpectedValuesActualGas
};
TestFunctionCall(FunctionCall _call): m_call(std::move(_call)), m_gasCosts(m_call.expectations.gasUsed) {} TestFunctionCall(FunctionCall _call): m_call(std::move(_call)), m_gasCosts(m_call.expectations.gasUsed) {}
/// Formats this function call test and applies the format that was detected during parsing. /// Formats this function call test and applies the format that was detected during parsing.
/// If _renderResult is false, the expected result of the call will be used, if it's true /// _renderMode determines the source of values to be inserted into the updated test expectations.
/// the actual result is used. /// RenderMode::ActualValuesExpectedGas: use the values produced by the test but for gas preserve the original expectations,
/// RenderMode::ExpectedValuesExpectedGas: preserve the original expectations for both gas and other values,
/// RenderMode::ExpectedValuesActualGas: use the original expectations but for gas use actual values,
/// 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.
/// If __renderGasCostResult is false, the expected gas costs will be used, if it's true /// If _interactivePrint is true, we are producing output that will be interactively shown to the user
/// the actual gas costs will be used /// in the terminal rather than used to update the expectations in the test file.
/// If test expectations do not match, the contract ABI is consulted in order to get the /// If test expectations do not match, the contract ABI is consulted in order to get the
/// right encoding for returned bytes, based on the parsed return types. /// right encoding for returned bytes, based on the parsed return types.
/// Reports warnings and errors to the error reporter. /// Reports warnings and errors to the error reporter.
std::string format( std::string format(
ErrorReporter& _errorReporter, ErrorReporter& _errorReporter,
std::string const& _linePrefix = "", std::string const& _linePrefix = "",
bool const _renderResult = false, RenderMode _renderMode = RenderMode::ExpectedValuesExpectedGas,
bool const _highlight = false, bool const _highlight = false,
bool const _renderGasCostResult = false bool const _interactivePrint = false
) const; ) const;
/// Overloaded version that passes an error reporter which is never used outside /// Overloaded version that passes an error reporter which is never used outside
/// of this function. /// of this function.
std::string format( std::string format(
std::string const& _linePrefix = "", std::string const& _linePrefix = "",
bool const _renderResult = false, RenderMode const _renderMode = RenderMode::ExpectedValuesExpectedGas,
bool const _highlight = false, bool const _highlight = false
bool const _renderGasCostResult = false
) const ) const
{ {
ErrorReporter reporter; ErrorReporter reporter;
return format(reporter, _linePrefix, _renderResult, _highlight, _renderGasCostResult); return format(reporter, _linePrefix, _renderMode, _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
@ -124,7 +132,8 @@ private:
/// Formats gas usage expectations one per line /// Formats gas usage expectations one per line
std::string formatGasExpectations( std::string formatGasExpectations(
std::string const& _linePrefix, std::string const& _linePrefix,
bool const _renderGasCostResult bool _useActualCost,
bool _showDifference
) const; ) const;
/// Compares raw expectations (which are converted to a byte representation before), /// Compares raw expectations (which are converted to a byte representation before),

View File

@ -51,7 +51,7 @@ BOOST_AUTO_TEST_CASE(format_unsigned_singleline)
test.setRawBytes(toBigEndian(u256{2})); test.setRawBytes(toBigEndian(u256{2}));
test.setFailure(false); test.setFailure(false);
BOOST_REQUIRE_EQUAL(test.format("", true), "// f(uint8): 1 -> 2"); BOOST_REQUIRE_EQUAL(test.format("", TestFunctionCall::RenderMode::ActualValuesExpectedGas), "// f(uint8): 1 -> 2");
} }
BOOST_AUTO_TEST_CASE(format_unsigned_singleline_signed_encoding) BOOST_AUTO_TEST_CASE(format_unsigned_singleline_signed_encoding)
@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(format_unsigned_singleline_signed_encoding)
test.setRawBytes(toBigEndian(u256{-1})); test.setRawBytes(toBigEndian(u256{-1}));
test.setFailure(false); test.setFailure(false);
BOOST_REQUIRE_EQUAL(test.format("", true), "// f(uint8): 1 -> -1"); BOOST_REQUIRE_EQUAL(test.format("", TestFunctionCall::RenderMode::ActualValuesExpectedGas), "// f(uint8): 1 -> -1");
} }
BOOST_AUTO_TEST_CASE(format_unsigned_multiline) BOOST_AUTO_TEST_CASE(format_unsigned_multiline)
@ -119,7 +119,7 @@ BOOST_AUTO_TEST_CASE(format_signed_singleline)
test.setRawBytes(toBigEndian(u256{-2})); test.setRawBytes(toBigEndian(u256{-2}));
test.setFailure(false); test.setFailure(false);
BOOST_REQUIRE_EQUAL(test.format("", true), "// f(int8): -1 -> -2"); BOOST_REQUIRE_EQUAL(test.format("", TestFunctionCall::RenderMode::ActualValuesExpectedGas), "// f(int8): -1 -> -2");
} }
BOOST_AUTO_TEST_CASE(format_hex_singleline) BOOST_AUTO_TEST_CASE(format_hex_singleline)
@ -142,7 +142,7 @@ BOOST_AUTO_TEST_CASE(format_hex_singleline)
test.setRawBytes(actualBytes); test.setRawBytes(actualBytes);
test.setFailure(false); test.setFailure(false);
BOOST_REQUIRE_EQUAL(test.format("", true), "// f(bytes32): 0x31 -> 0x32"); BOOST_REQUIRE_EQUAL(test.format("", TestFunctionCall::RenderMode::ActualValuesExpectedGas), "// f(bytes32): 0x31 -> 0x32");
} }
BOOST_AUTO_TEST_CASE(format_hex_string_singleline) BOOST_AUTO_TEST_CASE(format_hex_string_singleline)
@ -177,7 +177,7 @@ BOOST_AUTO_TEST_CASE(format_bool_true_singleline)
test.setRawBytes(actualBytes); test.setRawBytes(actualBytes);
test.setFailure(false); test.setFailure(false);
BOOST_REQUIRE_EQUAL(test.format("", true), "// f(bool): true -> false"); BOOST_REQUIRE_EQUAL(test.format("", TestFunctionCall::RenderMode::ActualValuesExpectedGas), "// f(bool): true -> false");
} }
BOOST_AUTO_TEST_CASE(format_bool_false_singleline) BOOST_AUTO_TEST_CASE(format_bool_false_singleline)