diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4d56ec9d6..01a9a1884 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -21,4 +21,4 @@ include_directories(BEFORE ..) target_link_libraries(${EXECUTABLE} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) add_executable(solfuzzer fuzzer.cpp) -target_link_libraries(solfuzzer soljson) +target_link_libraries(solfuzzer soljson ${Boost_PROGRAM_OPTIONS_LIBRARIES}) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index caf09a910..6dc3f77fd 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -79,7 +79,7 @@ TMPDIR=$(mktemp -d) "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/test/ for f in *.sol do - "$REPO_ROOT"/build/test/solfuzzer < "$f" + "$REPO_ROOT"/build/test/solfuzzer --quiet < "$f" done ) rm -rf "$TMPDIR" diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp index 410313c5c..afa506714 100644 --- a/test/fuzzer.cpp +++ b/test/fuzzer.cpp @@ -16,21 +16,32 @@ */ /** * Executable for use with AFL . - * Reads a single source from stdin and signals a failure for internal errors. */ +#include +#include + #include +#include + #include #include using namespace std; +using namespace dev; +using namespace dev::eth; +namespace po = boost::program_options; extern "C" { extern char const* compileJSON(char const* _input, bool _optimize); +typedef void (*CStyleReadFileCallback)(char const* _path, char** o_contents, char** o_error); +extern char const* compileStandard(char const* _input, CStyleReadFileCallback _readCallback); } +bool quiet = false; + string contains(string const& _haystack, vector const& _needles) { for (string const& needle: _needles) @@ -39,7 +50,42 @@ string contains(string const& _haystack, vector const& _needles) return ""; } -int main() +void testConstantOptimizer() +{ + if (!quiet) + cout << "Testing constant optimizer" << endl; + vector numbers; + while (!cin.eof()) + { + h256 data; + cin.read(reinterpret_cast(data.data()), 32); + numbers.push_back(u256(data)); + } + if (!quiet) + cout << "Got " << numbers.size() << " inputs:" << endl; + + Assembly assembly; + for (u256 const& n: numbers) + { + if (!quiet) + cout << n << endl; + assembly.append(n); + } + for (bool isCreation: {false, true}) + { + for (unsigned runs: {1, 2, 3, 20, 40, 100, 200, 400, 1000}) + { + ConstantOptimisationMethod::optimiseConstants( + isCreation, + runs, + assembly, + const_cast(assembly.items()) + ); + } + } +} + +string readInput() { string input; while (!cin.eof()) @@ -48,6 +94,41 @@ int main() getline(cin, s); input += s + '\n'; } + return input; +} + +void testStandardCompiler() +{ + if (!quiet) + cout << "Testing compiler via JSON interface." << endl; + string input = readInput(); + string outputString(compileStandard(input.c_str(), NULL)); + Json::Value output; + if (!Json::Reader().parse(outputString, output)) + { + cout << "Compiler produced invalid JSON output." << endl; + abort(); + } + if (output.isMember("errors")) + for (auto const& error: output["errors"]) + { + string invalid = contains(error["type"].asString(), vector{ + "Exception", + "InternalCompilerError" + }); + if (!invalid.empty()) + { + cout << "Invalid error: \"" << error["type"].asString() << "\"" << endl; + abort(); + } + } +} + +void testCompiler() +{ + if (!quiet) + cout << "Testing compiler." << endl; + string input = readInput(); bool optimize = true; string outputString(compileJSON(input.c_str(), optimize)); @@ -87,5 +168,57 @@ int main() cout << "Output JSON has neither \"errors\" nor \"contracts\"." << endl; abort(); } +} + +int main(int argc, char** argv) +{ + po::options_description options( + R"(solfuzzer, fuzz-testing binary for use with AFL. +Usage: solfuzzer [Options] < input +Reads a single source from stdin, compiles it and signals a failure for internal errors. + +Allowed options)", + po::options_description::m_default_line_length, + po::options_description::m_default_line_length - 23); + options.add_options() + ("help", "Show this help screen.") + ("quiet", "Only output errors.") + ( + "standard-json", + "Test via the standard-json interface, i.e. " + "input is expected to be JSON-encoded instead of " + "plain source file." + ) + ( + "const-opt", + "Run the constant optimizer instead of compiling. " + "Expects a binary string of up to 32 bytes on stdin." + ); + + po::variables_map arguments; + try + { + po::command_line_parser cmdLineParser(argc, argv); + cmdLineParser.options(options); + po::store(cmdLineParser.run(), arguments); + } + catch (po::error const& _exception) + { + cerr << _exception.what() << endl; + return false; + } + + if (arguments.count("quiet")) + quiet = true; + + if (arguments.count("help")) + cout << options; + else if (arguments.count("const-opt")) + testConstantOptimizer(); + else if (arguments.count("standard-json")) + testStandardCompiler(); + else + testCompiler(); + return 0; }