Introduced TestCaseReader.

This commit is contained in:
a3d4 2020-03-06 01:22:51 +01:00
parent e5a49e2556
commit 66783c30ce
24 changed files with 348 additions and 346 deletions

View File

@ -54,7 +54,7 @@ inline std::optional<RevertStrings> revertStringsFromString(std::string const& _
for (auto i: {RevertStrings::Default, RevertStrings::Strip, RevertStrings::Debug, RevertStrings::VerboseDebug})
if (revertStringsToString(i) == _str)
return i;
return {};
return std::nullopt;
}
}

View File

@ -13,6 +13,8 @@ set(sources
Metadata.h
TestCase.cpp
TestCase.h
TestCaseReader.cpp
TestCaseReader.h
)
detect_stray_source_files("${sources}" ".")

View File

@ -56,16 +56,12 @@ int parseUnsignedInteger(string::iterator& _it, string::iterator _end)
}
CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion)
CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion):
EVMVersionRestrictedTestCase(_filename),
m_evmVersion(_evmVersion)
{
ifstream file(_filename);
if (!file)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\"."));
file.exceptions(ios::badbit);
m_sources = parseSourcesAndSettings(file);
m_expectations = parseExpectations(file);
m_sources = m_reader.sources();
m_expectations = parseExpectations(m_reader.stream());
}
TestCase::TestResult CommonSyntaxTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)

View File

