Merge pull request #11110 from ethereum/issue_10475_isoltest_external_sources

[isoltest] Add support for external sources.
This commit is contained in:
chriseth 2021-04-27 10:54:39 +02:00 committed by GitHub
commit f72592549b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 328 additions and 68 deletions

View File

@ -697,15 +697,15 @@ vector<string> CompilerStack::contractNames() const
return contractNames; return contractNames;
} }
string const CompilerStack::lastContractName() const string const CompilerStack::lastContractName(optional<string> const& _sourceName) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
// try to find some user-supplied contract // try to find some user-supplied contract
string contractName; string contractName;
for (auto const& it: m_sources) for (auto const& it: m_sources)
for (ASTPointer<ASTNode> const& node: it.second.ast->nodes()) if (_sourceName.value_or(it.first) == it.first)
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) for (auto const* contract: ASTNode::filteredNodes<ContractDefinition>(it.second.ast->nodes()))
contractName = contract->fullyQualifiedName(); contractName = contract->fullyQualifiedName();
return contractName; return contractName;
} }

View File

@ -261,8 +261,8 @@ public:
/// @returns a list of the contract names in the sources. /// @returns a list of the contract names in the sources.
std::vector<std::string> contractNames() const; std::vector<std::string> contractNames() const;
/// @returns the name of the last contract. /// @returns the name of the last contract. If _sourceName is defined the last contract of that source will be returned.
std::string const lastContractName() const; std::string const lastContractName(std::optional<std::string> const& _sourceName = std::nullopt) const;
/// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use /// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use
std::string const filesystemFriendlyName(std::string const& _contractName) const; std::string const filesystemFriendlyName(std::string const& _contractName) const;

View File

@ -21,6 +21,7 @@
#include <test/Common.h> #include <test/Common.h>
#include <libsolutil/Assertions.h> #include <libsolutil/Assertions.h>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
@ -222,4 +223,19 @@ void CommonOptions::setSingleton(std::unique_ptr<CommonOptions const>&& _instanc
std::unique_ptr<CommonOptions const> CommonOptions::m_singleton = nullptr; std::unique_ptr<CommonOptions const> CommonOptions::m_singleton = nullptr;
bool isValidSemanticTestPath(boost::filesystem::path const& _testPath)
{
bool insideSemanticTests = false;
fs::path testPathPrefix;
for (auto const& element: _testPath)
{
testPathPrefix /= element;
if (boost::ends_with(canonical(testPathPrefix).generic_string(), "/test/libsolidity/semanticTests"))
insideSemanticTests = true;
if (insideSemanticTests && boost::starts_with(element.string(), "_"))
return false;
}
return true;
}
} }

View File

@ -86,4 +86,9 @@ private:
static std::unique_ptr<CommonOptions const> m_singleton; static std::unique_ptr<CommonOptions const> m_singleton;
}; };
/// @return true if it is ok to treat the file located under the specified path as a semantic test.
/// I.e. if the test is located in the semantic test directory and is not excluded due to being a part of external sources.
/// Note: @p _testPath can be relative but must include at least the `/test/libsolidity/semanticTests/` part
bool isValidSemanticTestPath(boost::filesystem::path const& _testPath);
} }

View File

