Refactor ASTJSON tests to allow easier addition of variations.

This commit is contained in:
Marenz 2021-10-27 13:22:02 +02:00
parent 9240368e39
commit ab5a06e2b9
2 changed files with 96 additions and 96 deletions

View File

@ -16,17 +16,20 @@
*/ */
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
#include <boost/algorithm/string/replace.hpp>
#include <test/libsolidity/ASTJSONTest.h>
#include <test/Common.h>
#include <libsolutil/AnsiColorized.h>
#include <liblangutil/SourceReferenceFormatter.h> #include <liblangutil/SourceReferenceFormatter.h>
#include <libsolidity/ast/ASTJsonConverter.h> #include <libsolidity/ast/ASTJsonConverter.h>
#include <libsolidity/interface/CompilerStack.h> #include <libsolutil/AnsiColorized.h>
#include <libsolutil/CommonIO.h>
#include <test/Common.h>
#include <test/libsolidity/ASTJSONTest.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/throw_exception.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <boost/throw_exception.hpp>
#include <fstream> #include <fstream>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
@ -72,8 +75,13 @@ ASTJSONTest::ASTJSONTest(string const& _filename)
if (!boost::algorithm::ends_with(_filename, ".sol")) if (!boost::algorithm::ends_with(_filename, ".sol"))
BOOST_THROW_EXCEPTION(runtime_error("Invalid test contract file name: \"" + _filename + "\".")); BOOST_THROW_EXCEPTION(runtime_error("Invalid test contract file name: \"" + _filename + "\"."));
m_astFilename = _filename.substr(0, _filename.size() - 4) + ".json"; string_view baseName = _filename;
m_astParseOnlyFilename = _filename.substr(0, _filename.size() - 4) + "_parseOnly.json"; baseName.remove_suffix(4);
m_variants = {
TestVariant(baseName, CompilerStack::State::Parsed),
TestVariant(baseName, CompilerStack::State::AnalysisPerformed),
};
ifstream file(_filename); ifstream file(_filename);
if (!file) if (!file)
@ -102,26 +110,13 @@ ASTJSONTest::ASTJSONTest(string const& _filename)
} }
m_sources.emplace_back(sourceName.empty() ? "a" : sourceName, source); m_sources.emplace_back(sourceName.empty() ? "a" : sourceName, source);
file.close(); file.close();
file.open(m_astFilename);
if (file) for (TestVariant& variant: m_variants)
{ {
string line; variant.expectation = readFileAsString(variant.astFilename());
while (getline(file, line)) boost::replace_all(variant.expectation, "\r\n", "\n");
m_expectation += line + "\n";
} }
file.close();
file.open(m_astParseOnlyFilename);
if (file)
{
string line;
while (getline(file, line))
m_expectationParseOnly += line + "\n";
}
file.close();
} }
TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted)
@ -135,97 +130,76 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi
sources[m_sources[i].first] = m_sources[i].second; sources[m_sources[i].first] = m_sources[i].second;
sourceIndices[m_sources[i].first] = static_cast<unsigned>(i + 1); sourceIndices[m_sources[i].first] = static_cast<unsigned>(i + 1);
} }
c.setSources(sources);
c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion());
bool resultsMatch = true;
if (!c.compile(CompilerStack::State::Parsed)) for (TestVariant& variant: m_variants)
{ {
SourceReferenceFormatter formatter(_stream, c, _formatted, false); c.reset();
formatter.printErrorInformation(c.errors()); c.setSources(sources);
return TestResult::FatalError; c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion());
if (!c.parseAndAnalyze(variant.stopAfter))
{
// Ignore non-fatal analysis errors, we only want to export.
if (c.state() > CompilerStack::State::Parsed)
continue;
SourceReferenceFormatter formatter(_stream, c, _formatted, false);
formatter.printErrorInformation(c.errors());
return TestResult::FatalError;
}
resultsMatch = resultsMatch && runTest(
variant,
sourceIndices,
c,
_stream,
_linePrefix,
_formatted
);
} }
bool resultsMatch = runTest(
m_expectationParseOnly,
m_resultParseOnly,
sourceIndices,
c,
"parseOnly",
_stream,
_linePrefix,
_formatted
);
c.reset();
c.setSources(sources);
c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion());
if (!c.parse())
{
// Empty Expectations means we expect failure
if (m_expectation.empty())
return resultsMatch ? TestResult::Success : TestResult::Failure;
SourceReferenceFormatter{_stream, c, _formatted, false}
.printErrorInformation(c.errors());
return TestResult::FatalError;
}
c.analyze();
resultsMatch = runTest(
m_expectation,
m_result,
sourceIndices,
c,
"",
_stream,
_linePrefix,
_formatted
) && resultsMatch;
return resultsMatch ? TestResult::Success : TestResult::Failure; return resultsMatch ? TestResult::Success : TestResult::Failure;
} }
bool ASTJSONTest::runTest( bool ASTJSONTest::runTest(
string& _expectation, TestVariant& _variant,
string& _result,
map<string, unsigned> const& _sourceIndices, map<string, unsigned> const& _sourceIndices,
CompilerStack& _compiler, CompilerStack& _compiler,
string const& _variation,
ostream& _stream, ostream& _stream,
string const& _linePrefix, string const& _linePrefix,
bool const _formatted bool const _formatted
) )
{ {
if (m_sources.size() > 1) if (m_sources.size() > 1)
_result += "[\n"; _variant.result += "[\n";
for (size_t i = 0; i < m_sources.size(); i++) for (size_t i = 0; i < m_sources.size(); i++)
{ {
ostringstream result; ostringstream result;
ASTJsonConverter(_compiler.state(), _sourceIndices).print(result, _compiler.ast(m_sources[i].first)); ASTJsonConverter(_compiler.state(), _sourceIndices).print(result, _compiler.ast(m_sources[i].first));
_result += result.str(); _variant.result += result.str();
if (i != m_sources.size() - 1) if (i != m_sources.size() - 1)
_result += ","; _variant.result += ",";
_result += "\n"; _variant.result += "\n";
} }
if (m_sources.size() > 1) if (m_sources.size() > 1)
_result += "]\n"; _variant.result += "]\n";
replaceTagWithVersion(_expectation); replaceTagWithVersion(_variant.expectation);
if (_expectation != _result) if (_variant.expectation != _variant.result)
{ {
string nextIndentLevel = _linePrefix + " "; string nextIndentLevel = _linePrefix + " ";
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << AnsiColorized(_stream, _formatted, {BOLD, CYAN}) <<
_linePrefix << _linePrefix <<
"Expected result" << "Expected result" <<
(!_variation.empty() ? " (" + _variation + "):" : ":") << (!_variant.name().empty() ? " (" + _variant.name() + "):" : ":") <<
endl; endl;
{ {
istringstream stream(_expectation); istringstream stream(_variant.expectation);
string line; string line;
while (getline(stream, line)) while (getline(stream, line))
_stream << nextIndentLevel << line << endl; _stream << nextIndentLevel << line << endl;
@ -235,10 +209,10 @@ bool ASTJSONTest::runTest(
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << AnsiColorized(_stream, _formatted, {BOLD, CYAN}) <<
_linePrefix << _linePrefix <<
"Obtained result" << "Obtained result" <<
(!_variation.empty() ? " (" + _variation + "):" : ":") << (!_variant.name().empty() ? " (" + _variant.name() + "):" : ":") <<
endl; endl;
{ {
istringstream stream(_result); istringstream stream(_variant.result);
string line; string line;
while (getline(stream, line)) while (getline(stream, line))
_stream << nextIndentLevel << line << endl; _stream << nextIndentLevel << line << endl;
@ -266,14 +240,18 @@ void ASTJSONTest::printSource(ostream& _stream, string const& _linePrefix, bool
void ASTJSONTest::printUpdatedExpectations(std::ostream&, std::string const&) const void ASTJSONTest::printUpdatedExpectations(std::ostream&, std::string const&) const
{ {
updateExpectation(m_astFilename, m_result, ""); for (TestVariant const& variant: m_variants)
updateExpectation(m_astParseOnlyFilename, m_resultParseOnly, "parseOnly "); updateExpectation(
variant.astFilename(),
variant.result,
variant.name().empty() ? "" : variant.name() + " "
);
} }
void ASTJSONTest::updateExpectation(string const& _filename, string const& _expectation, string const& _variation) const void ASTJSONTest::updateExpectation(string const& _filename, string const& _expectation, string const& _variant) const
{ {
ofstream file(_filename.c_str()); ofstream file(_filename.c_str());
if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write " + _variation + "AST expectation to \"" + _filename + "\".")); if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write " + _variant + "AST expectation to \"" + _filename + "\"."));
file.exceptions(ios::badbit); file.exceptions(ios::badbit);
string replacedResult = _expectation; string replacedResult = _expectation;

View File

@ -19,6 +19,7 @@
#pragma once #pragma once
#include <libsolutil/AnsiColorized.h> #include <libsolutil/AnsiColorized.h>
#include <libsolidity/interface/CompilerStack.h>
#include <test/TestCase.h> #include <test/TestCase.h>
#include <iosfwd> #include <iosfwd>
@ -37,6 +38,32 @@ namespace solidity::frontend::test
class ASTJSONTest: public TestCase class ASTJSONTest: public TestCase
{ {
public: public:
struct TestVariant
{
TestVariant(std::string_view _baseName, CompilerStack::State _stopAfter):
baseName(_baseName),
stopAfter(_stopAfter)
{}
std::string name() const
{
return stopAfter == CompilerStack::State::Parsed ? "parseOnly" : "";
}
std::string astFilename() const
{
return std::string(baseName) +
(name().empty() ? "" : "_") +
name() +
".json";
}
std::string baseName;
CompilerStack::State stopAfter;
std::string result;
std::string expectation;
};
static std::unique_ptr<TestCase> create(Config const& _config) static std::unique_ptr<TestCase> create(Config const& _config)
{ {
return std::make_unique<ASTJSONTest>(_config.filename); return std::make_unique<ASTJSONTest>(_config.filename);
@ -49,11 +76,9 @@ public:
void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override; void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override;
private: private:
bool runTest( bool runTest(
std::string& _expectation, TestVariant& _testVariant,
std::string& _result,
std::map<std::string, unsigned> const& _sourceIndices, std::map<std::string, unsigned> const& _sourceIndices,
CompilerStack& _compiler, CompilerStack& _compiler,
std::string const& _variation,
std::ostream& _stream, std::ostream& _stream,
std::string const& _linePrefix = "", std::string const& _linePrefix = "",
bool const _formatted = false bool const _formatted = false
@ -61,15 +86,12 @@ private:
void updateExpectation( void updateExpectation(
std::string const& _filename, std::string const& _filename,
std::string const& _expectation, std::string const& _expectation,
std::string const& _variation std::string const& _variant
) const; ) const;
std::vector<TestVariant> m_variants;
std::vector<std::pair<std::string, std::string>> m_sources; std::vector<std::pair<std::string, std::string>> m_sources;
std::string m_expectationParseOnly;
std::string m_astFilename;
std::string m_astParseOnlyFilename;
std::string m_result;
std::string m_resultParseOnly;
}; };
} }