Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions libdevcore/CommonIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#if defined(_WIN32)
#include <windows.h>
#else
#include <unistd.h>
#include <termios.h>
#endif
#include <boost/filesystem.hpp>
Expand Down Expand Up @@ -118,3 +119,51 @@ void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDe
}
}
}

#if defined(_WIN32)
class DisableConsoleBuffering
{
public:
DisableConsoleBuffering()
{
m_stdin = GetStdHandle(STD_INPUT_HANDLE);
GetConsoleMode(m_stdin, &m_oldMode);
SetConsoleMode(m_stdin, m_oldMode & (~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT)));
}
~DisableConsoleBuffering()
{
SetConsoleMode(m_stdin, m_oldMode);
}
private:
HANDLE m_stdin;
DWORD m_oldMode;
};
#else
class DisableConsoleBuffering
{
public:
DisableConsoleBuffering()
{
tcgetattr(0, &m_termios);
m_termios.c_lflag &= ~ICANON;
m_termios.c_lflag &= ~ECHO;
m_termios.c_cc[VMIN] = 1;
m_termios.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &m_termios);
}
~DisableConsoleBuffering()
{
m_termios.c_lflag |= ICANON;
m_termios.c_lflag |= ECHO;
tcsetattr(0, TCSADRAIN, &m_termios);
}
private:
struct termios m_termios;
};
#endif

int dev::readStandardInputChar()
{
DisableConsoleBuffering disableConsoleBuffering;
return cin.get();
}
3 changes: 3 additions & 0 deletions libdevcore/CommonIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ std::string readFileAsString(std::string const& _file);
/// Retrieve and returns the contents of standard input (until EOF).
std::string readStandardInput();

/// Retrieve and returns a character from standard input (without waiting for EOL).
int readStandardInputChar();

/// Write the given binary data into the given file, replacing the file if it pre-exists.
/// Throws exception on error.
/// @param _writeDeleteRename useful not to lose any data: If set, first writes to another file in
Expand Down
6 changes: 6 additions & 0 deletions scripts/isoltest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash

set -e

REPO_ROOT="$(dirname "$0")"/..
exec ${REPO_ROOT}/build/test/tools/isoltest --testpath ${REPO_ROOT}/test
66 changes: 66 additions & 0 deletions test/libsolidity/FormattedScope.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
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/>.
*/

#pragma once

#include <boost/noncopyable.hpp>

#include <ostream>
#include <vector>

namespace dev
{
namespace solidity
{
namespace test
{

namespace formatting
{

static constexpr char const* RESET = "\033[0m";
static constexpr char const* RED = "\033[1;31m";
static constexpr char const* GREEN = "\033[1;32m";
static constexpr char const* YELLOW = "\033[1;33m";
static constexpr char const* CYAN = "\033[1;36m";
static constexpr char const* BOLD = "\033[1m";
static constexpr char const* INVERSE = "\033[7m";

}

class FormattedScope: boost::noncopyable
{
public:
/// @arg _formatting List of formatting strings (e.g. colors) defined in the formatting namespace.
FormattedScope(std::ostream& _stream, bool const _enabled, std::vector<char const*> const& _formatting):
m_stream(_stream), m_enabled(_enabled)
{
if (m_enabled)
for (auto const& format: _formatting)
m_stream << format;
}
~FormattedScope() { if (m_enabled) m_stream << formatting::RESET; }
template<typename T>
std::ostream& operator<<(T&& _t) { return m_stream << std::forward<T>(_t); }
private:
std::ostream& m_stream;
bool m_enabled;
};

}
}
}
62 changes: 49 additions & 13 deletions test/libsolidity/SyntaxTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -56,41 +57,76 @@ 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 << ": ";
_stream << expectation.message << endl;
}
}

void SyntaxTest::printErrorList(
ostream& _stream,
ErrorList const& _errorList,
string const& _indent
string const& _linePrefix,
bool const _ignoreWarnings,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use enums instead? I'm fine with named bit fields, too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure it makes sense to define an enum/bit field for this single function alone?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, because then you know what the bools mean.

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<errinfo_sourceLocation>(*error)->start
);
if (line >= 0)
_stream << "(" << line << "): ";
}
_stream << error->typeName() << ": ";
}
_stream << 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<size_t>(_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
Expand Down Expand Up @@ -196,7 +232,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(),
Expand Down
17 changes: 14 additions & 3 deletions test/libsolidity/SyntaxTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#pragma once

#include <test/libsolidity/AnalysisFramework.h>
#include <test/libsolidity/FormattedScope.h>
#include <libsolidity/interface/Exceptions.h>

#include <boost/noncopyable.hpp>
Expand Down Expand Up @@ -47,13 +48,22 @@ class SyntaxTest: AnalysisFramework
public:
SyntaxTest(std::string const& _filename);

bool run(std::ostream& _stream, std::string const& _indent);
bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false);

std::vector<SyntaxTestExpectation> const& expectations() const { return m_expectations; }
std::string const& source() const { return m_source; }
ErrorList const& errorList() const { return m_errorList; }
ErrorList const& compilerErrors() const { return m_compiler.errors(); }

void printExpected(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted = false) const;

void printExpected(std::ostream& _stream, std::string const& _indent) const;
void printErrorList(
std::ostream& _stream,
ErrorList const& _errors,
std::string const& _indent
std::string const& _linePrefix,
bool const _ignoreWarnings,
bool const _lineNumbers,
bool const _formatted = false
) const;

static int registerTests(
Expand All @@ -66,6 +76,7 @@ class SyntaxTest: AnalysisFramework
static std::string errorMessage(Error const& _e);
static std::string parseSource(std::istream& _stream);
static std::vector<SyntaxTestExpectation> parseExpectations(std::istream& _stream);
int offsetToLineNumber(int _location) const;

std::string m_source;
std::vector<SyntaxTestExpectation> m_expectations;
Expand Down
3 changes: 3 additions & 0 deletions test/tools/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
add_executable(solfuzzer fuzzer.cpp)
target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES})

add_executable(isoltest isoltest.cpp ../Options.cpp ../libsolidity/SyntaxTest.cpp ../libsolidity/AnalysisFramework.cpp)
target_link_libraries(isoltest PRIVATE libsolc solidity evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
Loading