@ -18,16 +18,9 @@
#include <test/Common.h>
#include <test/TestCase.h>
#include <libsolutil/StringUtils.h>
#include <boost/algorithm/cxx11/none_of.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/range/adaptor/map.hpp>
#include <stdexcept>
#include <iostream>
using namespace std;
@ -37,11 +30,12 @@ using namespace solidity::frontend::test;
void TestCase::printUpdatedSettings(ostream& _stream, const string& _linePrefix, const bool)
{
if (m_validatedSettings.empty())
auto& settings = m_reader.settings();
if (settings.empty())
return;
_stream << _linePrefix << "// ====" << endl;
for (auto const& setting: m_validatedSettings)
for (auto const& setting: settings)
_stream << _linePrefix << "// " << setting.first << ": " << setting.second << endl;
}
@ -53,108 +47,12 @@ bool TestCase::isTestFilename(boost::filesystem::path const& _filename)
!boost::starts_with(_filename.string(), ".");
}
void TestCase::validateSettings()
{
if (!m_settings.empty())
throw runtime_error(
"Unknown setting(s): " +
util::joinHumanReadable(m_settings | boost::adaptors::map_keys)
);
}
bool TestCase::shouldRun()
{
m_reader.ensureAllSettingsRead();
return m_shouldRun;
}
pair<map<string, string>, size_t> TestCase::parseSourcesAndSettingsWithLineNumbers(istream& _stream)
{
map<string, string> sources;
string currentSourceName;
string currentSource;
string line;
size_t lineNumber = 1;
static string const sourceDelimiterStart("==== Source:");
static string const sourceDelimiterEnd("====");
static string const comment("// ");
static string const settingsDelimiter("// ====");
static string const delimiter("// ----");
bool sourcePart = true;
while (getline(_stream, line))
{
lineNumber++;
if (boost::algorithm::starts_with(line, delimiter))
break;
else if (boost::algorithm::starts_with(line, settingsDelimiter))
sourcePart = false;
else if (sourcePart)
{
if (boost::algorithm::starts_with(line, sourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd))
{
if (!(currentSourceName.empty() && currentSource.empty()))
sources[currentSourceName] = std::move(currentSource);
currentSource = {};
currentSourceName = boost::trim_copy(line.substr(
sourceDelimiterStart.size(),
line.size() - sourceDelimiterEnd.size() - sourceDelimiterStart.size()
));
if (sources.count(currentSourceName))
throw runtime_error("Multiple definitions of test source \"" + currentSourceName + "\".");
}
else
currentSource += line + "\n";
}
else if (boost::algorithm::starts_with(line, comment))
{
size_t colon = line.find(':');
if (colon == string::npos)
throw runtime_error(string("Expected \":\" inside setting."));
string key = line.substr(comment.size(), colon - comment.size());
string value = line.substr(colon + 1);
boost::algorithm::trim(key);
boost::algorithm::trim(value);
m_settings[key] = value;
}
else
throw runtime_error(string("Expected \"//\" or \"// ---\" to terminate settings and source."));
}
sources[currentSourceName] = currentSource;
return {sources, lineNumber};
}
map<string, string> TestCase::parseSourcesAndSettings(istream& _stream)
{
return get<0>(parseSourcesAndSettingsWithLineNumbers(_stream));
}
pair<string, size_t> TestCase::parseSourceAndSettingsWithLineNumbers(istream& _stream)
{
auto [sourceMap, lineOffset] = parseSourcesAndSettingsWithLineNumbers(_stream);
if (sourceMap.size() != 1)
BOOST_THROW_EXCEPTION(runtime_error("Expected single source definition, but got multiple sources."));
return {std::move(sourceMap.begin()->second), lineOffset};
}
string TestCase::parseSourceAndSettings(istream& _stream)
{
return parseSourceAndSettingsWithLineNumbers(_stream).first;
}
string TestCase::parseSimpleExpectations(std::istream& _file)
{
string result;
string line;
while (getline(_file, line))
if (boost::algorithm::starts_with(line, "// "))
result += line.substr(3) + "\n";
else if (line == "//")
result += "\n";
else
BOOST_THROW_EXCEPTION(runtime_error("Test expectations must start with \"// \"."));
return result;
}
void TestCase::expect(string::iterator& _it, string::iterator _end, string::value_type _c)
{
if (_it == _end || *_it != _c)
@ -162,19 +60,13 @@ void TestCase::expect(string::iterator& _it, string::iterator _end, string::valu
++_it;
}
void EVMVersionRestrictedTestCase::validateSettings()
EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(string const& _filename):
TestCase(_filename)
{
if (!m_settings.count("EVMVersion"))
if (!m_reader.hasSetting("EVMVersion"))
return;
string versionString = m_settings["EVMVersion"];
m_validatedSettings["EVMVersion"] = versionString;
m_settings.erase("EVMVersion");
TestCase::validateSettings();
if (versionString.empty())
return;
string versionString = m_reader.stringSetting("EVMVersion", "");
string comparator;
size_t versionBegin = 0;

View File

@ -17,16 +17,13 @@
#pragma once
#include <test/TestCaseReader.h>
#include <liblangutil/EVMVersion.h>
#include <boost/filesystem.hpp>
#include <functional>
#include <iosfwd>
#include <memory>
#include <string>
#include <vector>
#include <map>
namespace solidity::frontend::test
{
@ -68,23 +65,19 @@ public:
static bool isTestFilename(boost::filesystem::path const& _filename);
/// Validates the settings, i.e. moves them from m_settings to m_validatedSettings.
/// Throws a runtime exception if any setting is left at this class (i.e. unknown setting).
virtual void validateSettings();
/// Returns true, if the test case is supported in the current environment and false
/// otherwise which causes this test to be skipped.
/// This might check e.g. for restrictions on the EVM version.
/// The function throws an exception if there are unread settings.
bool shouldRun();
protected:
std::pair<std::map<std::string, std::string>, std::size_t> parseSourcesAndSettingsWithLineNumbers(std::istream& _file);
std::map<std::string, std::string> parseSourcesAndSettings(std::istream& _file);
std::pair<std::string, std::size_t> parseSourceAndSettingsWithLineNumbers(std::istream& _file);
std::string parseSourceAndSettings(std::istream& _file);
static void expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c);
// Used by ASTJSONTest, the only TestCase class with a custom parser of the test files.
TestCase() = default;
static std::string parseSimpleExpectations(std::istream& _file);
TestCase(std::string const& _filename): m_reader(_filename) {}
static void expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c);
template<typename IteratorType>
static void skipWhitespace(IteratorType& _it, IteratorType _end)
@ -100,18 +93,14 @@ protected:
++_it;
}
/// Parsed settings.
std::map<std::string, std::string> m_settings;
/// Updated settings after validation.
std::map<std::string, std::string> m_validatedSettings;
TestCaseReader m_reader;
bool m_shouldRun = true;
};
class EVMVersionRestrictedTestCase: public TestCase
{
public:
void validateSettings() override;
protected:
EVMVersionRestrictedTestCase(std::string const& _filename);
};
}

174
test/TestCaseReader.cpp Normal file
View File

