From 3b8c03864794a3ec73efe793c0ca1e97261bf4b3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 28 Oct 2020 19:48:16 +0100 Subject: [PATCH] Empty storage option for isoltest. --- test/libsolidity/SemanticTest.cpp | 11 ++++++- .../array/byte_array_storage_layout.sol | 4 +++ .../storage/empty_nonempty_empty.sol | 29 +++++++++++++++++++ test/libsolidity/util/SoltestTypes.h | 5 +++- test/libsolidity/util/TestFileParser.cpp | 16 ++++++++++ test/libsolidity/util/TestFileParser.h | 3 +- test/libsolidity/util/TestFileParserTests.cpp | 22 ++++++++++++++ test/libsolidity/util/TestFunctionCall.cpp | 14 +++++++++ 8 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 9c784c32e..73ca07e39 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -176,7 +176,16 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line constructed = true; } - if (test.call().kind == FunctionCall::Kind::Constructor) + if (test.call().kind == FunctionCall::Kind::Storage) + { + test.setFailure(false); + bytes result(1, !storageEmpty(m_contractAddress)); + test.setRawBytes(result); + soltestAssert(test.call().expectations.rawBytes().size() == 1, ""); + if (test.call().expectations.rawBytes() != result) + success = false; + } + else if (test.call().kind == FunctionCall::Kind::Constructor) { if (m_transactionSuccessful == test.call().expectations.failure) success = false; diff --git a/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol b/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol index 7343cc62b..588b954ae 100644 --- a/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol +++ b/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol @@ -41,6 +41,10 @@ contract c { // ==== // compileViaYul: also // ---- +// storage: empty // test_short() -> 1780731860627700044960722568376587075150542249149356309979516913770823710 +// storage: nonempty // test_long() -> 67 +// storage: nonempty // test_pop() -> 1780731860627700044960722568376592200742329637303199754547598369979433020 +// storage: nonempty diff --git a/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol b/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol new file mode 100644 index 000000000..70d72ac62 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol @@ -0,0 +1,29 @@ +contract Test { + bytes x; + function set(bytes memory _a) public { x = _a; } +} +// ---- +// set(bytes): 0x20, 3, "abc" +// storage: nonempty +// set(bytes): 0x20, 0 +// storage: empty +// set(bytes): 0x20, 31, "1234567890123456789012345678901" +// storage: nonempty +// set(bytes): 0x20, 36, "12345678901234567890123456789012", "XXXX" +// storage: nonempty +// set(bytes): 0x20, 3, "abc" +// storage: nonempty +// set(bytes): 0x20, 0 +// storage: empty +// set(bytes): 0x20, 3, "abc" +// storage: nonempty +// set(bytes): 0x20, 36, "12345678901234567890123456789012", "XXXX" +// storage: nonempty +// set(bytes): 0x20, 0 +// storage: empty +// set(bytes): 0x20, 66, "12345678901234567890123456789012", "12345678901234567890123456789012", "12" +// storage: nonempty +// set(bytes): 0x20, 3, "abc" +// storage: nonempty +// set(bytes): 0x20, 0 +// storage: empty diff --git a/test/libsolidity/util/SoltestTypes.h b/test/libsolidity/util/SoltestTypes.h index de901dc95..7558dbd7d 100644 --- a/test/libsolidity/util/SoltestTypes.h +++ b/test/libsolidity/util/SoltestTypes.h @@ -58,6 +58,7 @@ namespace solidity::frontend::test K(Library, "library", 0) \ K(Right, "right", 0) \ K(Failure, "FAILURE", 0) \ + K(Storage, "storage", 0) \ namespace soltest { @@ -284,7 +285,9 @@ struct FunctionCall /// a low-level call with unstructured calldata will be issued. LowLevel, /// Marks a library deployment call. - Library + Library, + /// Check that the storage of the current contract is empty or non-empty. + Storage }; Kind kind = Kind::Regular; /// Marks this function call as "short-handed", meaning diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index 2ea6cd994..713a8d634 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -85,6 +85,21 @@ vector TestFileParser::parseFunctionCall call.kind = FunctionCall::Kind::Library; call.expectations.failure = false; } + else if (accept(Token::Storage, true)) + { + expect(Token::Colon); + call.expectations.failure = false; + call.expectations.result.push_back(Parameter()); + // empty / non-empty is encoded as false / true + if (m_scanner.currentLiteral() == "empty") + call.expectations.result.back().rawBytes = bytes(1, uint8_t(false)); + else if (m_scanner.currentLiteral() == "nonempty") + call.expectations.result.back().rawBytes = bytes(1, uint8_t(true)); + else + throw TestParserError("Expected \"empty\" or \"nonempty\"."); + call.kind = FunctionCall::Kind::Storage; + m_scanner.scanNextToken(); + } else { bool lowLevelCall = false; @@ -484,6 +499,7 @@ void TestFileParser::Scanner::scanNextToken() if (_literal == "right") return TokenDesc{Token::Right, _literal}; if (_literal == "hex") return TokenDesc{Token::Hex, _literal}; if (_literal == "FAILURE") return TokenDesc{Token::Failure, _literal}; + if (_literal == "storage") return TokenDesc{Token::Storage, _literal}; return TokenDesc{Token::Identifier, _literal}; }; diff --git a/test/libsolidity/util/TestFileParser.h b/test/libsolidity/util/TestFileParser.h index 502279d2d..77d563260 100644 --- a/test/libsolidity/util/TestFileParser.h +++ b/test/libsolidity/util/TestFileParser.h @@ -44,7 +44,8 @@ namespace solidity::frontend::test * // h(uint256), 1 ether: 42 * // -> FAILURE # If REVERT or other EVM failure was detected # * // () # Call fallback function # - * // (), 1 ether # Call ether function # + * // (), 1 ether # Call receive ether function # + * // EMPTY_STORAGE # Check that storage is empty * ... */ class TestFileParser diff --git a/test/libsolidity/util/TestFileParserTests.cpp b/test/libsolidity/util/TestFileParserTests.cpp index 35c542ea6..cf83d3029 100644 --- a/test/libsolidity/util/TestFileParserTests.cpp +++ b/test/libsolidity/util/TestFileParserTests.cpp @@ -953,6 +953,28 @@ BOOST_AUTO_TEST_CASE(library) ); } +BOOST_AUTO_TEST_CASE(empty_storage) +{ + char const* source = R"( + // storage: empty + )"; + auto const calls = parse(source); + BOOST_REQUIRE_EQUAL(calls.size(), 1); + BOOST_CHECK(calls.at(0).kind == FunctionCall::Kind::Storage); + BOOST_CHECK(calls.at(0).expectations.result.front().rawBytes == bytes(1, 0)); +} + +BOOST_AUTO_TEST_CASE(nonempty_storage) +{ + char const* source = R"( + // storage: nonempty + )"; + auto const calls = parse(source); + BOOST_REQUIRE_EQUAL(calls.size(), 1); + BOOST_CHECK(calls.at(0).kind == FunctionCall::Kind::Storage); + BOOST_CHECK(calls.at(0).expectations.result.front().rawBytes == bytes(1, 1)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index b7bd460b6..3061b5f21 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -61,6 +61,20 @@ string TestFunctionCall::format( stream << _linePrefix << newline << ws << "library:" << ws << m_call.signature; return; } + else if (m_call.kind == FunctionCall::Kind::Storage) + { + stream << _linePrefix << newline << ws << "storage" << colon << ws; + soltestAssert(m_rawBytes.size() == 1, ""); + soltestAssert(m_call.expectations.rawBytes().size() == 1, ""); + bool isEmpty = _renderResult ? m_rawBytes.front() == 0 : m_call.expectations.rawBytes().front() == 0; + string output = isEmpty ? "empty" : "nonempty"; + if (_renderResult && !matchesExpectation()) + AnsiColorized(stream, highlight, {util::formatting::RED_BACKGROUND}) << output; + else + stream << output; + + return; + } /// Formats the function signature. This is the same independent from the display-mode. stream << _linePrefix << newline << ws << m_call.signature;