@ -59,7 +59,7 @@ int parseUnsignedInteger(string::iterator& _it, string::iterator _end)
CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion):
EVMVersionRestrictedTestCase(_filename), EVMVersionRestrictedTestCase(_filename),
m_sources(m_reader.sources().sources), m_sources(m_reader.sources()),
m_expectations(parseExpectations(m_reader.stream())), m_expectations(parseExpectations(m_reader.stream())),
m_evmVersion(_evmVersion) m_evmVersion(_evmVersion)
{ {
@ -92,12 +92,13 @@ void CommonSyntaxTest::printExpectationAndError(ostream& _stream, string const&
void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const
{ {
if (m_sources.empty()) if (m_sources.sources.empty())
return; return;
bool outputSourceNames = (m_sources.size() != 1 || !m_sources.begin()->first.empty()); assert(m_sources.externalSources.empty());
bool outputSourceNames = (m_sources.sources.size() != 1 || !m_sources.sources.begin()->first.empty());
for (auto const& [name, source]: m_sources) for (auto const& [name, source]: m_sources.sources)
if (_formatted) if (_formatted)
{ {
if (source.empty()) if (source.empty())

View File

@ -20,6 +20,7 @@
#include <test/libsolidity/AnalysisFramework.h> #include <test/libsolidity/AnalysisFramework.h>
#include <test/TestCase.h> #include <test/TestCase.h>
#include <test/TestCaseReader.h>
#include <liblangutil/Exceptions.h> #include <liblangutil/Exceptions.h>
#include <libsolutil/AnsiColorized.h> #include <libsolutil/AnsiColorized.h>
@ -81,7 +82,7 @@ protected:
static std::vector<SyntaxTestError> parseExpectations(std::istream& _stream); static std::vector<SyntaxTestError> parseExpectations(std::istream& _stream);
std::map<std::string, std::string> m_sources; frontend::test::SourceMap m_sources;
std::vector<SyntaxTestError> m_expectations; std::vector<SyntaxTestError> m_expectations;
std::vector<SyntaxTestError> m_errorList; std::vector<SyntaxTestError> m_errorList;
langutil::EVMVersion const m_evmVersion; langutil::EVMVersion const m_evmVersion;

View File

@ -60,7 +60,8 @@ public:
u256 const& _value = 0, u256 const& _value = 0,
std::string const& _contractName = "", std::string const& _contractName = "",
bytes const& _arguments = {}, bytes const& _arguments = {},
std::map<std::string, util::h160> const& _libraryAddresses = {} std::map<std::string, util::h160> const& _libraryAddresses = {},
std::optional<std::string> const& _sourceName = std::nullopt
) = 0; ) = 0;
bytes const& compileAndRun( bytes const& compileAndRun(

View File

@ -18,24 +18,29 @@
#include <test/TestCaseReader.h> #include <test/TestCaseReader.h>
#include <libsolidity/parsing/Parser.h>
#include <libsolutil/StringUtils.h> #include <libsolutil/StringUtils.h>
#include <libsolutil/CommonIO.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/throw_exception.hpp> #include <boost/throw_exception.hpp>
#include <boost/filesystem.hpp>
#include <range/v3/view/map.hpp> #include <range/v3/view/map.hpp>
using namespace std; using namespace std;
using namespace solidity::langutil;
using namespace solidity::frontend::test; using namespace solidity::frontend::test;
TestCaseReader::TestCaseReader(string const& _filename): namespace fs = boost::filesystem;
m_file(_filename)
{
if (!m_file)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open file: \"" + _filename + "\"."));
m_file.exceptions(ios::badbit);
tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber(m_file); TestCaseReader::TestCaseReader(string const& _filename): m_fileStream(_filename), m_fileName(_filename)
{
if (!m_fileStream)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open file: \"" + _filename + "\"."));
m_fileStream.exceptions(ios::badbit);
tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber(m_fileStream);
m_unreadSettings = m_settings; m_unreadSettings = m_settings;
} }
@ -55,7 +60,7 @@ string const& TestCaseReader::source() const
string TestCaseReader::simpleExpectations() string TestCaseReader::simpleExpectations()
{ {
return parseSimpleExpectations(m_file); return parseSimpleExpectations(m_fileStream);
} }
bool TestCaseReader::boolSetting(std::string const& _name, bool _defaultValue) bool TestCaseReader::boolSetting(std::string const& _name, bool _defaultValue)
@ -105,10 +110,12 @@ void TestCaseReader::ensureAllSettingsRead() const
pair<SourceMap, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(istream& _stream) pair<SourceMap, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(istream& _stream)
{ {
map<string, string> sources; map<string, string> sources;
map<string, boost::filesystem::path> externalSources;
string currentSourceName; string currentSourceName;
string currentSource; string currentSource;
string line; string line;
size_t lineNumber = 1; size_t lineNumber = 1;
static string const externalSourceDelimiterStart("==== ExternalSource:");
static string const sourceDelimiterStart("==== Source:"); static string const sourceDelimiterStart("==== Source:");
static string const sourceDelimiterEnd("===="); static string const sourceDelimiterEnd("====");
static string const comment("// "); static string const comment("// ");
@ -137,6 +144,41 @@ pair<SourceMap, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(is
if (sources.count(currentSourceName)) if (sources.count(currentSourceName))
BOOST_THROW_EXCEPTION(runtime_error("Multiple definitions of test source \"" + currentSourceName + "\".")); BOOST_THROW_EXCEPTION(runtime_error("Multiple definitions of test source \"" + currentSourceName + "\"."));
} }
else if (boost::algorithm::starts_with(line, externalSourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd))
{
string externalSourceString = boost::trim_copy(line.substr(
externalSourceDelimiterStart.size(),
line.size() - sourceDelimiterEnd.size() - externalSourceDelimiterStart.size()
));
string externalSourceName;
size_t remappingPos = externalSourceString.find('=');
// Does the external source define a remapping?
if (remappingPos != string::npos)
{
externalSourceName = boost::trim_copy(externalSourceString.substr(0, remappingPos));
externalSourceString = boost::trim_copy(externalSourceString.substr(remappingPos + 1));
}
else
externalSourceName = externalSourceString;
solAssert(!externalSourceName.empty(), "");
fs::path externalSourceTarget(externalSourceString);
fs::path testCaseParentDir = m_fileName.parent_path();
if (!externalSourceTarget.is_relative())
BOOST_THROW_EXCEPTION(runtime_error("External Source paths need to be relative to the location of the test case."));
fs::path externalSourceFullPath = testCaseParentDir / externalSourceTarget;
string externalSourceContent;
if (!fs::exists(externalSourceFullPath))
BOOST_THROW_EXCEPTION(runtime_error("External Source '" + externalSourceTarget.string() + "' not found."));
else
externalSourceContent = util::readFileAsString(externalSourceFullPath.string());
if (sources.count(externalSourceName))
BOOST_THROW_EXCEPTION(runtime_error("Multiple definitions of test source \"" + externalSourceName + "\"."));
sources[externalSourceName] = externalSourceContent;
externalSources[externalSourceName] = externalSourceTarget;
}
else else
currentSource += line + "\n"; currentSource += line + "\n";
} }
@ -156,7 +198,7 @@ pair<SourceMap, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(is
} }
// Register the last source as the main one // Register the last source as the main one
sources[currentSourceName] = currentSource; sources[currentSourceName] = currentSource;
return {{move(sources), move(currentSourceName)}, lineNumber}; return {{move(sources), move(externalSources), move(currentSourceName)}, lineNumber};
} }
string TestCaseReader::parseSimpleExpectations(istream& _file) string TestCaseReader::parseSimpleExpectations(istream& _file)

