Skip to content

Commit 481971c

Browse files
committed
[isoltest] Add support for external sources.
1 parent 2969bc0 commit 481971c

48 files changed

Lines changed: 325 additions & 65 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

libsolidity/interface/CompilerStack.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -697,15 +697,15 @@ vector<string> CompilerStack::contractNames() const
697697
return contractNames;
698698
}
699699

700-
string const CompilerStack::lastContractName() const
700+
string const CompilerStack::lastContractName(optional<string> const& _sourceName) const
701701
{
702702
if (m_stackState < AnalysisPerformed)
703703
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
704704
// try to find some user-supplied contract
705705
string contractName;
706706
for (auto const& it: m_sources)
707-
for (ASTPointer<ASTNode> const& node: it.second.ast->nodes())
708-
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
707+
if (_sourceName.value_or(it.first) == it.first)
708+
for (auto const* contract: ASTNode::filteredNodes<ContractDefinition>(it.second.ast->nodes()))
709709
contractName = contract->fullyQualifiedName();
710710
return contractName;
711711
}

libsolidity/interface/CompilerStack.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,8 @@ class CompilerStack
261261
/// @returns a list of the contract names in the sources.
262262
std::vector<std::string> contractNames() const;
263263

264-
/// @returns the name of the last contract.
265-
std::string const lastContractName() const;
264+
/// @returns the name of the last contract. If _sourceName is defined the last contract of that source will be returned.
265+
std::string const lastContractName(std::optional<std::string> const& _sourceName = std::nullopt) const;
266266

267267
/// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use
268268
std::string const filesystemFriendlyName(std::string const& _contractName) const;

test/Common.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <test/Common.h>
2222

2323
#include <libsolutil/Assertions.h>
24+
#include <boost/algorithm/string.hpp>
2425
#include <boost/filesystem.hpp>
2526
#include <boost/program_options.hpp>
2627

@@ -222,4 +223,19 @@ void CommonOptions::setSingleton(std::unique_ptr<CommonOptions const>&& _instanc
222223

223224
std::unique_ptr<CommonOptions const> CommonOptions::m_singleton = nullptr;
224225

226+
bool isValidSemanticTestPath(boost::filesystem::path const& _testPath)
227+
{
228+
bool insideSemanticTests = false;
229+
fs::path testPathPrefix;
230+
for (auto const& element: _testPath)
231+
{
232+
testPathPrefix /= element;
233+
if (boost::ends_with(canonical(testPathPrefix).generic_string(), "/test/libsolidity/semanticTests"))
234+
insideSemanticTests = true;
235+
if (insideSemanticTests && boost::starts_with(element.string(), "_"))
236+
return false;
237+
}
238+
return true;
239+
}
240+
225241
}

test/Common.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,9 @@ struct CommonOptions
8686
static std::unique_ptr<CommonOptions const> m_singleton;
8787
};
8888

89+
/// @return true if it is ok to treat the file located under the specified path as a semantic test.
90+
/// I.e. if the test is located in the semantic test directory and is not excluded due to being a part of external sources.
91+
/// Note: @p _testPath can be relative but must include at least the `/test/libsolidity/semanticTests/` part
92+
bool isValidSemanticTestPath(boost::filesystem::path const& _testPath);
93+
8994
}

test/CommonSyntaxTest.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ int parseUnsignedInteger(string::iterator& _it, string::iterator _end)
5959