@ -0,0 +1,174 @@
/*
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/>.
*/
#include <test/TestCaseReader.h>
#include <libsolutil/StringUtils.h>
#include <boost/algorithm/string.hpp>
#include <boost/range/adaptor/map.hpp>
#include <boost/throw_exception.hpp>
using namespace std;
using namespace solidity::frontend::test;
TestCaseReader::TestCaseReader(string const& _filename):
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);
m_unreadSettings = m_settings;
}
string const& TestCaseReader::source()
{
if (m_sources.size() != 1)
BOOST_THROW_EXCEPTION(runtime_error("Expected single source definition, but got multiple sources."));
return m_sources.begin()->second;
}
string TestCaseReader::simpleExpectations()
{
return parseSimpleExpectations(m_file);
}
bool TestCaseReader::hasSetting(std::string const& _name) const
{
return m_settings.count(_name) != 0;
}
bool TestCaseReader::boolSetting(std::string const& _name, bool _defaultValue)
{
if (!hasSetting(_name))
return _defaultValue;
m_unreadSettings.erase(_name);
string value = m_settings.at(_name);
if (value == "false")
return false;
if (value == "true")
return true;
BOOST_THROW_EXCEPTION(runtime_error("Invalid Boolean value: " + value + "."));
}
size_t TestCaseReader::sizetSetting(std::string const& _name, size_t _defaultValue)
{
if (!hasSetting(_name))
return _defaultValue;
m_unreadSettings.erase(_name);
static_assert(sizeof(unsigned long) <= sizeof(size_t));
return stoul(m_settings.at(_name));
}
string TestCaseReader::stringSetting(string const& _name, string const& _defaultValue)
{
if (!hasSetting(_name))
return _defaultValue;
m_unreadSettings.erase(_name);
return m_settings.at(_name);
}
void TestCaseReader::setSetting(std::string const& _name, std::string const& _value)
{
m_settings[_name] = _value;
}
void TestCaseReader::ensureAllSettingsRead() const
{
if (!m_unreadSettings.empty())
throw runtime_error(
"Unknown setting(s): " +
util::joinHumanReadable(m_unreadSettings | boost::adaptors::map_keys)
);
}
pair<map<string, string>, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(istream& _stream)
{
map<string, string> sources;
string currentSourceName;
string currentSource;
string line;
size_t lineNumber = 1;
static string const sourceDelimiterStart("==== Source:");
static string const sourceDelimiterEnd("====");
static string const comment("// ");
static string const settingsDelimiter("// ====");
static string const delimiter("// ----");
bool sourcePart = true;
while (getline(_stream, line))
{
lineNumber++;
if (boost::algorithm::starts_with(line, delimiter))
break;
else if (boost::algorithm::starts_with(line, settingsDelimiter))
sourcePart = false;
else if (sourcePart)
{
if (boost::algorithm::starts_with(line, sourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd))
{
if (!(currentSourceName.empty() && currentSource.empty()))
sources[currentSourceName] = std::move(currentSource);
currentSource = {};
currentSourceName = boost::trim_copy(line.substr(
sourceDelimiterStart.size(),
line.size() - sourceDelimiterEnd.size() - sourceDelimiterStart.size()
));
if (sources.count(currentSourceName))
throw runtime_error("Multiple definitions of test source \"" + currentSourceName + "\".");
}
else
currentSource += line + "\n";
}
else if (boost::algorithm::starts_with(line, comment))
{
size_t colon = line.find(':');
if (colon == string::npos)
throw runtime_error(string("Expected \":\" inside setting."));
string key = line.substr(comment.size(), colon - comment.size());
string value = line.substr(colon + 1);
boost::algorithm::trim(key);
boost::algorithm::trim(value);
m_settings[key] = value;
}
else
throw runtime_error(string("Expected \"//\" or \"// ---\" to terminate settings and source."));
}
sources[currentSourceName] = currentSource;
return { sources, lineNumber };
}
string TestCaseReader::parseSimpleExpectations(istream& _file)
{
string result;
string line;
while (getline(_file, line))
if (boost::algorithm::starts_with(line, "// "))
result += line.substr(3) + "\n";
else if (line == "//")
result += "\n";
else
BOOST_THROW_EXCEPTION(runtime_error("Test expectations must start with \"// \"."));
return result;
}

