-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14506 from ethereum/extracted-natspec-json-tests
Replace Boost-based Natspec test case with one derived from `SyntaxTest`
- Loading branch information
Showing
134 changed files
with
5,543 additions
and
3,446 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
/* | ||
This file is part of solidity. | ||
solidity is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
solidity is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with solidity. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
/** | ||
* Unit tests for the solidity compiler ABI JSON Interface output. | ||
*/ | ||
|
||
#include <test/libsolidity/NatspecJSONTest.h> | ||
|
||
#include <libsolutil/CommonIO.h> | ||
#include <libsolutil/StringUtils.h> | ||
|
||
#include <boost/algorithm/string/predicate.hpp> | ||
|
||
#include <fmt/format.h> | ||
|
||
#include <vector> | ||
|
||
using namespace std; | ||
using namespace solidity::frontend::test; | ||
using namespace solidity::util; | ||
|
||
ostream& solidity::frontend::test::operator<<(ostream& _output, NatspecJSONKind _kind) | ||
{ | ||
switch (_kind) { | ||
case NatspecJSONKind::Devdoc: _output << "devdoc"; break; | ||
case NatspecJSONKind::Userdoc: _output << "userdoc"; break; | ||
} | ||
return _output; | ||
} | ||
|
||
unique_ptr<TestCase> NatspecJSONTest::create(Config const& _config) | ||
{ | ||
return make_unique<NatspecJSONTest>(_config.filename, _config.evmVersion); | ||
} | ||
|
||
void NatspecJSONTest::parseCustomExpectations(istream& _stream) | ||
{ | ||
soltestAssert(m_expectedNatspecJSON.empty()); | ||
|
||
// We expect a series of expectations in the following format: | ||
// | ||
// // <qualified contract name> <devdoc|userdoc> | ||
// // <json> | ||
|
||
string line; | ||
while (getline(_stream, line)) | ||
{ | ||
string_view strippedLine = expectLinePrefix(line); | ||
if (strippedLine.empty()) | ||
continue; | ||
|
||
auto [contractName, kind] = parseExpectationHeader(strippedLine); | ||
|
||
string rawJSON = extractExpectationJSON(_stream); | ||
string jsonErrors; | ||
Json::Value parsedJSON; | ||
bool jsonParsingSuccessful = jsonParseStrict(rawJSON, parsedJSON, &jsonErrors); | ||
if (!jsonParsingSuccessful) | ||
BOOST_THROW_EXCEPTION(runtime_error(fmt::format( | ||
"Malformed JSON in {} expectation for contract {}.\n" | ||
"Note that JSON expectations must be pretty-printed to be split correctly. " | ||
"The object is assumed to and at the first unindented closing brace.\n" | ||
"{}", | ||
toString(kind), | ||
contractName, | ||
rawJSON | ||
))); | ||
|
||
m_expectedNatspecJSON[string(contractName)][kind] = parsedJSON; | ||
} | ||
} | ||
|
||
bool NatspecJSONTest::expectationsMatch() | ||
{ | ||
// NOTE: Comparing pretty printed Json::Values to avoid using its operator==, which fails to | ||
// compare equal numbers as equal. For example, for 'version' field the value is sometimes int, | ||
// sometimes uint and they compare as different even when both are 1. | ||
return | ||
SyntaxTest::expectationsMatch() && | ||
prettyPrinted(obtainedNatspec()) == prettyPrinted(m_expectedNatspecJSON); | ||
} | ||
|
||
void NatspecJSONTest::printExpectedResult(ostream& _stream, string const& _linePrefix, bool _formatted) const | ||
{ | ||
SyntaxTest::printExpectedResult(_stream, _linePrefix, _formatted); | ||
if (!m_expectedNatspecJSON.empty()) | ||
{ | ||
_stream << _linePrefix << "----" << endl; | ||
printIndented(_stream, formatNatspecExpectations(m_expectedNatspecJSON), _linePrefix); | ||
} | ||
} | ||
|
||
void NatspecJSONTest::printObtainedResult(ostream& _stream, string const& _linePrefix, bool _formatted) const | ||
{ | ||
SyntaxTest::printObtainedResult(_stream, _linePrefix, _formatted); | ||
|
||
NatspecMap natspecJSON = obtainedNatspec(); | ||
if (!natspecJSON.empty()) | ||
{ | ||
_stream << _linePrefix << "----" << endl; | ||
// TODO: Diff both versions and highlight differences. | ||
// We should have a helper for doing that in newly defined test cases without much effort. | ||
printIndented(_stream, formatNatspecExpectations(natspecJSON), _linePrefix); | ||
} | ||
} | ||
|
||
tuple<string_view, NatspecJSONKind> NatspecJSONTest::parseExpectationHeader(string_view _line) | ||
{ | ||
for (NatspecJSONKind kind: {NatspecJSONKind::Devdoc, NatspecJSONKind::Userdoc}) | ||
{ | ||
string kindSuffix = " " + toString(kind); | ||
if (boost::algorithm::ends_with(_line, kindSuffix)) | ||
return {_line.substr(0, _line.size() - kindSuffix.size()), kind}; | ||
} | ||
|
||
BOOST_THROW_EXCEPTION(runtime_error( | ||
"Natspec kind (devdoc/userdoc) not present in the expectation: "s.append(_line) | ||
)); | ||
} | ||
|
||
string NatspecJSONTest::extractExpectationJSON(istream& _stream) | ||
{ | ||
string rawJSON; | ||
string line; | ||
while (getline(_stream, line)) | ||
{ | ||
string_view strippedLine = expectLinePrefix(line); | ||
rawJSON += strippedLine; | ||
rawJSON += "\n"; | ||
|
||
if (boost::algorithm::starts_with(strippedLine, "}")) | ||
break; | ||
} | ||
|
||
return rawJSON; | ||
} | ||
|
||
string_view NatspecJSONTest::expectLinePrefix(string_view _line) | ||
{ | ||
size_t startPosition = 0; | ||
if (!boost::algorithm::starts_with(_line, "//")) | ||
BOOST_THROW_EXCEPTION(runtime_error( | ||
"Expectation line is not a comment: "s.append(_line) | ||
)); | ||
|
||
startPosition += 2; | ||
if (startPosition < _line.size() && _line[startPosition] == ' ') | ||
++startPosition; | ||
|
||
return _line.substr(startPosition, _line.size() - startPosition); | ||
} | ||
|
||
string NatspecJSONTest::formatNatspecExpectations(NatspecMap const& _expectations) const | ||
{ | ||
string output; | ||
bool first = true; | ||
// NOTE: Not sorting explicitly because CompilerStack seems to put contracts roughly in the | ||
// order in which they appear in the source, which is much better than alphabetical order. | ||
for (auto const& [contractName, expectationsForAllKinds]: _expectations) | ||
for (auto const& [jsonKind, natspecJSON]: expectationsForAllKinds) | ||
{ | ||
if (!first) | ||
output += "\n\n"; | ||
first = false; | ||
|
||
output += contractName + " " + toString(jsonKind) + "\n"; | ||
output += jsonPrint(natspecJSON, {JsonFormat::Pretty, 4}); | ||
} | ||
|
||
return output; | ||
} | ||
|
||
NatspecMap NatspecJSONTest::obtainedNatspec() const | ||
{ | ||
if (compiler().state() < CompilerStack::AnalysisSuccessful) | ||
return {}; | ||
|
||
NatspecMap result; | ||
for (string contractName: compiler().contractNames()) | ||
{ | ||
result[contractName][NatspecJSONKind::Devdoc] = compiler().natspecDev(contractName); | ||
result[contractName][NatspecJSONKind::Userdoc] = compiler().natspecUser(contractName); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
SerializedNatspecMap NatspecJSONTest::prettyPrinted(NatspecMap const& _expectations) const | ||
{ | ||
SerializedNatspecMap result; | ||
for (auto const& [contractName, expectationsForAllKinds]: _expectations) | ||
for (auto const& [jsonKind, natspecJSON]: expectationsForAllKinds) | ||
result[contractName][jsonKind] = jsonPrint(natspecJSON, {JsonFormat::Pretty, 4}); | ||
|
||
return result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/* | ||
This file is part of solidity. | ||
solidity is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
solidity is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with solidity. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
/** | ||
* Unit tests for the Natspec userdoc and devdoc JSON output. | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <test/libsolidity/SyntaxTest.h> | ||
|
||
#include <libsolutil/JSON.h> | ||
|
||
#include <istream> | ||
#include <map> | ||
#include <memory> | ||
#include <ostream> | ||
#include <string> | ||
#include <string_view> | ||
#include <tuple> | ||
|
||
namespace solidity::frontend::test | ||
{ | ||
|
||
enum class NatspecJSONKind | ||
{ | ||
Devdoc, | ||
Userdoc, | ||
}; | ||
|
||
std::ostream& operator<<(std::ostream& _output, NatspecJSONKind _kind); | ||
|
||
using NatspecMap = std::map<std::string, std::map<NatspecJSONKind, Json::Value>>; | ||
using SerializedNatspecMap = std::map<std::string, std::map<NatspecJSONKind, std::string>>; | ||
|
||
class NatspecJSONTest: public SyntaxTest | ||
{ | ||
public: | ||
|
||
static std::unique_ptr<TestCase> create(Config const& _config); | ||
|
||
NatspecJSONTest(std::string const& _filename, langutil::EVMVersion _evmVersion): | ||
SyntaxTest( | ||
_filename, | ||
_evmVersion, | ||
langutil::Error::Severity::Error // _minSeverity | ||
) | ||
{} | ||
|
||
protected: | ||
void parseCustomExpectations(std::istream& _stream) override; | ||
bool expectationsMatch() override; | ||
void printExpectedResult(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) const override; | ||
void printObtainedResult(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) const override; | ||
|
||
NatspecMap m_expectedNatspecJSON; | ||
|
||
private: | ||
static std::tuple<std::string_view, NatspecJSONKind> parseExpectationHeader(std::string_view _line); | ||
static std::string extractExpectationJSON(std::istream& _stream); | ||
static std::string_view expectLinePrefix(std::string_view _line); | ||
|
||
std::string formatNatspecExpectations(NatspecMap const& _expectations) const; | ||
SerializedNatspecMap prettyPrinted(NatspecMap const& _expectations) const; | ||
NatspecMap obtainedNatspec() const; | ||
}; | ||
|
||
} |
Oops, something went wrong.