View File

@ -16,6 +16,7 @@
*/ */
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
#include <boost/filesystem.hpp>
#include <fstream> #include <fstream>
#include <map> #include <map>
#include <string> #include <string>
@ -31,6 +32,7 @@ namespace solidity::frontend::test
struct SourceMap struct SourceMap
{ {
std::map<std::string, std::string> sources; std::map<std::string, std::string> sources;
std::map<std::string, boost::filesystem::path> externalSources;
std::string mainSourceFile; std::string mainSourceFile;
}; };
@ -48,7 +50,7 @@ public:
std::string const& source() const; std::string const& source() const;
std::size_t lineNumber() const { return m_lineNumber; } std::size_t lineNumber() const { return m_lineNumber; }
std::map<std::string, std::string> const& settings() const { return m_settings; } std::map<std::string, std::string> const& settings() const { return m_settings; }
std::ifstream& stream() { return m_file; } std::ifstream& stream() { return m_fileStream; }
std::string simpleExpectations(); std::string simpleExpectations();
@ -62,7 +64,8 @@ private:
std::pair<SourceMap, std::size_t> parseSourcesAndSettingsWithLineNumber(std::istream& _file); std::pair<SourceMap, std::size_t> parseSourcesAndSettingsWithLineNumber(std::istream& _file);
static std::string parseSimpleExpectations(std::istream& _file); static std::string parseSimpleExpectations(std::istream& _file);
std::ifstream m_file; std::ifstream m_fileStream;
boost::filesystem::path m_fileName;
SourceMap m_sources; SourceMap m_sources;
std::size_t m_lineNumber = 0; std::size_t m_lineNumber = 0;
std::map<std::string, std::string> m_settings; std::map<std::string, std::string> m_settings;