61
test/TestCaseReader.h Normal file
View File

@ -0,0 +1,61 @@
/*
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/>.
*/
#include <fstream>
#include <map>
#include <string>
#pragma once
namespace solidity::frontend::test
{
/**
* A reader for test case data file, which parses source, settings and (optionally) simple expectations.
*/
class TestCaseReader
{
public:
TestCaseReader() = default;
explicit TestCaseReader(std::string const& _filename);
std::map<std::string, std::string> const& sources() { return m_sources; }
std::string const& source();
std::size_t lineNumber() { return m_lineNumber; }
std::map<std::string, std::string> const& settings() { return m_settings; }
std::ifstream& stream() { return m_file; }
std::string simpleExpectations();
bool hasSetting(std::string const& _name) const;
bool boolSetting(std::string const& _name, bool _defaultValue);
size_t sizetSetting(std::string const& _name, size_t _defaultValue);
std::string stringSetting(std::string const& _name, std::string const& _defaultValue);
void setSetting(std::string const& _name, std::string const& _value);
void ensureAllSettingsRead() const;
private:
std::pair<std::map<std::string, std::string>, std::size_t> parseSourcesAndSettingsWithLineNumber(std::istream& _file);
static std::string parseSimpleExpectations(std::istream& _file);
std::ifstream m_file;
std::map<std::string, std::string> m_sources;
std::size_t m_lineNumber = 0;
std::map<std::string, std::string> m_settings;
std::map<std::string, std::string> m_unreadSettings; ///< tracks which settings are left unread
};
}

View File

@ -94,7 +94,6 @@ int registerTests(
{
stringstream errorStream;
auto testCase = _testCaseCreator(config);
testCase->validateSettings();
if (testCase->shouldRun())
switch (testCase->run(errorStream))
{

View File

@ -36,15 +36,11 @@ using namespace solidity::util;
using namespace solidity::frontend;
using namespace solidity::frontend::test;
ABIJsonTest::ABIJsonTest(string const& _filename)
ABIJsonTest::ABIJsonTest(string const& _filename):
TestCase(_filename)
{
ifstream file(_filename);
if (!file)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\"."));
file.exceptions(ios::badbit);
m_source = parseSourceAndSettings(file);
m_expectation = parseSimpleExpectations(file);
m_source = m_reader.source();
m_expectation = m_reader.simpleExpectations();
}
TestCase::TestResult ABIJsonTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)

View File

@ -36,35 +36,14 @@ using namespace std;
namespace fs = boost::filesystem;
using namespace boost::unit_test;
GasTest::GasTest(string const& _filename)
GasTest::GasTest(string const& _filename):
TestCase(_filename)
{
ifstream file(_filename);
if (!file)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\"."));
file.exceptions(ios::badbit);
m_source = parseSourceAndSettings(file);
if (m_settings.count("optimize"))
{
m_optimise = true;
m_validatedSettings["optimize"] = "true";
m_settings.erase("optimize");
}
if (m_settings.count("optimize-yul"))
{
m_optimiseYul = true;
m_validatedSettings["optimize-yul"] = "true";
m_settings.erase("optimize-yul");
}
if (m_settings.count("optimize-runs"))
{
m_optimiseRuns = stoul(m_settings["optimize-runs"]);
m_validatedSettings["optimize-runs"] = m_settings["optimize-runs"];
m_settings.erase("optimize-runs");
}
parseExpectations(file);
m_source = m_reader.source();
m_optimise = m_reader.boolSetting("optimize", false);
m_optimiseYul = m_reader.boolSetting("optimize-yul", false);
m_optimiseRuns = m_reader.sizetSetting("optimize-runs", 200);
parseExpectations(m_reader.stream());
}
void GasTest::parseExpectations(std::istream& _stream)

View File