6060
CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion):
6161
EVMVersionRestrictedTestCase(_filename),
62-
m_sources(m_reader.sources().sources),
62+
m_sources(m_reader.sources()),
6363
m_expectations(parseExpectations(m_reader.stream())),
6464
m_evmVersion(_evmVersion)
6565
{
@@ -92,12 +92,13 @@ void CommonSyntaxTest::printExpectationAndError(ostream& _stream, string const&
9292

9393
void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const
9494
{
95-
if (m_sources.empty())
95+
if (m_sources.sources.empty())
9696
return;
9797

98-
bool outputSourceNames = (m_sources.size() != 1 || !m_sources.begin()->first.empty());
98+
assert(m_sources.externalSources.empty());
99+
bool outputSourceNames = (m_sources.sources.size() != 1 || !m_sources.sources.begin()->first.empty());
99100

100-
for (auto const& [name, source]: m_sources)
101+
for (auto const& [name, source]: m_sources.sources)
101102
if (_formatted)
102103
{
103104
if (source.empty())

test/CommonSyntaxTest.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include <test/libsolidity/AnalysisFramework.h>
2222
#include <test/TestCase.h>
23+
#include <test/TestCaseReader.h>
2324
#include <liblangutil/Exceptions.h>
2425
#include <libsolutil/AnsiColorized.h>
2526

@@ -81,7 +82,7 @@ class CommonSyntaxTest: public frontend::test::EVMVersionRestrictedTestCase
8182

8283
static std::vector<SyntaxTestError> parseExpectations(std::istream& _stream);
8384

84-
std::map<std::string, std::string> m_sources;
85+
frontend::test::SourceMap m_sources;
8586
std::vector<SyntaxTestError> m_expectations;
8687
std::vector<SyntaxTestError> m_errorList;
8788
langutil::EVMVersion const m_evmVersion;

test/ExecutionFramework.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ class ExecutionFramework
6060
u256 const& _value = 0,
6161
std::string const& _contractName = "",
6262
bytes const& _arguments = {},
63-
std::map<std::string, util::h160> const& _libraryAddresses = {}
63+
std::map<std::string, util::h160> const& _libraryAddresses = {},
64+
std::optional<std::string> const& _sourceName = std::nullopt
6465
) = 0;
6566

6667
bytes const& compileAndRun(

test/TestCaseReader.cpp

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,29 @@
1818

1919
#include <test/TestCaseReader.h>
2020

21+
#include <libsolidity/parsing/Parser.h>
2122
#include <libsolutil/StringUtils.h>
23+
#include <libsolutil/CommonIO.h>
2224

2325
#include <boost/algorithm/string.hpp>
2426
#include <boost/throw_exception.hpp>
27+
#include <boost/filesystem.hpp>
2528

2629
#include <range/v3/view/map.hpp>
2730

2831
using namespace std;
32+
using namespace solidity::langutil;
2933
using namespace solidity::frontend::test;
3034

31-
TestCaseReader::TestCaseReader(string const& _filename):
32-
m_file(_filename)
35+
namespace fs = boost::filesystem;
36+
37+
TestCaseReader::TestCaseReader(string const& _filename): m_fileStream(_filename), m_fileName(_filename)
3338
{
34-
if (!m_file)
39+
if (!m_fileStream)
3540
BOOST_THROW_EXCEPTION(runtime_error("Cannot open file: \"" + _filename + "\"."));
36-
m_file.exceptions(ios::badbit);
41+
m_fileStream.exceptions(ios::badbit);
3742

38-
tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber(m_file);
43+
tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber(m_fileStream);
3944
m_unreadSettings = m_settings;
4045
}
4146

@@ -55,7 +60,7 @@ string const& TestCaseReader::source() const
5560

5661
string TestCaseReader::simpleExpectations()
5762
{
58-
return parseSimpleExpectations(m_file);
63+
return parseSimpleExpectations(m_fileStream);
5964
}
6065

6166
bool TestCaseReader::boolSetting(std::string const& _name, bool _defaultValue)
@@ -105,10 +110,12 @@ void TestCaseReader::ensureAllSettingsRead() const
105110
pair<SourceMap, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(istream& _stream)
106111
{
107112
map<string, string> sources;
113+
map<string, boost::filesystem::path> externalSources;
108114
string currentSourceName;
109115
string currentSource;
110116
string line;
111117
size_t lineNumber = 1;
118+
static string const externalSourceDelimiterStart("==== ExternalSource:");
112119
static string const sourceDelimiterStart("==== Source:");
113120
static string const sourceDelimiterEnd("====");
114121
static string const comment("// ");
@@ -137,6 +144,41 @@ pair<SourceMap, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(is
137144
if (sources.count(currentSourceName))
138145
BOOST_THROW_EXCEPTION(runtime_error("Multiple definitions of test source \"" + currentSourceName + "\"."));
139146
}
147+
else if (boost::algorithm::starts_with(line, externalSourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd))
148+
{
149+
string externalSourceString = boost::trim_copy(line.substr(
150+
externalSourceDelimiterStart.size(),
151+
line.size() - sourceDelimiterEnd.size() - externalSourceDelimiterStart.size()
152+
));
153+
154+
string externalSourceName;
155+
size_t remappingPos = externalSourceString.find('=');
156+
// Does the external source define a remapping?
157+
if (remappingPos != string::npos)
158+
{
159+
externalSourceName = boost::trim_copy(externalSourceString.substr(0, remappingPos));
160+
externalSourceString = boost::trim_copy(externalSourceString.substr(remappingPos + 1));
161+
}
162+
else
163+
externalSourceName = externalSourceString;
164+
165+
solAssert(!externalSourceName.empty(), "");
166+
fs::path externalSourceTarget(externalSourceString);
167+
fs::path testCaseParentDir = m_fileName.parent_path();
168+
if (!externalSourceTarget.is_relative())
169+
BOOST_THROW_EXCEPTION(runtime_error("External Source paths need to be relative to the location of the test case."));
170+
fs::path externalSourceFullPath = testCaseParentDir / externalSourceTarget;
171+
string externalSourceContent;
172+
if (!fs::exists(externalSourceFullPath))
173+
BOOST_THROW_EXCEPTION(runtime_error("External Source '" + externalSourceTarget.string() + "' not found."));
174+
else
175+
externalSourceContent = util::readFileAsString(externalSourceFullPath.string());
176+
177+
if (sources.count(externalSourceName))
178+
BOOST_THROW_EXCEPTION(runtime_error("Multiple definitions of test source \"" + externalSourceName + "\"."));
179+
sources[externalSourceName] = externalSourceContent;
180+
externalSources[externalSourceName] = externalSourceTarget;
181+
}
140182
else
141183
currentSource += line + "\n";
142184
}
@@ -156,7 +198,7 @@ pair<SourceMap, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(is
156198
}
157199
// Register the last source as the main one
158200
sources[currentSourceName] = currentSource;
159-
return {{move(sources), move(currentSourceName)}, lineNumber};
201+
return {{move(sources), move(externalSources), move(currentSourceName)}, lineNumber};
160202
}
161203

162204
string TestCaseReader::parseSimpleExpectations(istream& _file)

test/TestCaseReader.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
// SPDX-License-Identifier: GPL-3.0
1818

19+
#include <boost/filesystem.hpp>
1920
#include <fstream>
2021
#include <map>
2122
#include <string>
@@ -31,6 +32,7 @@ namespace solidity::frontend::test
3132
struct SourceMap
3233
{
3334
std::map<std::string, std::string> sources;
35+
std::map<std::string, boost::filesystem::path> externalSources;
3436
std::string mainSourceFile;
3537
};
3638

@@ -48,7 +50,7 @@ class TestCaseReader
4850
std::string const& source() const;
4951
std::size_t lineNumber() const { return m_lineNumber; }
5052
std::map<std::string, std::string> const& settings() const { return m_settings; }
51-
std::ifstream& stream() { return m_file; }
53+
std::ifstream& stream() { return m_fileStream; }
5254

5355
std::string simpleExpectations();
5456

@@ -62,7 +64,8 @@ class TestCaseReader
6264
std::pair<SourceMap, std::size_t> parseSourcesAndSettingsWithLineNumber(std::istream& _file);
6365
static std::string parseSimpleExpectations(std::istream& _file);
6466

65-
std::ifstream m_file;
67+
std::ifstream m_fileStream;
68+
boost::filesystem::path m_fileName;
6669
SourceMap m_sources;
6770
std::size_t m_lineNumber = 0;
6871
std::map<std::string, std::string> m_settings;

test/boostTest.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,10 @@ int registerTests(
8686
fs::directory_iterator(fullpath),
8787
fs::directory_iterator()
8888
))
89-
if (fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename()))
89+
if (
90+
solidity::test::isValidSemanticTestPath(entry) &&
91+
(fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename()))
92+
)
9093
numTestsAdded += registerTests(
9194
*sub_suite,
9295
_basepath, _path / entry.path().filename(),

0 commit comments

Comments
 (0)