View File

@ -86,7 +86,10 @@ int registerTests(
fs::directory_iterator(fullpath), fs::directory_iterator(fullpath),
fs::directory_iterator() fs::directory_iterator()
)) ))
if (fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename())) if (
solidity::test::isValidSemanticTestPath(entry) &&
(fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename()))
)
numTestsAdded += registerTests( numTestsAdded += registerTests(
*sub_suite, *sub_suite,
_basepath, _path / entry.path().filename(), _basepath, _path / entry.path().filename(),

View File

@ -256,7 +256,7 @@ TestCase::TestResult SemanticTest::runTest(
{ {
soltestAssert( soltestAssert(
m_allowNonExistingFunctions || m_allowNonExistingFunctions ||
m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature), m_compiler.methodIdentifiers(m_compiler.lastContractName(m_sources.mainSourceFile)).isMember(test.call().signature),
"The function " + test.call().signature + " is not known to the compiler" "The function " + test.call().signature + " is not known to the compiler"
); );
@ -283,7 +283,7 @@ TestCase::TestResult SemanticTest::runTest(
test.setFailure(!m_transactionSuccessful); test.setFailure(!m_transactionSuccessful);
test.setRawBytes(std::move(output)); test.setRawBytes(std::move(output));
test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName())); test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName(m_sources.mainSourceFile)));
} }
} }
@ -379,42 +379,62 @@ void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool
if (m_sources.sources.empty()) if (m_sources.sources.empty())
return; return;
bool outputNames = (m_sources.sources.size() != 1 || !m_sources.sources.begin()->first.empty()); bool outputNames = (m_sources.sources.size() - m_sources.externalSources.size() != 1 || !m_sources.sources.begin()->first.empty());
set<string> externals;
for (auto const& [name, path]: m_sources.externalSources)
{
externals.insert(name);
string externalSource;
if (name == path)
externalSource = name;
else
externalSource = name + "=" + path.generic_string();
if (_formatted)
_stream << _linePrefix << formatting::CYAN << "==== ExternalSource: " << externalSource << " ===="s << formatting::RESET << endl;
else
_stream << _linePrefix << "==== ExternalSource: " << externalSource << " ===="s << endl;
}
for (auto const& [name, source]: m_sources.sources) for (auto const& [name, source]: m_sources.sources)
if (_formatted) if (externals.find(name) == externals.end())
{ {
if (source.empty()) if (_formatted)
continue;
if (outputNames)
_stream << _linePrefix << formatting::CYAN << "==== Source: " << name << " ====" << formatting::RESET << endl;
vector<char const*> sourceFormatting(source.length(), formatting::RESET);
_stream << _linePrefix << sourceFormatting.front() << source.front();
for (size_t i = 1; i < source.length(); i++)
{ {
if (sourceFormatting[i] != sourceFormatting[i - 1]) if (source.empty())
_stream << sourceFormatting[i]; continue;
if (source[i] != '\n')
_stream << source[i]; if (outputNames)
else _stream << _linePrefix << formatting::CYAN << "==== Source: " << name
<< " ====" << formatting::RESET << endl;
vector<char const*> sourceFormatting(source.length(), formatting::RESET);
_stream << _linePrefix << sourceFormatting.front() << source.front();
for (size_t i = 1; i < source.length(); i++)
{ {
_stream << formatting::RESET << endl; if (sourceFormatting[i] != sourceFormatting[i - 1])
if (i + 1 < source.length()) _stream << sourceFormatting[i];
_stream << _linePrefix << sourceFormatting[i]; if (source[i] != '\n')
_stream << source[i];
else
{
_stream << formatting::RESET << endl;
if (i + 1 < source.length())
_stream << _linePrefix << sourceFormatting[i];
}
} }
_stream << formatting::RESET;
}
else
{
if (outputNames)
_stream << _linePrefix << "==== Source: " + name << " ====" << endl;
stringstream stream(source);
string line;
while (getline(stream, line))
_stream << _linePrefix << line << endl;
} }
_stream << formatting::RESET;
}
else
{
if (outputNames)
_stream << _linePrefix << "==== Source: " + name << " ====" << endl;
stringstream stream(source);
string line;
while (getline(stream, line))
_stream << _linePrefix << line << endl;
} }
} }
@ -455,6 +475,6 @@ bool SemanticTest::deploy(
map<string, solidity::test::Address> const& _libraries map<string, solidity::test::Address> const& _libraries
) )
{ {
auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries); auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries, m_sources.mainSourceFile);
return !output.empty() && m_transactionSuccessful; return !output.empty() && m_transactionSuccessful;
} }