@ -28,9 +28,7 @@ using namespace solidity::frontend::test;
SMTCheckerTest::SMTCheckerTest(string const& _filename, langutil::EVMVersion _evmVersion): SyntaxTest(_filename, _evmVersion)
{
if (m_settings.count("SMTSolvers"))
{
auto const& choice = m_settings.at("SMTSolvers");
auto const& choice = m_reader.stringSetting("SMTSolvers", "any");
if (choice == "any")
m_enabledSolvers = smt::SMTSolverChoice::All();
else if (choice == "z3")
@ -41,9 +39,6 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename, langutil::EVMVersion _ev
m_enabledSolvers = smt::SMTSolverChoice::None();
else
BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT solver choice."));
}
else
m_enabledSolvers = smt::SMTSolverChoice::All();
auto available = ModelChecker::availableSolvers();
if (!available.z3)

View File

@ -37,59 +37,39 @@ namespace fs = boost::filesystem;
SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion):
SolidityExecutionFramework(_evmVersion)
SolidityExecutionFramework(_evmVersion),
EVMVersionRestrictedTestCase(_filename)
{
ifstream file(_filename);
soltestAssert(file, "Cannot open test contract: \"" + _filename + "\".");
file.exceptions(ios::badbit);
m_source = m_reader.source();
m_lineOffset = m_reader.lineNumber();
std::tie(m_source, m_lineOffset) = parseSourceAndSettingsWithLineNumbers(file);
if (m_settings.count("compileViaYul"))
if (m_reader.hasSetting("compileViaYul"))
{
if (m_settings["compileViaYul"] == "also")
string choice = m_reader.stringSetting("compileViaYul", "");
if (choice == "also")
{
m_validatedSettings["compileViaYul"] = m_settings["compileViaYul"];
m_runWithYul = true;
m_runWithoutYul = true;
}
else
{
m_validatedSettings["compileViaYul"] = "only";
m_reader.setSetting("compileViaYul", "only");
m_runWithYul = true;
m_runWithoutYul = false;
}
m_settings.erase("compileViaYul");
}
if (m_settings.count("ABIEncoderV1Only"))
{
if (m_settings["ABIEncoderV1Only"] == "true")
{
m_validatedSettings["ABIEncoderV1Only"] = "true";
m_runWithABIEncoderV1Only = true;
}
m_settings.erase("ABIEncoderV1Only");
}
m_runWithABIEncoderV1Only = m_reader.boolSetting("ABIEncoderV1Only", false);
if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2)
m_shouldRun = false;
if (m_settings.count("revertStrings"))
{
auto revertStrings = revertStringsFromString(m_settings["revertStrings"]);
if (revertStrings)
m_revertStrings = *revertStrings;
m_validatedSettings["revertStrings"] = revertStringsToString(m_revertStrings);
m_settings.erase("revertStrings");
}
auto revertStrings = revertStringsFromString(m_reader.stringSetting("revertStrings", "default"));
soltestAssert(revertStrings, "Invalid revertStrings setting.");
m_revertStrings = revertStrings.value();
if (m_settings.count("allowNonExistingFunctions"))
{
m_validatedSettings["allowNonExistingFunctions"] = true;
m_settings.erase("allowNonExistingFunctions");
}
m_allowNonExistingFunctions = m_reader.boolSetting("allowNonExistingFunctions", false);
parseExpectations(file);
parseExpectations(m_reader.stream());
soltestAssert(!m_tests.empty(), "No tests specified in " + _filename);
}
@ -152,7 +132,7 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
else
{
soltestAssert(
m_validatedSettings.count("allowNonExistingFunctions") || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature),
m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature),
"The function " + test.call().signature + " is not known to the compiler"
);

View File

@ -65,6 +65,7 @@ private:
bool m_runWithYul = false;
bool m_runWithoutYul = true;
bool m_runWithABIEncoderV1Only = false;
bool m_allowNonExistingFunctions = false;
};
}

View File

@ -37,20 +37,7 @@ namespace fs = boost::filesystem;
SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion, bool _parserErrorRecovery): CommonSyntaxTest(_filename, _evmVersion)
{
if (m_settings.count("optimize-yul"))
{
if (m_settings["optimize-yul"] == "true")
{
m_validatedSettings["optimize-yul"] = "true";
m_settings.erase("optimize-yul");
}
else if (m_settings["optimize-yul"] == "false")
{
m_validatedSettings["optimize-yul"] = "false";
m_settings.erase("optimize-yul");
m_optimiseYul = false;
}
}
m_optimiseYul = m_reader.boolSetting("optimize-yul", true);
m_parserErrorRecovery = _parserErrorRecovery;
}

View File

