Merge pull request #1710 from ethereum/strictasmtests

Check error messages for assembly tests.
This commit is contained in:
chriseth 2017-02-24 08:26:16 +01:00 committed by GitHub
commit 6bbba106a7

View File

@ -20,14 +20,19 @@
* Unit tests for inline assembly. * Unit tests for inline assembly.
*/ */
#include <string> #include "../TestHelper.h"
#include <memory>
#include <libevmasm/Assembly.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/inlineasm/AsmStack.h> #include <libsolidity/inlineasm/AsmStack.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/interface/Exceptions.h> #include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/AST.h> #include <libsolidity/ast/AST.h>
#include "../TestHelper.h" #include <test/libsolidity/ErrorCheck.h>
#include <libevmasm/Assembly.h>
#include <boost/optional.hpp>
#include <string>
#include <memory>
using namespace std; using namespace std;
@ -41,31 +46,44 @@ namespace test
namespace namespace
{ {
bool successParse(std::string const& _source, bool _assemble = false, bool _allowWarnings = true) boost::optional<Error> parseAndReturnFirstError(string const& _source, bool _assemble = false, bool _allowWarnings = true)
{ {
assembly::InlineAssemblyStack stack; assembly::InlineAssemblyStack stack;
bool success = false;
try try
{ {
if (!stack.parse(std::make_shared<Scanner>(CharStream(_source)))) success = stack.parse(std::make_shared<Scanner>(CharStream(_source)));
return false; if (success && _assemble)
if (_assemble)
{
stack.assemble(); stack.assemble();
if (!stack.errors().empty())
if (!_allowWarnings || !Error::containsOnlyWarnings(stack.errors()))
return false;
}
} }
catch (FatalError const&) catch (FatalError const&)
{ {
if (Error::containsErrorOfType(stack.errors(), Error::Type::ParserError)) BOOST_FAIL("Fatal error leaked.");
return false; success = false;
}
if (!success)
{
BOOST_CHECK_EQUAL(stack.errors().size(), 1);
return *stack.errors().front();
}
else
{
// If success is true, there might still be an error in the assembly stage.
if (_allowWarnings && Error::containsOnlyWarnings(stack.errors()))
return {};
else if (!stack.errors().empty())
{
if (!_allowWarnings)
BOOST_CHECK_EQUAL(stack.errors().size(), 1);
return *stack.errors().front();
}
}
return {};
} }
if (Error::containsErrorOfType(stack.errors(), Error::Type::ParserError))
return false;
BOOST_CHECK(Error::containsOnlyWarnings(stack.errors())); bool successParse(std::string const& _source, bool _assemble = false, bool _allowWarnings = true)
return true; {
return !parseAndReturnFirstError(_source, _assemble, _allowWarnings);
} }
bool successAssemble(string const& _source, bool _allowWarnings = true) bool successAssemble(string const& _source, bool _allowWarnings = true)
@ -73,6 +91,14 @@ bool successAssemble(string const& _source, bool _allowWarnings = true)
return successParse(_source, true, _allowWarnings); return successParse(_source, true, _allowWarnings);
} }
Error expectError(std::string const& _source, bool _assemble, bool _allowWarnings = false)
{
auto error = parseAndReturnFirstError(_source, _assemble, _allowWarnings);
BOOST_REQUIRE(error);
return *error;
}
void parsePrintCompare(string const& _source) void parsePrintCompare(string const& _source)
{ {
assembly::InlineAssemblyStack stack; assembly::InlineAssemblyStack stack;
@ -83,6 +109,21 @@ void parsePrintCompare(string const& _source)
} }
#define CHECK_ERROR(text, assemble, typ, substring) \
do \
{ \
Error err = expectError((text), (assemble), false); \
BOOST_CHECK(err.type() == (Error::Type::typ)); \
BOOST_CHECK(searchErrorMessage(err, (substring))); \
} while(0)
#define CHECK_PARSE_ERROR(text, type, substring) \
CHECK_ERROR(text, false, type, substring)
#define CHECK_ASSEMBLE_ERROR(text, type, substring) \
CHECK_ERROR(text, true, type, substring)
BOOST_AUTO_TEST_SUITE(SolidityInlineAssembly) BOOST_AUTO_TEST_SUITE(SolidityInlineAssembly)
@ -245,7 +286,7 @@ BOOST_AUTO_TEST_CASE(string_literals)
BOOST_AUTO_TEST_CASE(oversize_string_literals) BOOST_AUTO_TEST_CASE(oversize_string_literals)
{ {
BOOST_CHECK(!successAssemble("{ let x := \"123456789012345678901234567890123\" }")); CHECK_ASSEMBLE_ERROR("{ let x := \"123456789012345678901234567890123\" }", TypeError, "String literal too long");
} }
BOOST_AUTO_TEST_CASE(assignment_after_tag) BOOST_AUTO_TEST_CASE(assignment_after_tag)
@ -255,15 +296,16 @@ BOOST_AUTO_TEST_CASE(assignment_after_tag)
BOOST_AUTO_TEST_CASE(magic_variables) BOOST_AUTO_TEST_CASE(magic_variables)
{ {
BOOST_CHECK(!successAssemble("{ this }")); CHECK_ASSEMBLE_ERROR("{ this pop }", DeclarationError, "Identifier not found or not unique");
BOOST_CHECK(!successAssemble("{ ecrecover }")); CHECK_ASSEMBLE_ERROR("{ ecrecover pop }", DeclarationError, "Identifier not found or not unique");
BOOST_CHECK(successAssemble("{ let ecrecover := 1 ecrecover }")); BOOST_CHECK(successAssemble("{ let ecrecover := 1 ecrecover }"));
} }
BOOST_AUTO_TEST_CASE(imbalanced_stack) BOOST_AUTO_TEST_CASE(imbalanced_stack)
{ {
BOOST_CHECK(successAssemble("{ 1 2 mul pop }", false)); BOOST_CHECK(successAssemble("{ 1 2 mul pop }", false));
BOOST_CHECK(!successAssemble("{ 1 }", false)); CHECK_ASSEMBLE_ERROR("{ 1 }", Warning, "Inline assembly block is not balanced. It leaves");
CHECK_ASSEMBLE_ERROR("{ pop }", Warning, "Inline assembly block is not balanced. It takes");
BOOST_CHECK(successAssemble("{ let x := 4 7 add }", false)); BOOST_CHECK(successAssemble("{ let x := 4 7 add }", false));
} }
@ -279,20 +321,17 @@ BOOST_AUTO_TEST_CASE(designated_invalid_instruction)
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration) BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration)
{ {
// Error message: "Cannot use instruction names for identifier names." CHECK_ASSEMBLE_ERROR("{ let gas := 1 }", ParserError, "Cannot use instruction names for identifier names.");
BOOST_CHECK(!successAssemble("{ let gas := 1 }"));
} }
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_assignment) BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_assignment)
{ {
// Error message: "Identifier expected, got instruction name." CHECK_ASSEMBLE_ERROR("{ 2 =: gas }", ParserError, "Identifier expected, got instruction name.");
BOOST_CHECK(!successAssemble("{ 2 =: gas }"));
} }
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_functional_assignment) BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_functional_assignment)
{ {
// Error message: "Cannot use instruction names for identifier names." CHECK_ASSEMBLE_ERROR("{ gas := 2 }", ParserError, "Label name / variable name must precede \":\"");
BOOST_CHECK(!successAssemble("{ gas := 2 }"));
} }
BOOST_AUTO_TEST_CASE(revert) BOOST_AUTO_TEST_CASE(revert)