CommonSyntaxTest: Add support for syntax tests with custom expectations in addition to expected errors

This commit is contained in:
Kamil Śliwak 2023-08-18 15:44:13 +02:00
parent c965d6332c
commit e847596e39
3 changed files with 62 additions and 9 deletions

View File

@ -19,6 +19,7 @@
#include <test/CommonSyntaxTest.h> #include <test/CommonSyntaxTest.h>
#include <test/Common.h> #include <test/Common.h>
#include <test/TestCase.h> #include <test/TestCase.h>
#include <libsolutil/CommonIO.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
@ -29,6 +30,7 @@
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::util;
using namespace solidity::util::formatting; using namespace solidity::util::formatting;
using namespace solidity::langutil; using namespace solidity::langutil;
using namespace solidity::frontend; using namespace solidity::frontend;
@ -66,6 +68,7 @@ CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion
TestCase::TestResult CommonSyntaxTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) TestCase::TestResult CommonSyntaxTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)
{ {
parseCustomExpectations(m_reader.stream());
parseAndAnalyze(); parseAndAnalyze();
return conclude(_stream, _linePrefix, _formatted); return conclude(_stream, _linePrefix, _formatted);
@ -73,7 +76,7 @@ TestCase::TestResult CommonSyntaxTest::run(ostream& _stream, string const& _line
TestCase::TestResult CommonSyntaxTest::conclude(ostream& _stream, string const& _linePrefix, bool _formatted) TestCase::TestResult CommonSyntaxTest::conclude(ostream& _stream, string const& _linePrefix, bool _formatted)
{ {
if (m_expectations == m_errorList) if (expectationsMatch())
return TestResult::Success; return TestResult::Success;
printExpectationAndError(_stream, _linePrefix, _formatted); printExpectationAndError(_stream, _linePrefix, _formatted);
@ -84,9 +87,9 @@ void CommonSyntaxTest::printExpectationAndError(ostream& _stream, string const&
{ {
string nextIndentLevel = _linePrefix + " "; string nextIndentLevel = _linePrefix + " ";
util::AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; util::AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
printErrorList(_stream, m_expectations, nextIndentLevel, _formatted); printExpectedResult(_stream, nextIndentLevel, _formatted);
util::AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; util::AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
printErrorList(_stream, m_errorList, nextIndentLevel, _formatted); printObtainedResult(_stream, nextIndentLevel, _formatted);
} }
void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const
@ -149,6 +152,30 @@ void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix,
} }
} }
void CommonSyntaxTest::parseCustomExpectations(istream& _stream)
{
string remainingExpectations = boost::trim_copy(readUntilEnd(_stream));
soltestAssert(
remainingExpectations.empty(),
"Found custom expectations not supported by the test case:\n" + remainingExpectations
);
}
bool CommonSyntaxTest::expectationsMatch()
{
return m_expectations == m_errorList;
}
void CommonSyntaxTest::printExpectedResult(ostream& _stream, string const& _linePrefix, bool _formatted) const
{
printErrorList(_stream, m_expectations, _linePrefix, _formatted);
}
void CommonSyntaxTest::printObtainedResult(ostream& _stream, string const& _linePrefix, bool _formatted) const
{
printErrorList(_stream, m_errorList, _linePrefix, _formatted);
}
void CommonSyntaxTest::printErrorList( void CommonSyntaxTest::printErrorList(
ostream& _stream, ostream& _stream,
vector<SyntaxTestError> const& _errorList, vector<SyntaxTestError> const& _errorList,
@ -157,7 +184,10 @@ void CommonSyntaxTest::printErrorList(
) )
{ {
if (_errorList.empty()) if (_errorList.empty())
{
if (_formatted)
util::AnsiColorized(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; util::AnsiColorized(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl;
}
else else
for (auto const& error: _errorList) for (auto const& error: _errorList)
{ {
@ -194,12 +224,20 @@ string CommonSyntaxTest::errorMessage(util::Exception const& _e)
vector<SyntaxTestError> CommonSyntaxTest::parseExpectations(istream& _stream) vector<SyntaxTestError> CommonSyntaxTest::parseExpectations(istream& _stream)
{ {
static string const customExpectationsDelimiter("// ----");
vector<SyntaxTestError> expectations; vector<SyntaxTestError> expectations;
string line; string line;
while (getline(_stream, line)) while (getline(_stream, line))
{ {
auto it = line.begin(); auto it = line.begin();
// Anything below the delimiter is left up to the derived class to process in a custom way.
// The delimiter is optional and identical to the one that starts error expectations in
// TestCaseReader::parseSourcesAndSettingsWithLineNumber().
if (boost::algorithm::starts_with(line, customExpectationsDelimiter))
break;
skipSlashes(it, line.end()); skipSlashes(it, line.end());
skipWhitespace(it, line.end()); skipWhitespace(it, line.end());

View File

@ -62,14 +62,27 @@ public:
void printSource(std::ostream& _stream, std::string const &_linePrefix = "", bool _formatted = false) const override; void printSource(std::ostream& _stream, std::string const &_linePrefix = "", bool _formatted = false) const override;
void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override
{ {
if (!m_errorList.empty()) printObtainedResult(_stream, _linePrefix, false);
printErrorList(_stream, m_errorList, _linePrefix, false);
} }
static std::string errorMessage(util::Exception const& _e); static std::string errorMessage(util::Exception const& _e);
protected: protected:
/// Should be implemented by those derived test cases that want to allow extra expectations
/// after the error/warning expectations. The default implementation does not allow them and
/// fails instead.
/// @param _stream Input stream positioned at the beginning of the extra expectations.
virtual void parseCustomExpectations(std::istream& _stream);
virtual void parseAndAnalyze() = 0; virtual void parseAndAnalyze() = 0;
/// Should return true if obtained values match expectations.
/// The default implementation only compares the error list. Derived classes that support
/// custom expectations should override this to include them in the comparison.
virtual bool expectationsMatch();
virtual void printExpectedResult(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) const;
virtual void printObtainedResult(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) const;
static void printErrorList( static void printErrorList(
std::ostream& _stream, std::ostream& _stream,
std::vector<SyntaxTestError> const& _errors, std::vector<SyntaxTestError> const& _errors,

View File

@ -114,15 +114,17 @@ pair<SourceMap, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(is
static string const sourceDelimiterEnd("===="); static string const sourceDelimiterEnd("====");
static string const comment("// "); static string const comment("// ");
static string const settingsDelimiter("// ===="); static string const settingsDelimiter("// ====");
static string const delimiter("// ----"); static string const expectationsDelimiter("// ----");
bool sourcePart = true; bool sourcePart = true;
while (getline(_stream, line)) while (getline(_stream, line))
{ {
lineNumber++; lineNumber++;
if (boost::algorithm::starts_with(line, delimiter)) // Anything below the delimiter is left up to the test case to process in a custom way.
if (boost::algorithm::starts_with(line, expectationsDelimiter))
break; break;
else if (boost::algorithm::starts_with(line, settingsDelimiter))
if (boost::algorithm::starts_with(line, settingsDelimiter))
sourcePart = false; sourcePart = false;
else if (sourcePart) else if (sourcePart)
{ {