@ -48,17 +48,11 @@ using namespace solidity::frontend::test;
using namespace std;
EwasmTranslationTest::EwasmTranslationTest(string const& _filename)
EwasmTranslationTest::EwasmTranslationTest(string const& _filename):
EVMVersionRestrictedTestCase(_filename)
{
boost::filesystem::path path(_filename);
ifstream file(_filename);
if (!file)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open test case: \"" + _filename + "\"."));
file.exceptions(ios::badbit);
m_source = parseSourceAndSettings(file);
m_expectation = parseSimpleExpectations(file);
m_source = m_reader.source();
m_expectation = m_reader.simpleExpectations();
}
TestCase::TestResult EwasmTranslationTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted)

View File

@ -60,15 +60,11 @@ string toString(SideEffects const& _sideEffects)
}
}
FunctionSideEffects::FunctionSideEffects(string const& _filename)
FunctionSideEffects::FunctionSideEffects(string const& _filename):
TestCase(_filename)
{
ifstream file(_filename);
if (!file)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open test input: \"" + _filename + "\"."));
file.exceptions(ios::badbit);
m_source = parseSourceAndSettings(file);
m_expectation = parseSimpleExpectations(file);
m_source = m_reader.source();
m_expectation = m_reader.simpleExpectations();
}
TestCase::TestResult FunctionSideEffects::run(ostream& _stream, string const& _linePrefix, bool _formatted)

View File

@ -38,23 +38,12 @@ using namespace solidity::frontend;
using namespace solidity::frontend::test;
using namespace std;
ObjectCompilerTest::ObjectCompilerTest(string const& _filename)
ObjectCompilerTest::ObjectCompilerTest(string const& _filename):
TestCase(_filename)
{
boost::filesystem::path path(_filename);
ifstream file(_filename);
if (!file)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open test case: \"" + _filename + "\"."));
file.exceptions(ios::badbit);
m_source = parseSourceAndSettings(file);
if (m_settings.count("optimize"))
{
m_optimize = true;
m_validatedSettings["optimize"] = "true";
m_settings.erase("optimize");
}
m_expectation = parseSimpleExpectations(file);
m_source = m_reader.source();
m_optimize = m_reader.boolSetting("optimize", false);
m_expectation = m_reader.simpleExpectations();
}
TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted)

View File

@ -70,9 +70,7 @@ vector<string> validDialectNames()
void SyntaxTest::parseAndAnalyze()
{
string dialectName = m_validatedSettings.count("dialect") ? m_validatedSettings["dialect"] : "evmTyped";
yul::Dialect const& dialect = validDialects.at(dialectName)(m_evmVersion);
yul::Dialect const& dialect = validDialects.at(m_dialectName)(m_evmVersion);
if (m_sources.size() != 1)
BOOST_THROW_EXCEPTION(runtime_error{"Expected only one source for yul test."});
@ -114,21 +112,15 @@ void SyntaxTest::parseAndAnalyze()
}
void SyntaxTest::validateSettings()
SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion):
CommonSyntaxTest(_filename, _evmVersion)
{
CommonSyntaxTest::validateSettings();
m_dialectName = m_reader.stringSetting("dialect", "evmTyped");
if (!m_settings.count("dialect"))
return;
string const dialect = m_settings["dialect"];
m_validatedSettings["dialect"] = dialect;
m_settings.erase("dialect");
if (!validDialects.count(dialect))
if (!validDialects.count(m_dialectName))
BOOST_THROW_EXCEPTION(runtime_error{
"Invalid Dialect \"" +
dialect +
m_dialectName +
"\". Valid dialects are " +
joinHumanReadable(validDialectNames(), ", ", " and ") +
"."

View File

@ -36,15 +36,13 @@ public:
{
return std::make_unique<SyntaxTest>(_config.filename, _config.evmVersion);
}
SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion):
CommonSyntaxTest(_filename, _evmVersion) {}
SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion);
virtual ~SyntaxTest() {}
/// Validates the settings, i.e. moves them from m_settings to m_validatedSettings.
/// Throws a runtime exception if any setting is left at this class (i.e. unknown setting).
void validateSettings() override;
protected:
void parseAndAnalyze() override;
private:
std::string m_dialectName;
};
}

View File