View File

@ -36,10 +36,13 @@ using namespace std;
bytes SolidityExecutionFramework::multiSourceCompileContract( bytes SolidityExecutionFramework::multiSourceCompileContract(
map<string, string> const& _sourceCode, map<string, string> const& _sourceCode,
optional<string> const& _mainSourceName,
string const& _contractName, string const& _contractName,
map<string, Address> const& _libraryAddresses map<string, Address> const& _libraryAddresses
) )
{ {
if (_mainSourceName.has_value())
solAssert(_sourceCode.find(_mainSourceName.value()) != _sourceCode.end(), "");
map<string, string> sourcesWithPreamble = _sourceCode; map<string, string> sourcesWithPreamble = _sourceCode;
for (auto& entry: sourcesWithPreamble) for (auto& entry: sourcesWithPreamble)
entry.second = addPreamble(entry.second); entry.second = addPreamble(entry.second);
@ -68,7 +71,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
formatter.printErrorInformation(*error); formatter.printErrorInformation(*error);
BOOST_ERROR("Compiling contract failed"); BOOST_ERROR("Compiling contract failed");
} }
std::string contractName(_contractName.empty() ? m_compiler.lastContractName() : _contractName); string contractName(_contractName.empty() ? m_compiler.lastContractName(_mainSourceName) : _contractName);
evmasm::LinkerObject obj; evmasm::LinkerObject obj;
if (m_compileViaYul) if (m_compileViaYul)
{ {
@ -98,7 +101,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
try try
{ {
asmStack.optimize(); asmStack.optimize();
obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode); obj = move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode);
obj.link(_libraryAddresses); obj.link(_libraryAddresses);
break; break;
} }
@ -126,6 +129,7 @@ bytes SolidityExecutionFramework::compileContract(
{ {
return multiSourceCompileContract( return multiSourceCompileContract(
{{"", _sourceCode}}, {{"", _sourceCode}},
nullopt,
_contractName, _contractName,
_libraryAddresses _libraryAddresses
); );

View File

@ -49,10 +49,11 @@ public:
u256 const& _value = 0, u256 const& _value = 0,
std::string const& _contractName = "", std::string const& _contractName = "",
bytes const& _arguments = {}, bytes const& _arguments = {},
std::map<std::string, solidity::test::Address> const& _libraryAddresses = {} std::map<std::string, solidity::test::Address> const& _libraryAddresses = {},
std::optional<std::string> const& _sourceName = std::nullopt
) override ) override
{ {
bytes bytecode = multiSourceCompileContract(_sourceCode, _contractName, _libraryAddresses); bytes bytecode = multiSourceCompileContract(_sourceCode, _sourceName, _contractName, _libraryAddresses);
sendMessage(bytecode + _arguments, true, _value); sendMessage(bytecode + _arguments, true, _value);
return m_output; return m_output;
} }
@ -65,6 +66,7 @@ public:
bytes multiSourceCompileContract( bytes multiSourceCompileContract(
std::map<std::string, std::string> const& _sources, std::map<std::string, std::string> const& _sources,
std::optional<std::string> const& _mainSourceName = std::nullopt,
std::string const& _contractName = "", std::string const& _contractName = "",
std::map<std::string, solidity::test::Address> const& _libraryAddresses = {} std::map<std::string, solidity::test::Address> const& _libraryAddresses = {}
); );

View File

@ -65,7 +65,7 @@ string SyntaxTest::addPreamble(string const& _sourceCode)
void SyntaxTest::setupCompiler() void SyntaxTest::setupCompiler()
{ {
compiler().reset(); compiler().reset();
auto sourcesWithPragma = m_sources; auto sourcesWithPragma = m_sources.sources;
for (auto& source: sourcesWithPragma) for (auto& source: sourcesWithPragma)
source.second = addPreamble(source.second); source.second = addPreamble(source.second);
compiler().setSources(sourcesWithPragma); compiler().setSources(sourcesWithPragma);
@ -122,8 +122,8 @@ void SyntaxTest::filterObtainedErrors()
solAssert(location->source, ""); solAssert(location->source, "");
sourceName = location->source->name(); sourceName = location->source->name();
solAssert(m_sources.count(sourceName) == 1, ""); solAssert(m_sources.sources.count(sourceName) == 1, "");
int preambleSize = static_cast<int>(location->source->source().size()) - static_cast<int>(m_sources[sourceName].size()); int preambleSize = static_cast<int>(location->source->source().size()) - static_cast<int>(m_sources.sources[sourceName].size());
solAssert(preambleSize >= 0, ""); solAssert(preambleSize >= 0, "");
// ignore the version & license pragma inserted by the testing tool when calculating locations. // ignore the version & license pragma inserted by the testing tool when calculating locations.

View File

@ -0,0 +1,2 @@
contract External {
}

View File

@ -0,0 +1,2 @@
contract External {
}

View File

@ -0,0 +1,2 @@
import "external.sol";
import "other_external.sol";

View File

@ -0,0 +1 @@
import "subdir/import.sol";

View File

@ -0,0 +1,2 @@
contract OtherExternal {
}

View File

@ -0,0 +1 @@
import "sub_external.sol";

View File

@ -0,0 +1,2 @@
contract SubExternal {
}

View File

@ -0,0 +1,2 @@
contract A {
}

View File

@ -0,0 +1,2 @@
contract C {
}

View File

@ -0,0 +1,2 @@
contract D {
}

View File

@ -0,0 +1,2 @@
contract D {
}

View File

@ -0,0 +1,2 @@
contract C {
}

View File

@ -0,0 +1,3 @@
import {C} from "../../c.sol";
contract B {
}

View File

@ -0,0 +1,3 @@
import {B} from "../B/b.sol";
contract G {
}

View File

@ -0,0 +1,2 @@
contract A {
}

View File

@ -0,0 +1,8 @@
import {A} from "./a.sol";
import {B} from "./B/b.sol";
import {C} from "../c.sol";
import {D} from "../D/d.sol";
import {G} from "./E/../F/../G/./g.sol";
import {H} from "../../../../_relative_imports/h.sol";
contract Contract {
}

View File

@ -0,0 +1,2 @@
contract H {
}

View File

@ -0,0 +1,2 @@
contract B {
}

View File

@ -0,0 +1,2 @@
import {A} from "./a.sol";
import {B} from "../b.sol";

View File

@ -0,0 +1,2 @@
contract Dot_A {
}

View File

@ -0,0 +1,2 @@
contract Dot_Dot_B {
}

View File

@ -0,0 +1,8 @@
==== ExternalSource: a=_external/external.sol=sol ====
import {External} from "a";
contract C {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,10 @@
==== ExternalSource: _external/external.sol ====
==== ExternalSource: _external/other_external.sol ====
import {External} from "_external/external.sol";
import {OtherExternal} from "_external/other_external.sol";
contract C {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,14 @@
==== ExternalSource: _external/external.sol ====
==== Source: s1.sol ====
import {External} from "_external/external.sol";
contract S1 {
}
==== Source: s2.sol ====
import {S1} from "s1.sol";
contract C {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,12 @@
==== ExternalSource: _non_normalized_paths//a.sol ====
==== ExternalSource: C/////c.sol=_non_normalized_paths/c.sol ====
==== ExternalSource: C/../////D/d.sol=_non_normalized_paths///d.sol ====
import {A} from "_non_normalized_paths//a.sol";
import {C} from "C/////c.sol";
import {D} from "C/../////D/d.sol";
contract Contract {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,14 @@
==== ExternalSource: _relative_imports/dir/contract.sol ====
==== ExternalSource: _relative_imports/dir/a.sol ====
==== ExternalSource: _relative_imports/dir/B/b.sol ====
==== ExternalSource: _relative_imports/c.sol ====
==== ExternalSource: _relative_imports/D/d.sol ====
==== ExternalSource: _relative_imports/dir/G/g.sol ====
==== ExternalSource: _relative_imports/h.sol ====
import {A, B, C, D, G, H, Contract} from "_relative_imports/dir/contract.sol";
contract CC {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,8 @@
==== ExternalSource: _external/external.sol ====
import {External} from "_external/external.sol";
contract C {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,10 @@
==== ExternalSource: _external/external.sol ====
==== ExternalSource: _external/other_external.sol ====
import {External} from "_external/external.sol";
import {OtherExternal} from "_external/other_external.sol";
contract C {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,10 @@
==== ExternalSource: _external/import_with_subdir.sol ====
==== ExternalSource: subdir/import.sol=_external/subdir/import.sol ====
==== ExternalSource: sub_external.sol=_external/subdir/sub_external.sol ====
import {SubExternal} from "sub_external.sol";
contract C {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,12 @@
==== ExternalSource: ./a.sol=_source_name_starting_with_dots/dot_a.sol ====
==== ExternalSource: ../b.sol=_source_name_starting_with_dots/dot_dot_b.sol ====
==== ExternalSource: _source_name_starting_with_dots/dir/a.sol ====
==== ExternalSource: _source_name_starting_with_dots/b.sol ====
==== ExternalSource: _source_name_starting_with_dots/dir/contract.sol ====
import {A, B} from "_source_name_starting_with_dots/dir/contract.sol";
contract Contract {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,12 @@
==== ExternalSource: ExtSource.sol=_external/external.sol ====
==== ExternalSource: /ExtSource.sol=_external/other_external.sol ====
import "ExtSource.sol";
import "/ExtSource.sol";
contract C {
External _external;
OtherExternal _otherExternal;
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -25,6 +25,7 @@
#include <test/libyul/Common.h> #include <test/libyul/Common.h>
#include <test/libyul/SyntaxTest.h> #include <test/libyul/SyntaxTest.h>
#include <test/TestCaseReader.h>
#include <test/libsolidity/util/SoltestErrors.h> #include <test/libsolidity/util/SoltestErrors.h>
@ -35,14 +36,15 @@ using namespace solidity;
using namespace solidity::util; using namespace solidity::util;
using namespace solidity::langutil; using namespace solidity::langutil;
using namespace solidity::yul::test; using namespace solidity::yul::test;
using namespace solidity::frontend::test;
void SyntaxTest::parseAndAnalyze() void SyntaxTest::parseAndAnalyze()
{ {
if (m_sources.size() != 1) if (m_sources.sources.size() != 1)
BOOST_THROW_EXCEPTION(runtime_error{"Expected only one source for yul test."}); BOOST_THROW_EXCEPTION(runtime_error{"Expected only one source for yul test."});
string const& name = m_sources.begin()->first; string const& name = m_sources.sources.begin()->first;
string const& source = m_sources.begin()->second; string const& source = m_sources.sources.begin()->second;
ErrorList errorList{}; ErrorList errorList{};
soltestAssert(m_dialect, ""); soltestAssert(m_dialect, "");

View File

@ -79,9 +79,9 @@ public:
m_filterExpression = regex{"(" + filter + "(\\.sol|\\.yul))"}; m_filterExpression = regex{"(" + filter + "(\\.sol|\\.yul))"};
} }
bool matches(string const& _name) const bool matches(fs::path const& _path, string const& _name) const
{ {
return regex_match(_name, m_filterExpression); return regex_match(_name, m_filterExpression) && solidity::test::isValidSemanticTestPath(_path);
} }
private: private:
@ -154,7 +154,7 @@ TestTool::Result TestTool::process()
try try
{ {
if (m_filter.matches(m_name)) if (m_filter.matches(m_path, m_name))
{ {
(AnsiColorized(cout, formatted, {BOLD}) << m_name << ": ").flush(); (AnsiColorized(cout, formatted, {BOLD}) << m_name << ": ").flush();