From 3232561d979954f0625102d33cf042fc5eda7211 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 12 Mar 2018 13:33:37 +0100 Subject: Refactoring; fuse SyntaxTestParser and SyntaxTester to SyntaxTest. --- test/libsolidity/SyntaxTest.cpp | 205 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 test/libsolidity/SyntaxTest.cpp (limited to 'test/libsolidity/SyntaxTest.cpp') diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp new file mode 100644 index 00000000..8cbe2f36 --- /dev/null +++ b/test/libsolidity/SyntaxTest.cpp @@ -0,0 +1,205 @@ +/* + 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 . +*/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace dev; +using namespace solidity; +using namespace dev::solidity::test; +using namespace std; +namespace fs = boost::filesystem; +using namespace boost::unit_test; + +template +void skipWhitespace(IteratorType& it, IteratorType end) +{ + while (it != end && isspace(*it)) + ++it; +} + +template +void skipSlashes(IteratorType& it, IteratorType end) +{ + while (it != end && *it == '/') + ++it; +} + +SyntaxTest::SyntaxTest(string const& _filename) +{ + ifstream file(_filename); + if (!file) + BOOST_THROW_EXCEPTION(runtime_error("cannot open test contract: \"" + _filename + "\"")); + file.exceptions(ios::badbit); + + m_source = parseSource(file); + m_expectations = parseExpectations(file); +} + +bool SyntaxTest::run(ostream& _stream, string const& _indent) +{ + m_errorList = parseAnalyseAndReturnError(m_source, true, true, true).second; + if (!matchesExpectations(m_errorList)) + { + std::string nextIndentLevel = _indent.empty() ? "\t" : _indent + _indent; + _stream << _indent << "Expected result:" << endl; + printExpected(_stream, nextIndentLevel); + _stream << _indent << "Obtained result:\n"; + printErrorList(_stream, m_errorList, nextIndentLevel); + return false; + } + return true; +} + +void SyntaxTest::printExpected(ostream& _stream, string const& _indent) const +{ + if (m_expectations.empty()) + _stream << _indent << "Success" << endl; + else + for (auto const& expectation: m_expectations) + _stream << _indent << expectation.type << ": " << expectation.message << endl; +} + +void SyntaxTest::printErrorList( + ostream& _stream, + ErrorList const& _errorList, + string const& _indent +) const +{ + if (_errorList.empty()) + _stream << _indent << "Success" << endl; + else + for (auto const& error: _errorList) + _stream << _indent << error->typeName() << ": " << errorMessage(*error) << endl; +} + +bool SyntaxTest::matchesExpectations(ErrorList const& _errorList) const +{ + if (_errorList.size() != m_expectations.size()) + return false; + else + for (size_t i = 0; i < _errorList.size(); i++) + if ( + !(_errorList[i]->typeName() == m_expectations[i].type) || + !(errorMessage(*_errorList[i]) == m_expectations[i].message) + ) + return false; + return true; +} + +string SyntaxTest::errorMessage(Error const& _e) +{ + if (_e.comment()) + return boost::replace_all_copy(*_e.comment(), "\n", "\\n"); + else + return "NONE"; +} + +string SyntaxTest::parseSource(istream& _stream) +{ + string source; + string line; + string const delimiter("// ----"); + while (getline(_stream, line)) + if (boost::algorithm::starts_with(line, delimiter)) + break; + else + source += line + "\n"; + return source; +} + +vector SyntaxTest::parseExpectations(istream& _stream) +{ + vector expectations; + string line; + while (getline(_stream, line)) + { + auto it = line.begin(); + + skipSlashes(it, line.end()); + skipWhitespace(it, line.end()); + + if (it == line.end()) continue; + + auto typeBegin = it; + while (it != line.end() && *it != ':') + ++it; + string errorType(typeBegin, it); + + // skip colon + if (it != line.end()) it++; + + skipWhitespace(it, line.end()); + + string errorMessage(it, line.end()); + expectations.emplace_back(SyntaxTestExpectation{move(errorType), move(errorMessage)}); + } + return expectations; +} + +#if BOOST_VERSION < 105900 +test_case *make_test_case( + function const& _fn, + string const& _name, + string const& /* _filename */, + size_t /* _line */ +) +{ + return make_test_case(_fn, _name); +} +#endif + +int SyntaxTest::registerTests( + boost::unit_test::test_suite& _suite, + boost::filesystem::path const& _basepath, + boost::filesystem::path const& _path +) +{ + int numTestsAdded = 0; + fs::path fullpath = _basepath / _path; + if (fs::is_directory(fullpath)) + { + test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string()); + for (auto const& entry: boost::iterator_range( + fs::directory_iterator(fullpath), + fs::directory_iterator() + )) + numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename()); + _suite.add(sub_suite); + } + else + { + _suite.add(make_test_case( + [fullpath] + { + std::stringstream errorStream; + if (!SyntaxTest(fullpath.string()).run(errorStream, "")) + BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); + }, + _path.stem().string(), + _path.string(), + 0 + )); + numTestsAdded = 1; + } + return numTestsAdded; +} -- cgit v1.2.3 From 7091b6c8b5a91a13ff02255cc0bca08266527e4f Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 13 Mar 2018 12:30:56 +0100 Subject: Minor adjustments. --- test/libsolidity/SyntaxTest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'test/libsolidity/SyntaxTest.cpp') diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 8cbe2f36..f1c60458 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -48,7 +48,7 @@ SyntaxTest::SyntaxTest(string const& _filename) { ifstream file(_filename); if (!file) - BOOST_THROW_EXCEPTION(runtime_error("cannot open test contract: \"" + _filename + "\"")); + BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\".")); file.exceptions(ios::badbit); m_source = parseSource(file); @@ -60,7 +60,7 @@ bool SyntaxTest::run(ostream& _stream, string const& _indent) m_errorList = parseAnalyseAndReturnError(m_source, true, true, true).second; if (!matchesExpectations(m_errorList)) { - std::string nextIndentLevel = _indent.empty() ? "\t" : _indent + _indent; + std::string nextIndentLevel = _indent + "\t"; _stream << _indent << "Expected result:" << endl; printExpected(_stream, nextIndentLevel); _stream << _indent << "Obtained result:\n"; @@ -99,8 +99,8 @@ bool SyntaxTest::matchesExpectations(ErrorList const& _errorList) const else for (size_t i = 0; i < _errorList.size(); i++) if ( - !(_errorList[i]->typeName() == m_expectations[i].type) || - !(errorMessage(*_errorList[i]) == m_expectations[i].message) + (_errorList[i]->typeName() != m_expectations[i].type) || + (errorMessage(*_errorList[i]) != m_expectations[i].message) ) return false; return true; -- cgit v1.2.3 From 09420f1a4480e8bfeb13f861fda801e49bce8487 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 13 Mar 2018 17:54:22 +0100 Subject: Store filenames in static variable to guarantee sufficient lifetime. --- test/libsolidity/SyntaxTest.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'test/libsolidity/SyntaxTest.cpp') diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index f1c60458..45a851b6 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include using namespace dev; @@ -188,6 +189,9 @@ int SyntaxTest::registerTests( } else { + static vector> filenames; + + filenames.emplace_back(new string(_path.string())); _suite.add(make_test_case( [fullpath] { @@ -196,7 +200,7 @@ int SyntaxTest::registerTests( BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); }, _path.stem().string(), - _path.string(), + *filenames.back(), 0 )); numTestsAdded = 1; -- cgit v1.2.3 From 269241e9105a3b3014002bf711ade985d87febe4 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 12 Mar 2018 13:57:48 +0100 Subject: Add formatted printing to SyntaxTest and expand its public interface. --- test/libsolidity/SyntaxTest.cpp | 56 +++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 13 deletions(-) (limited to 'test/libsolidity/SyntaxTest.cpp') diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 45a851b6..70abac48 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -27,6 +27,7 @@ using namespace dev; using namespace solidity; using namespace dev::solidity::test; +using namespace dev::solidity::test::formatting; using namespace std; namespace fs = boost::filesystem; using namespace boost::unit_test; @@ -56,41 +57,70 @@ SyntaxTest::SyntaxTest(string const& _filename) m_expectations = parseExpectations(file); } -bool SyntaxTest::run(ostream& _stream, string const& _indent) +bool SyntaxTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) { m_errorList = parseAnalyseAndReturnError(m_source, true, true, true).second; if (!matchesExpectations(m_errorList)) { - std::string nextIndentLevel = _indent + "\t"; - _stream << _indent << "Expected result:" << endl; - printExpected(_stream, nextIndentLevel); - _stream << _indent << "Obtained result:\n"; - printErrorList(_stream, m_errorList, nextIndentLevel); + std::string nextIndentLevel = _linePrefix + " "; + FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; + printExpected(_stream, nextIndentLevel, _formatted); + FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:\n"; + printErrorList(_stream, m_errorList, nextIndentLevel, false, false, _formatted); return false; } return true; } -void SyntaxTest::printExpected(ostream& _stream, string const& _indent) const +void SyntaxTest::printExpected(ostream& _stream, string const& _linePrefix, bool const _formatted) const { if (m_expectations.empty()) - _stream << _indent << "Success" << endl; + FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; else for (auto const& expectation: m_expectations) - _stream << _indent << expectation.type << ": " << expectation.message << endl; + FormattedScope(_stream, _formatted, {BOLD, expectation.type == "Warning" ? YELLOW : RED}) << + _linePrefix << expectation.type << ": " << expectation.message << endl; } void SyntaxTest::printErrorList( ostream& _stream, ErrorList const& _errorList, - string const& _indent + string const& _linePrefix, + bool const _ignoreWarnings, + bool const _lineNumbers, + bool const _formatted ) const { if (_errorList.empty()) - _stream << _indent << "Success" << endl; + FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; else for (auto const& error: _errorList) - _stream << _indent << error->typeName() << ": " << errorMessage(*error) << endl; + { + bool isWarning = (error->type() == Error::Type::Warning); + if (isWarning && _ignoreWarnings) continue; + + FormattedScope scope(_stream, _formatted, {BOLD, isWarning ? YELLOW : RED}); + _stream << _linePrefix; + if (_lineNumbers) + { + int line = offsetToLineNumber( + boost::get_error_info(*error)->start + ); + if (line >= 0) + _stream << "(" << line << "): "; + } + _stream << error->typeName() << ": " << errorMessage(*error) << endl; + } +} + +int SyntaxTest::offsetToLineNumber(int _location) const +{ + // parseAnalyseAndReturnError(...) prepends a version pragma + _location -= strlen("pragma solidity >=0.0;\n"); + if (_location < 0 || static_cast(_location) >= m_source.size()) + return -1; + else + return 1 + std::count(m_source.begin(), m_source.begin() + _location, '\n'); } bool SyntaxTest::matchesExpectations(ErrorList const& _errorList) const @@ -196,7 +226,7 @@ int SyntaxTest::registerTests( [fullpath] { std::stringstream errorStream; - if (!SyntaxTest(fullpath.string()).run(errorStream, "")) + if (!SyntaxTest(fullpath.string()).run(errorStream)) BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); }, _path.stem().string(), -- cgit v1.2.3 From 50ad89d369a3ffd90471b2738f2f30491841e197 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 15 Mar 2018 16:27:34 +0100 Subject: Only colour error type, not error message in isoltest. --- test/libsolidity/SyntaxTest.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'test/libsolidity/SyntaxTest.cpp') diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 70abac48..acfdff29 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -78,8 +78,11 @@ void SyntaxTest::printExpected(ostream& _stream, string const& _linePrefix, bool FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; else for (auto const& expectation: m_expectations) + { FormattedScope(_stream, _formatted, {BOLD, expectation.type == "Warning" ? YELLOW : RED}) << - _linePrefix << expectation.type << ": " << expectation.message << endl; + _linePrefix << expectation.type << ": "; + _stream << expectation.message << endl; + } } void SyntaxTest::printErrorList( @@ -99,17 +102,20 @@ void SyntaxTest::printErrorList( bool isWarning = (error->type() == Error::Type::Warning); if (isWarning && _ignoreWarnings) continue; - FormattedScope scope(_stream, _formatted, {BOLD, isWarning ? YELLOW : RED}); - _stream << _linePrefix; - if (_lineNumbers) { - int line = offsetToLineNumber( - boost::get_error_info(*error)->start - ); - if (line >= 0) - _stream << "(" << line << "): "; + FormattedScope scope(_stream, _formatted, {BOLD, isWarning ? YELLOW : RED}); + _stream << _linePrefix; + if (_lineNumbers) + { + int line = offsetToLineNumber( + boost::get_error_info(*error)->start + ); + if (line >= 0) + _stream << "(" << line << "): "; + } + _stream << error->typeName() << ": "; } - _stream << error->typeName() << ": " << errorMessage(*error) << endl; + _stream << errorMessage(*error) << endl; } } -- cgit v1.2.3 From e68c19c47b03eebb9af528bf2b03bb0086484c63 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 16 Mar 2018 11:40:03 +0100 Subject: Only consider files ending with .sol and not starting with ~ in syntax tests. --- test/libsolidity/SyntaxTest.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'test/libsolidity/SyntaxTest.cpp') diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index acfdff29..ca051138 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -205,6 +205,13 @@ test_case *make_test_case( } #endif +bool SyntaxTest::isTestFilename(boost::filesystem::path const& _filename) +{ + return _filename.extension().string() == ".sol" && + !boost::starts_with(_filename.string(), "~") && + !boost::starts_with(_filename.string(), "."); +} + int SyntaxTest::registerTests( boost::unit_test::test_suite& _suite, boost::filesystem::path const& _basepath, @@ -220,7 +227,8 @@ int SyntaxTest::registerTests( fs::directory_iterator(fullpath), fs::directory_iterator() )) - numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename()); + if (fs::is_directory(entry.path()) || isTestFilename(entry.path().filename())) + numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename()); _suite.add(sub_suite); } else -- cgit v1.2.3 From 6f9644add18b27363a423e2c7ccb0e578ba800bc Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 3 Apr 2018 12:05:26 +0200 Subject: SyntaxTests: extend syntax tests and isoltest to support parser errors and compiler exceptions. --- test/libsolidity/SyntaxTest.cpp | 98 +++++++++++++---------------------------- 1 file changed, 30 insertions(+), 68 deletions(-) (limited to 'test/libsolidity/SyntaxTest.cpp') diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index ca051138..329543bf 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -59,93 +60,52 @@ SyntaxTest::SyntaxTest(string const& _filename) bool SyntaxTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) { - m_errorList = parseAnalyseAndReturnError(m_source, true, true, true).second; - if (!matchesExpectations(m_errorList)) + m_compiler.reset(); + m_compiler.addSource("", "pragma solidity >=0.0;\n" + m_source); + m_compiler.setEVMVersion(dev::test::Options::get().evmVersion()); + + if (m_compiler.parse()) + m_compiler.analyze(); + + for (auto const& currentError: filterErrors(m_compiler.errors(), true)) + m_errorList.emplace_back(SyntaxTestError{currentError->typeName(), errorMessage(*currentError)}); + + if (m_expectations != m_errorList) { - std::string nextIndentLevel = _linePrefix + " "; + string nextIndentLevel = _linePrefix + " "; FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; - printExpected(_stream, nextIndentLevel, _formatted); + printErrorList(_stream, m_expectations, nextIndentLevel, _formatted); FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:\n"; - printErrorList(_stream, m_errorList, nextIndentLevel, false, false, _formatted); + printErrorList(_stream, m_errorList, nextIndentLevel, _formatted); return false; } return true; } -void SyntaxTest::printExpected(ostream& _stream, string const& _linePrefix, bool const _formatted) const -{ - if (m_expectations.empty()) - FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; - else - for (auto const& expectation: m_expectations) - { - FormattedScope(_stream, _formatted, {BOLD, expectation.type == "Warning" ? YELLOW : RED}) << - _linePrefix << expectation.type << ": "; - _stream << expectation.message << endl; - } -} - void SyntaxTest::printErrorList( ostream& _stream, - ErrorList const& _errorList, + vector const& _errorList, string const& _linePrefix, - bool const _ignoreWarnings, - bool const _lineNumbers, bool const _formatted -) const +) { if (_errorList.empty()) FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; else for (auto const& error: _errorList) { - bool isWarning = (error->type() == Error::Type::Warning); - if (isWarning && _ignoreWarnings) continue; - { - FormattedScope scope(_stream, _formatted, {BOLD, isWarning ? YELLOW : RED}); + FormattedScope scope(_stream, _formatted, {BOLD, (error.type == "Warning") ? YELLOW : RED}); _stream << _linePrefix; - if (_lineNumbers) - { - int line = offsetToLineNumber( - boost::get_error_info(*error)->start - ); - if (line >= 0) - _stream << "(" << line << "): "; - } - _stream << error->typeName() << ": "; + _stream << error.type << ": "; } - _stream << errorMessage(*error) << endl; + _stream << error.message << endl; } } -int SyntaxTest::offsetToLineNumber(int _location) const -{ - // parseAnalyseAndReturnError(...) prepends a version pragma - _location -= strlen("pragma solidity >=0.0;\n"); - if (_location < 0 || static_cast(_location) >= m_source.size()) - return -1; - else - return 1 + std::count(m_source.begin(), m_source.begin() + _location, '\n'); -} - -bool SyntaxTest::matchesExpectations(ErrorList const& _errorList) const -{ - if (_errorList.size() != m_expectations.size()) - return false; - else - for (size_t i = 0; i < _errorList.size(); i++) - if ( - (_errorList[i]->typeName() != m_expectations[i].type) || - (errorMessage(*_errorList[i]) != m_expectations[i].message) - ) - return false; - return true; -} - -string SyntaxTest::errorMessage(Error const& _e) +string SyntaxTest::errorMessage(Exception const& _e) { - if (_e.comment()) + if (_e.comment() && !_e.comment()->empty()) return boost::replace_all_copy(*_e.comment(), "\n", "\\n"); else return "NONE"; @@ -164,9 +124,9 @@ string SyntaxTest::parseSource(istream& _stream) return source; } -vector SyntaxTest::parseExpectations(istream& _stream) +vector SyntaxTest::parseExpectations(istream& _stream) { - vector expectations; + vector expectations; string line; while (getline(_stream, line)) { @@ -188,7 +148,7 @@ vector SyntaxTest::parseExpectations(istream& _stream) skipWhitespace(it, line.end()); string errorMessage(it, line.end()); - expectations.emplace_back(SyntaxTestExpectation{move(errorType), move(errorMessage)}); + expectations.emplace_back(SyntaxTestError{move(errorType), move(errorMessage)}); } return expectations; } @@ -239,9 +199,11 @@ int SyntaxTest::registerTests( _suite.add(make_test_case( [fullpath] { - std::stringstream errorStream; - if (!SyntaxTest(fullpath.string()).run(errorStream)) - BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); + BOOST_REQUIRE_NO_THROW({ + stringstream errorStream; + if (!SyntaxTest(fullpath.string()).run(errorStream)) + BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); + }); }, _path.stem().string(), *filenames.back(), -- cgit v1.2.3 From f03695731b9b36cd4014620b7b63556a69b4e952 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 6 Apr 2018 18:37:01 +0200 Subject: Add source locations to syntax test expectations. --- test/libsolidity/SyntaxTest.cpp | 88 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 10 deletions(-) (limited to 'test/libsolidity/SyntaxTest.cpp') diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 329543bf..1c2355d5 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -34,17 +34,38 @@ namespace fs = boost::filesystem; using namespace boost::unit_test; template -void skipWhitespace(IteratorType& it, IteratorType end) +void skipWhitespace(IteratorType& _it, IteratorType _end) { - while (it != end && isspace(*it)) - ++it; + while (_it != _end && isspace(*_it)) + ++_it; } template -void skipSlashes(IteratorType& it, IteratorType end) +void skipSlashes(IteratorType& _it, IteratorType _end) { - while (it != end && *it == '/') - ++it; + while (_it != _end && *_it == '/') + ++_it; +} + +void expect(string::iterator& _it, string::iterator _end, string::value_type _c) +{ + if (_it == _end || *_it != _c) + throw runtime_error(string("Invalid test expectation. Expected: \"") + _c + "\"."); + ++_it; +} + +int parseUnsignedInteger(string::iterator &_it, string::iterator _end) +{ + if (_it == _end || !isdigit(*_it)) + throw runtime_error("Invalid test expectation. Source location expected."); + int result = 0; + while (_it != _end && isdigit(*_it)) + { + result *= 10; + result += *_it - '0'; + ++_it; + } + return result; } SyntaxTest::SyntaxTest(string const& _filename) @@ -60,22 +81,39 @@ SyntaxTest::SyntaxTest(string const& _filename) bool SyntaxTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) { + string const versionPragma = "pragma solidity >=0.0;\n"; m_compiler.reset(); - m_compiler.addSource("", "pragma solidity >=0.0;\n" + m_source); + m_compiler.addSource("", versionPragma + m_source); m_compiler.setEVMVersion(dev::test::Options::get().evmVersion()); if (m_compiler.parse()) m_compiler.analyze(); for (auto const& currentError: filterErrors(m_compiler.errors(), true)) - m_errorList.emplace_back(SyntaxTestError{currentError->typeName(), errorMessage(*currentError)}); + { + int locationStart = -1, locationEnd = -1; + if (auto location = boost::get_error_info(*currentError)) + { + // ignore the version pragma inserted by the testing tool when calculating locations. + if (location->start >= static_cast(versionPragma.size())) + locationStart = location->start - versionPragma.size(); + if (location->end >= static_cast(versionPragma.size())) + locationEnd = location->end - versionPragma.size(); + } + m_errorList.emplace_back(SyntaxTestError{ + currentError->typeName(), + errorMessage(*currentError), + locationStart, + locationEnd + }); + } if (m_expectations != m_errorList) { string nextIndentLevel = _linePrefix + " "; FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; printErrorList(_stream, m_expectations, nextIndentLevel, _formatted); - FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:\n"; + FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; printErrorList(_stream, m_errorList, nextIndentLevel, _formatted); return false; } @@ -99,6 +137,16 @@ void SyntaxTest::printErrorList( _stream << _linePrefix; _stream << error.type << ": "; } + if (error.locationStart >= 0 || error.locationEnd >= 0) + { + _stream << "("; + if (error.locationStart >= 0) + _stream << error.locationStart; + _stream << "-"; + if (error.locationEnd >= 0) + _stream << error.locationEnd; + _stream << "): "; + } _stream << error.message << endl; } } @@ -147,8 +195,28 @@ vector SyntaxTest::parseExpectations(istream& _stream) skipWhitespace(it, line.end()); + int locationStart = -1; + int locationEnd = -1; + + if (it != line.end() && *it == '(') + { + ++it; + locationStart = parseUnsignedInteger(it, line.end()); + expect(it, line.end(), '-'); + locationEnd = parseUnsignedInteger(it, line.end()); + expect(it, line.end(), ')'); + expect(it, line.end(), ':'); + } + + skipWhitespace(it, line.end()); + string errorMessage(it, line.end()); - expectations.emplace_back(SyntaxTestError{move(errorType), move(errorMessage)}); + expectations.emplace_back(SyntaxTestError{ + move(errorType), + move(errorMessage), + locationStart, + locationEnd + }); } return expectations; } -- cgit v1.2.3