@ -45,17 +45,11 @@ using namespace solidity::frontend;
using namespace solidity::frontend::test;
using namespace std;
YulInterpreterTest::YulInterpreterTest(string const& _filename)
YulInterpreterTest::YulInterpreterTest(string const& _filename):
EVMVersionRestrictedTestCase(_filename)
{
boost::filesystem::path path(_filename);
ifstream file(_filename);
if (!file)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open test case: \"" + _filename + "\"."));
file.exceptions(ios::badbit);
m_source = parseSourceAndSettings(file);
m_expectation = parseSimpleExpectations(file);
m_source = m_reader.source();
m_expectation = m_reader.simpleExpectations();
}
TestCase::TestResult YulInterpreterTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted)

View File

@ -89,7 +89,8 @@ using namespace solidity::frontend;
using namespace solidity::frontend::test;
using namespace std;
YulOptimizerTest::YulOptimizerTest(string const& _filename)
YulOptimizerTest::YulOptimizerTest(string const& _filename):
EVMVersionRestrictedTestCase(_filename)
{
boost::filesystem::path path(_filename);
@ -97,14 +98,9 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename)
BOOST_THROW_EXCEPTION(runtime_error("Filename path has to contain a directory: \"" + _filename + "\"."));
m_optimizerStep = std::prev(std::prev(path.end()))->string();
ifstream file(_filename);
soltestAssert(file, "Cannot open test contract: \"" + _filename + "\".");
file.exceptions(ios::badbit);
m_source = m_reader.source();
m_source = parseSourceAndSettings(file);
if (m_settings.count("dialect"))
{
auto dialectName = m_settings["dialect"];
auto dialectName = m_reader.stringSetting("dialect", "evm");
if (dialectName == "yul")
m_dialect = &Dialect::yulDeprecated();
else if (dialectName == "ewasm")
@ -116,19 +112,9 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename)
else
BOOST_THROW_EXCEPTION(runtime_error("Invalid dialect " + dialectName));
m_validatedSettings["dialect"] = dialectName;
m_settings.erase("dialect");
}
else
m_dialect = &EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion());
m_step = m_reader.stringSetting("step", "");
if (m_settings.count("step"))
{
m_validatedSettings["step"] = m_settings["step"];
m_settings.erase("step");
}
m_expectation = parseSimpleExpectations(file);
m_expectation = m_reader.simpleExpectations();
}
TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted)
@ -377,13 +363,13 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
m_obtainedResult = AsmPrinter{*m_dialect}(*m_ast) + "\n";
if (m_optimizerStep != m_validatedSettings["step"])
if (m_optimizerStep != m_step)
{
string nextIndentLevel = _linePrefix + " ";
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) <<
_linePrefix <<
"Invalid optimizer step. Given: \"" <<
m_validatedSettings["step"] <<
m_step <<
"\", should be: \"" <<
m_optimizerStep <<
"\"." <<
@ -410,7 +396,8 @@ void YulOptimizerTest::printSource(ostream& _stream, string const& _linePrefix,
void YulOptimizerTest::printUpdatedSettings(ostream& _stream, const string& _linePrefix, const bool _formatted)
{
m_validatedSettings["step"] = m_optimizerStep;
m_step = m_optimizerStep;
m_reader.setSetting("step", m_step);
EVMVersionRestrictedTestCase::printUpdatedSettings(_stream, _linePrefix, _formatted);
}

View File

@ -73,6 +73,7 @@ private:
std::string m_expectation;
Dialect const* m_dialect = nullptr;
std::string m_step;
std::set<YulString> m_reservedIdentifiers;
std::unique_ptr<NameDispenser> m_nameDispenser;
std::unique_ptr<OptimiserStepContext> m_context;

View File

@ -17,6 +17,7 @@ add_executable(isoltest
../CommonSyntaxTest.cpp
../EVMHost.cpp
../TestCase.cpp
../TestCaseReader.cpp
../libsolidity/util/BytesUtils.cpp
../libsolidity/util/ContractABIUtils.cpp
../libsolidity/util/TestFileParser.cpp

View File

@ -161,7 +161,6 @@ TestTool::Result TestTool::process()
(AnsiColorized(cout, formatted, {BOLD}) << m_name << ": ").flush();
m_test = m_testCaseCreator(TestCase::Config{m_path.string(), m_options.evmVersion()});
m_test->validateSettings();
if (m_test->shouldRun())
switch (TestCase::TestResult result = m_test->run(outputMessages, " ", formatted))
{