mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
NatspecJSONTest based on SyntaxTest
This commit is contained in:
parent
34c86d90be
commit
91cc72bcd4
@ -84,6 +84,8 @@ set(libsolidity_sources
|
|||||||
libsolidity/Metadata.cpp
|
libsolidity/Metadata.cpp
|
||||||
libsolidity/MemoryGuardTest.cpp
|
libsolidity/MemoryGuardTest.cpp
|
||||||
libsolidity/MemoryGuardTest.h
|
libsolidity/MemoryGuardTest.h
|
||||||
|
libsolidity/NatspecJSONTest.cpp
|
||||||
|
libsolidity/NatspecJSONTest.h
|
||||||
libsolidity/SemanticTest.cpp
|
libsolidity/SemanticTest.cpp
|
||||||
libsolidity/SemanticTest.h
|
libsolidity/SemanticTest.h
|
||||||
libsolidity/SemVerMatcher.cpp
|
libsolidity/SemVerMatcher.cpp
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <test/libsolidity/ASTPropertyTest.h>
|
#include <test/libsolidity/ASTPropertyTest.h>
|
||||||
#include <test/libsolidity/GasTest.h>
|
#include <test/libsolidity/GasTest.h>
|
||||||
#include <test/libsolidity/MemoryGuardTest.h>
|
#include <test/libsolidity/MemoryGuardTest.h>
|
||||||
|
#include <test/libsolidity/NatspecJSONTest.h>
|
||||||
#include <test/libsolidity/SyntaxTest.h>
|
#include <test/libsolidity/SyntaxTest.h>
|
||||||
#include <test/libsolidity/SemanticTest.h>
|
#include <test/libsolidity/SemanticTest.h>
|
||||||
#include <test/libsolidity/SMTCheckerTest.h>
|
#include <test/libsolidity/SMTCheckerTest.h>
|
||||||
@ -74,6 +75,7 @@ Testsuite const g_interactiveTestsuites[] = {
|
|||||||
{"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create},
|
{"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create},
|
||||||
{"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create},
|
{"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create},
|
||||||
{"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create},
|
{"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create},
|
||||||
|
{"JSON Natspec", "libsolidity", "natspecJSON", false, false, &NatspecJSONTest::create},
|
||||||
{"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create},
|
{"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create},
|
||||||
{"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create},
|
{"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create},
|
||||||
{"Memory Guard", "libsolidity", "memoryGuardTests", false, false, &MemoryGuardTest::create},
|
{"Memory Guard", "libsolidity", "memoryGuardTests", false, false, &MemoryGuardTest::create},
|
||||||
|
210
test/libsolidity/NatspecJSONTest.cpp
Normal file
210
test/libsolidity/NatspecJSONTest.cpp
Normal file
@ -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;
|
||||||
|
}
|
82
test/libsolidity/NatspecJSONTest.h
Normal file
82
test/libsolidity/NatspecJSONTest.h
Normal file
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -25,6 +25,7 @@ add_executable(isoltest
|
|||||||
../libsolidity/util/TestFunctionCall.cpp
|
../libsolidity/util/TestFunctionCall.cpp
|
||||||
../libsolidity/GasTest.cpp
|
../libsolidity/GasTest.cpp
|
||||||
../libsolidity/MemoryGuardTest.cpp
|
../libsolidity/MemoryGuardTest.cpp
|
||||||
|
../libsolidity/NatspecJSONTest.cpp
|
||||||
../libsolidity/SyntaxTest.cpp
|
../libsolidity/SyntaxTest.cpp
|
||||||
../libsolidity/SemanticTest.cpp
|
../libsolidity/SemanticTest.cpp
|
||||||
../libsolidity/AnalysisFramework.cpp
|
../libsolidity/AnalysisFramework.cpp
|
||||||
|
Loading…
Reference in New Issue
Block a user