diff options
Diffstat (limited to 'test')
26 files changed, 1763 insertions, 470 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index efcbb82e..2f001b21 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -26,7 +26,7 @@ file(GLOB HEADERS "*.h" "*/*.h") set(EXECUTABLE soltest) eth_simple_add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) -eth_use(${EXECUTABLE} REQUIRED Solidity::solidity Eth::ethcore) +eth_use(${EXECUTABLE} REQUIRED Solidity::solidity) include_directories(BEFORE ..) target_link_libraries(${EXECUTABLE} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 54e1675f..0a01ddb2 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -13,6 +13,8 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. + + The Implementation originally from https://msdn.microsoft.com/en-us/library/windows/desktop/aa365592(v=vs.85).aspx */ /** @file RPCSession.cpp * @author Dimtiry Khokhlov <dimitry@ethdev.com> @@ -32,26 +34,84 @@ using namespace dev; IPCSocket::IPCSocket(string const& _path): m_path(_path) { +#if defined(_WIN32) + m_socket = CreateFile( + m_path.c_str(), // pipe name + GENERIC_READ | // read and write access + GENERIC_WRITE, + 0, // no sharing + NULL, // default security attribute + OPEN_EXISTING, // opens existing pipe + 0, // default attributes + NULL); // no template file + + if (m_socket == INVALID_HANDLE_VALUE) + BOOST_FAIL("Error creating IPC socket object!"); + +#else if (_path.length() >= sizeof(sockaddr_un::sun_path)) BOOST_FAIL("Error opening IPC: socket path is too long!"); struct sockaddr_un saun; + memset(&saun, 0, sizeof(sockaddr_un)); saun.sun_family = AF_UNIX; strcpy(saun.sun_path, _path.c_str()); +// http://idletechnology.blogspot.ca/2011/12/unix-domain-sockets-on-osx.html +// +// SUN_LEN() might be optimal, but it seemingly affects the portability, +// with at least Android missing this macro. Just using the sizeof() for +// structure seemingly works, and would only have the side-effect of +// sending larger-than-required packets over the socket. Given that this +// code is only used for unit-tests, that approach seems simpler. +#if defined(__APPLE__) + saun.sun_len = sizeof(struct sockaddr_un); +#endif // defined(__APPLE__) + if ((m_socket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) BOOST_FAIL("Error creating IPC socket object"); - int len = sizeof(saun.sun_family) + strlen(saun.sun_path); - - if (connect(m_socket, reinterpret_cast<struct sockaddr const*>(&saun), len) < 0) + if (connect(m_socket, reinterpret_cast<struct sockaddr const*>(&saun), sizeof(struct sockaddr_un)) < 0) BOOST_FAIL("Error connecting to IPC socket: " << _path); m_fp = fdopen(m_socket, "r"); +#endif } string IPCSocket::sendRequest(string const& _req) { +#if defined(_WIN32) + string returnStr; + DWORD cbWritten; + BOOL fSuccess = WriteFile( + m_socket, // pipe handle + _req.c_str(), // message + _req.size(), // message length + &cbWritten, // bytes written + NULL); // not overlapped + + if (!fSuccess) + BOOST_FAIL("WriteFile to pipe failed"); + + DWORD cbRead; + TCHAR chBuf[c_buffsize]; + + // Read from the pipe. + fSuccess = ReadFile( + m_socket, // pipe handle + chBuf, // buffer to receive reply + c_buffsize,// size of buffer + &cbRead, // number of bytes read + NULL); // not overlapped + + returnStr += chBuf; + + if (!fSuccess) + BOOST_FAIL("ReadFile from pipe failed"); + + cerr << "."; //Output for log activity + return returnStr; +#else send(m_socket, _req.c_str(), _req.length(), 0); char c; @@ -64,6 +124,7 @@ string IPCSocket::sendRequest(string const& _req) break; } return response; +#endif } RPCSession& RPCSession::instance(const string& _path) @@ -183,15 +244,39 @@ void RPCSession::test_mineBlocks(int _number) u256 startBlock = fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString())); rpcCall("test_mineBlocks", { to_string(_number) }, true); - //@TODO do not use polling - but that would probably need a change to the test client - for (size_t polls = 0; polls < 100; ++polls) + bool mined = false; + + // We auto-calibrate the time it takes to mine the transaction. + // It would be better to go without polling, but that would probably need a change to the test client + + unsigned sleepTime = m_sleepTime; + size_t polls = 0; + for (; polls < 14 && !mined; ++polls) { + std::this_thread::sleep_for(chrono::milliseconds(sleepTime)); if (fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString())) >= startBlock + _number) - return; - std::this_thread::sleep_for(chrono::milliseconds(10)); //it does not work faster then 10 ms + mined = true; + else + sleepTime *= 2; + } + if (polls > 1) + { + m_successfulMineRuns = 0; + m_sleepTime += 2; + } + else if (polls == 1) + { + m_successfulMineRuns++; + if (m_successfulMineRuns > 5) + { + m_successfulMineRuns = 0; + if (m_sleepTime > 2) + m_sleepTime--; + } } - BOOST_FAIL("Error in test_mineBlocks: block mining timeout!"); + if (!mined) + BOOST_FAIL("Error in test_mineBlocks: block mining timeout!"); } void RPCSession::test_modifyTimestamp(size_t _timestamp) diff --git a/test/RPCSession.h b/test/RPCSession.h index 1a1fbbe5..2a9825b0 100644 --- a/test/RPCSession.h +++ b/test/RPCSession.h @@ -19,15 +19,37 @@ * @date 2016 */ -#include <string> -#include <stdio.h> -#include <map> +#if defined(_WIN32) +#include <windows.h> +#include "libdevcore/UndefMacros.h" +#else #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> +#endif + +#include <string> +#include <stdio.h> +#include <map> #include <json/value.h> #include <boost/test/unit_test.hpp> +#if defined(_WIN32) +const int c_buffsize = 5120000; //because windows pipe is broken and wont work as in examples. use larger buffer limit to receive whole package in one call +class IPCSocket : public boost::noncopyable +{ +public: + IPCSocket(std::string const& _path); + std::string sendRequest(std::string const& _req); + ~IPCSocket() { CloseHandle(m_socket); } + + std::string const& path() const { return m_path; } + +private: + std::string m_path; + HANDLE m_socket; +}; +#else class IPCSocket: public boost::noncopyable { public: @@ -41,8 +63,8 @@ private: FILE *m_fp; std::string m_path; int m_socket; - }; +#endif class RPCSession: public boost::noncopyable { @@ -102,6 +124,8 @@ private: IPCSocket m_ipcSocket; size_t m_rpcSequence = 1; + unsigned m_sleepTime = 10; + unsigned m_successfulMineRuns = 0; std::vector<std::string> m_accounts; }; diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp new file mode 100644 index 00000000..df35ff53 --- /dev/null +++ b/test/TestHelper.cpp @@ -0,0 +1,45 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** @file TestHelper.h +* @author Marko Simovic <markobarko@gmail.com> +* @date 2014 +*/ + +#include <boost/test/framework.hpp> +#include "TestHelper.h" +using namespace std; +using namespace dev::test; + +Options const& Options::get() +{ + static Options instance; + return instance; +} + +Options::Options() +{ + auto const& suite = boost::unit_test::framework::master_test_suite(); + for (auto i = 0; i < suite.argc; i++) + if (string(suite.argv[i]) == "--ipcpath" && i + 1 < suite.argc) + { + ipcPath = suite.argv[i + 1]; + i++; + } + if (ipcPath.empty()) + if (auto path = getenv("ETH_TEST_IPC")) + ipcPath = path; +} diff --git a/test/TestHelper.h b/test/TestHelper.h index 2d08d62c..2cb24fd7 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -102,5 +102,16 @@ namespace test } \ while (0) + +struct Options: boost::noncopyable +{ + std::string ipcPath; + + static Options const& get(); + +private: + Options(); +}; + } } diff --git a/test/boostTest.cpp b/test/boostTest.cpp index a2cfa5ee..4ddae0b7 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -25,6 +25,7 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" + #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4535) // calling _set_se_translator requires /EHa @@ -35,7 +36,3 @@ #endif #pragma GCC diagnostic pop - -#include <test/TestHelper.h> -using namespace boost::unit_test; - diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index 9aa47189..277de4eb 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -23,8 +23,7 @@ #include <string> #include <tuple> #include <boost/test/unit_test.hpp> -#include <libdevcore/Hash.h> -#include <libethcore/ABI.h> +#include <libdevcore/ABI.h> #include <test/libsolidity/SolidityExecutionFramework.h> using namespace std; @@ -40,7 +39,7 @@ namespace { static char const* registrarCode = R"DELIMITER( -//sol +pragma solidity ^0.4.0; contract NameRegister { function addr(string _name) constant returns (address o_owner); @@ -116,11 +115,6 @@ contract GlobalRegistrar is Registrar, AuctionSystem { // TODO: Populate with hall-of-fame. } - function() { - // prevent people from just sending funds to the registrar - throw; - } - function onAuctionEnd(string _name) internal { var auction = m_auctions[_name]; var record = m_toRecord[_name]; @@ -131,15 +125,13 @@ contract GlobalRegistrar is Registrar, AuctionSystem { if (previousOwner != 0) { if (!record.owner.send(auction.sumOfBids - auction.highestBid / 100)) throw; - } - else - { + } else { if (!auction.highestBidder.send(auction.highestBid - auction.secondHighestBid)) throw; } } - function reserve(string _name) external { + function reserve(string _name) external payable { if (bytes(_name).length == 0) throw; bool needAuction = requiresAuction(_name); @@ -148,9 +140,7 @@ contract GlobalRegistrar is Registrar, AuctionSystem { if (now < m_toRecord[_name].renewalDate) throw; bid(_name, msg.sender, msg.value); - } - else - { + } else { Record record = m_toRecord[_name]; if (record.owner != 0) throw; @@ -163,7 +153,7 @@ contract GlobalRegistrar is Registrar, AuctionSystem { return bytes(_name).length < c_freeBytes; } - modifier onlyrecordowner(string _name) { if (m_toRecord[_name].owner == msg.sender) _ } + modifier onlyrecordowner(string _name) { if (m_toRecord[_name].owner == msg.sender) _; } function transfer(string _name, address _newOwner) onlyrecordowner(_name) { m_toRecord[_name].owner = _newOwner; @@ -231,7 +221,7 @@ protected: if (!s_compiledRegistrar) { m_optimize = true; - m_compiler.reset(false, m_addStandardSources); + m_compiler.reset(false); m_compiler.addSource("", registrarCode); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); s_compiledRegistrar.reset(new bytes(m_compiler.object("GlobalRegistrar").bytecode)); @@ -333,7 +323,7 @@ BOOST_AUTO_TEST_CASE(double_reserve_long) registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), m_sender); - sendEther(account(1), u256(10) * eth::ether); + sendEther(account(1), u256(10) * ether); m_sender = account(1); registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), account(0)); @@ -350,7 +340,7 @@ BOOST_AUTO_TEST_CASE(properties) for (string const& name: names) { m_sender = account(0); - sendEther(account(count), u256(20) * eth::ether); + sendEther(account(count), u256(20) * ether); m_sender = account(count); auto sender = m_sender; addr += count; @@ -402,7 +392,7 @@ BOOST_AUTO_TEST_CASE(disown) BOOST_CHECK_EQUAL(registrar.name(u160(124)), name); // someone else tries disowning - sendEther(account(1), u256(10) * eth::ether); + sendEther(account(1), u256(10) * ether); m_sender = account(1); registrar.disown(name); BOOST_CHECK_EQUAL(registrar.owner(name), account(0)); @@ -451,7 +441,7 @@ BOOST_AUTO_TEST_CASE(auction_bidding) registrar.setNextValue(12); registrar.reserve(name); // another bid by someone else - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); m_rpc.test_modifyTimestamp(startTime + 2 * m_biddingTime - 50); registrar.setNextValue(13); @@ -480,7 +470,7 @@ BOOST_AUTO_TEST_CASE(auction_renewal) BOOST_CHECK_EQUAL(registrar.owner(name), m_sender); // try to re-register before interval end - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); m_rpc.test_modifyTimestamp(currentTimestamp() + m_renewalInterval - 1); registrar.setNextValue(80); diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp index 8cb8257a..736015fa 100644 --- a/test/contracts/FixedFeeRegistrar.cpp +++ b/test/contracts/FixedFeeRegistrar.cpp @@ -32,7 +32,6 @@ #pragma warning(pop) #endif -#include <libdevcore/Hash.h> #include <test/libsolidity/SolidityExecutionFramework.h> using namespace std; @@ -53,6 +52,8 @@ static char const* registrarCode = R"DELIMITER( // @authors: // Gav Wood <g@ethdev.com> +pragma solidity ^0.4.0; + contract Registrar { event Changed(string indexed name); @@ -70,9 +71,9 @@ contract FixedFeeRegistrar is Registrar { address owner; } - modifier onlyrecordowner(string _name) { if (m_record(_name).owner == msg.sender) _ } + modifier onlyrecordowner(string _name) { if (m_record(_name).owner == msg.sender) _; } - function reserve(string _name) { + function reserve(string _name) payable { Record rec = m_record(_name); if (rec.owner == 0 && msg.value >= c_fee) { rec.owner = msg.sender; @@ -132,7 +133,7 @@ protected: if (!s_compiledRegistrar) { m_optimize = true; - m_compiler.reset(false, m_addStandardSources); + m_compiler.reset(false); m_compiler.addSource("", registrarCode); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); s_compiledRegistrar.reset(new bytes(m_compiler.object("FixedFeeRegistrar").bytecode)); @@ -174,7 +175,7 @@ BOOST_AUTO_TEST_CASE(double_reserve) BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name)) == encodeArgs()); BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(h256(account(0), h256::AlignRight))); - sendEther(account(1), 100 * eth::ether); + sendEther(account(1), 100 * ether); m_sender = account(1); BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name)) == encodeArgs()); BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(h256(account(0), h256::AlignRight))); @@ -191,7 +192,7 @@ BOOST_AUTO_TEST_CASE(properties) { addr++; m_sender = account(0); - sendEther(account(count), 100 * eth::ether); + sendEther(account(count), 100 * ether); m_sender = account(count); Address owner = m_sender; // setting by sender works @@ -206,7 +207,7 @@ BOOST_AUTO_TEST_CASE(properties) count++; // but not by someone else m_sender = account(0); - sendEther(account(count), 100 * eth::ether); + sendEther(account(count), 100 * ether); m_sender = account(count); BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(h256(owner, h256::AlignRight))); BOOST_CHECK(callContractFunction("setAddr(string,address)", u256(0x40), addr + 1, u256(name.length()), name) == encodeArgs()); diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index fbab2404..ec968058 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -32,7 +32,6 @@ #pragma warning(pop) #endif -#include <libdevcore/Hash.h> #include <test/libsolidity/SolidityExecutionFramework.h> using namespace std; @@ -55,6 +54,9 @@ static char const* walletCode = R"DELIMITER( // use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by // some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the // interior is executed. + +pragma solidity ^0.4.0; + contract multiowned { // TYPES @@ -84,14 +86,14 @@ contract multiowned { // simple single-sig function modifier. modifier onlyowner { if (isOwner(msg.sender)) - _ + _; } // multi-sig function modifier: the operation must have an intrinsic hash in order // that later attempts can be realised as the same underlying operation and // thus count as confirmations. modifier onlymanyowners(bytes32 _operation) { if (confirmAndCheck(_operation)) - _ + _; } // METHODS @@ -279,7 +281,7 @@ contract daylimit is multiowned { // simple modifier for daily limit. modifier limitedDaily(uint _value) { if (underLimit(_value)) - _ + _; } // METHODS @@ -376,7 +378,7 @@ contract Wallet is multisig, multiowned, daylimit { } // gets called when no other function matches - function() { + function() payable { // just being sent some cash? if (msg.value > 0) Deposit(msg.sender, msg.value); @@ -446,7 +448,7 @@ protected: if (!s_compiledWallet) { m_optimize = true; - m_compiler.reset(false, m_addStandardSources); + m_compiler.reset(false); m_compiler.addSource("", walletCode); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); s_compiledWallet.reset(new bytes(m_compiler.object("Wallet").bytecode)); @@ -474,13 +476,13 @@ BOOST_AUTO_TEST_CASE(add_owners) BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(1), h256::AlignRight)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(account(1), h256::AlignRight)) == encodeArgs(true)); // now let the new owner add someone - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x13)) == encodeArgs(true)); // and check that a non-owner cannot add a new owner m_sender = account(0); - sendEther(account(2), 10 * eth::ether); + sendEther(account(2), 10 * ether); m_sender = account(2); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x20)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x20)) == encodeArgs(false)); @@ -559,17 +561,17 @@ BOOST_AUTO_TEST_CASE(multisig_value_transfer) // check that balance is and stays zero at destination address BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); m_sender = account(0); - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); auto ophash = callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00); BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); m_sender = account(0); - sendEther(account(2), 10 * eth::ether); + sendEther(account(2), 10 * ether); m_sender = account(2); callContractFunction("confirm(bytes32)", ophash); BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); m_sender = account(0); - sendEther(account(3), 10 * eth::ether); + sendEther(account(3), 10 * ether); m_sender = account(3); callContractFunction("confirm(bytes32)", ophash); // now it should go through @@ -586,11 +588,11 @@ BOOST_AUTO_TEST_CASE(revoke_addOwner) BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs()); // add a new owner Address deployer = m_sender; - h256 opHash = sha3(FixedHash<4>(dev::sha3("addOwner(address)")).asBytes() + h256(0x33).asBytes()); + h256 opHash = dev::keccak256(FixedHash<4>(dev::keccak256("addOwner(address)")).asBytes() + h256(0x33).asBytes()); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(false)); m_sender = account(0); - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(false)); @@ -598,12 +600,12 @@ BOOST_AUTO_TEST_CASE(revoke_addOwner) m_sender = deployer; BOOST_REQUIRE(callContractFunction("revoke(bytes32)", opHash) == encodeArgs()); m_sender = account(0); - sendEther(account(2), 10 * eth::ether); + sendEther(account(2), 10 * ether); m_sender = account(2); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(false)); m_sender = account(0); - sendEther(account(3), 10 * eth::ether); + sendEther(account(3), 10 * ether); m_sender = account(3); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(true)); @@ -621,24 +623,24 @@ BOOST_AUTO_TEST_CASE(revoke_transaction) Address deployer = m_sender; BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); m_sender = account(0); - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); auto opHash = callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00); BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); m_sender = account(0); - sendEther(account(2), 10 * eth::ether); + sendEther(account(2), 10 * ether); m_sender = account(2); callContractFunction("confirm(bytes32)", opHash); BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); m_sender = account(0); - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); BOOST_REQUIRE(callContractFunction("revoke(bytes32)", opHash) == encodeArgs()); m_sender = deployer; callContractFunction("confirm(bytes32)", opHash); BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); m_sender = account(0); - sendEther(account(3), 10 * eth::ether); + sendEther(account(3), 10 * ether); m_sender = account(3); callContractFunction("confirm(bytes32)", opHash); // now it should go through @@ -659,7 +661,7 @@ BOOST_AUTO_TEST_CASE(daylimit) // try to send tx over daylimit BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); BOOST_REQUIRE( callContractFunction("execute(address,uint256,bytes)", h256(0x05), 150, 0x60, 0x00) != @@ -668,7 +670,7 @@ BOOST_AUTO_TEST_CASE(daylimit) BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); // try to send tx under daylimit by stranger m_sender = account(0); - sendEther(account(4), 10 * eth::ether); + sendEther(account(4), 10 * ether); m_sender = account(4); BOOST_REQUIRE( callContractFunction("execute(address,uint256,bytes)", h256(0x05), 90, 0x60, 0x00) == @@ -677,7 +679,7 @@ BOOST_AUTO_TEST_CASE(daylimit) BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); // now send below limit by owner m_sender = account(0); - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); BOOST_REQUIRE( callContractFunction("execute(address,uint256,bytes)", h256(0x05), 90, 0x60, 0x00) == encodeArgs(u256(0)) diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp new file mode 100644 index 00000000..a0fc5dd7 --- /dev/null +++ b/test/libsolidity/ASTJSON.cpp @@ -0,0 +1,218 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2016 + * Tests for the json ast output. + */ + +#include <string> +#include <boost/test/unit_test.hpp> +#include <libsolidity/interface/Exceptions.h> +#include <libsolidity/interface/CompilerStack.h> +#include <libsolidity/ast/ASTJsonConverter.h> + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(SolidityASTJSON) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + CompilerStack c; + c.addSource("a", "contract C {}"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + BOOST_CHECK_EQUAL(astJson["name"], "SourceUnit"); +} + +BOOST_AUTO_TEST_CASE(source_location) +{ + CompilerStack c; + c.addSource("a", "contract C { function f() { var x = 2; x++; } }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + BOOST_CHECK_EQUAL(astJson["name"], "SourceUnit"); + BOOST_CHECK_EQUAL(astJson["children"][0]["name"], "ContractDefinition"); + BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["name"], "FunctionDefinition"); + BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["src"], "13:32:1"); +} + +BOOST_AUTO_TEST_CASE(inheritance_specifier) +{ + CompilerStack c; + c.addSource("a", "contract C1 {} contract C2 is C1 {}"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + BOOST_CHECK_EQUAL(astJson["children"][1]["attributes"]["name"], "C2"); + BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["name"], "InheritanceSpecifier"); + BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["src"], "30:2:1"); + BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["children"][0]["name"], "UserDefinedTypeName"); + BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["children"][0]["attributes"]["name"], "C1"); +} + +BOOST_AUTO_TEST_CASE(using_for_directive) +{ + CompilerStack c; + c.addSource("a", "library L {} contract C { using L for uint; }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value usingFor = astJson["children"][1]["children"][0]; + BOOST_CHECK_EQUAL(usingFor["name"], "UsingForDirective"); + BOOST_CHECK_EQUAL(usingFor["src"], "26:17:1"); + BOOST_CHECK_EQUAL(usingFor["children"][0]["name"], "UserDefinedTypeName"); + BOOST_CHECK_EQUAL(usingFor["children"][0]["attributes"]["name"], "L"); + BOOST_CHECK_EQUAL(usingFor["children"][1]["name"], "ElementaryTypeName"); + BOOST_CHECK_EQUAL(usingFor["children"][1]["attributes"]["name"], "uint"); +} + +BOOST_AUTO_TEST_CASE(enum_definition) +{ + CompilerStack c; + c.addSource("a", "contract C { enum E {} }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value enumDefinition = astJson["children"][0]["children"][0]; + BOOST_CHECK_EQUAL(enumDefinition["name"], "EnumDefinition"); + BOOST_CHECK_EQUAL(enumDefinition["attributes"]["name"], "E"); + BOOST_CHECK_EQUAL(enumDefinition["src"], "13:9:1"); +} + +BOOST_AUTO_TEST_CASE(enum_value) +{ + CompilerStack c; + c.addSource("a", "contract C { enum E { A, B } }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value enumDefinition = astJson["children"][0]["children"][0]; + BOOST_CHECK_EQUAL(enumDefinition["children"][0]["name"], "EnumValue"); + BOOST_CHECK_EQUAL(enumDefinition["children"][0]["attributes"]["name"], "A"); + BOOST_CHECK_EQUAL(enumDefinition["children"][0]["src"], "22:1:1"); + BOOST_CHECK_EQUAL(enumDefinition["children"][1]["name"], "EnumValue"); + BOOST_CHECK_EQUAL(enumDefinition["children"][1]["attributes"]["name"], "B"); + BOOST_CHECK_EQUAL(enumDefinition["children"][1]["src"], "25:1:1"); +} + +BOOST_AUTO_TEST_CASE(modifier_definition) +{ + CompilerStack c; + c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value modifier = astJson["children"][0]["children"][0]; + BOOST_CHECK_EQUAL(modifier["name"], "ModifierDefinition"); + BOOST_CHECK_EQUAL(modifier["attributes"]["name"], "M"); + BOOST_CHECK_EQUAL(modifier["src"], "13:25:1"); +} + +BOOST_AUTO_TEST_CASE(modifier_invocation) +{ + CompilerStack c; + c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value modifier = astJson["children"][0]["children"][1]["children"][2]; + BOOST_CHECK_EQUAL(modifier["name"], "ModifierInvocation"); + BOOST_CHECK_EQUAL(modifier["src"], "52:4:1"); + BOOST_CHECK_EQUAL(modifier["children"][0]["attributes"]["type"], "modifier (uint256)"); + BOOST_CHECK_EQUAL(modifier["children"][0]["attributes"]["value"], "M"); + BOOST_CHECK_EQUAL(modifier["children"][1]["attributes"]["value"], "1"); +} + +BOOST_AUTO_TEST_CASE(event_definition) +{ + CompilerStack c; + c.addSource("a", "contract C { event E(); }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value event = astJson["children"][0]["children"][0]; + BOOST_CHECK_EQUAL(event["name"], "EventDefinition"); + BOOST_CHECK_EQUAL(event["attributes"]["name"], "E"); + BOOST_CHECK_EQUAL(event["src"], "13:10:1"); +} + +BOOST_AUTO_TEST_CASE(array_type_name) +{ + CompilerStack c; + c.addSource("a", "contract C { uint[] i; }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value array = astJson["children"][0]["children"][0]["children"][0]; + BOOST_CHECK_EQUAL(array["name"], "ArrayTypeName"); + BOOST_CHECK_EQUAL(array["src"], "13:6:1"); +} + +BOOST_AUTO_TEST_CASE(placeholder_statement) +{ + CompilerStack c; + c.addSource("a", "contract C { modifier M { _; } }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value placeholder = astJson["children"][0]["children"][0]["children"][1]["children"][0]; + BOOST_CHECK_EQUAL(placeholder["name"], "PlaceholderStatement"); + BOOST_CHECK_EQUAL(placeholder["src"], "26:1:1"); +} + +BOOST_AUTO_TEST_CASE(non_utf8) +{ + CompilerStack c; + c.addSource("a", "contract C { function f() { var x = hex\"ff\"; } }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value literal = astJson["children"][0]["children"][0]["children"][2]["children"][0]["children"][1]; + BOOST_CHECK_EQUAL(literal["name"], "Literal"); + BOOST_CHECK_EQUAL(literal["attributes"]["hexvalue"], "ff"); + BOOST_CHECK_EQUAL(literal["attributes"]["token"], Json::nullValue); + BOOST_CHECK_EQUAL(literal["attributes"]["value"], Json::nullValue); + BOOST_CHECK(literal["attributes"]["type"].asString().find("invalid") != string::npos); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 557d496a..8d7a3540 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -114,8 +114,8 @@ BOOST_AUTO_TEST_CASE(location_test) shared_ptr<string const> n = make_shared<string>("source"); AssemblyItems items = compileContract(sourceCode); vector<SourceLocation> locations = - vector<SourceLocation>(17, SourceLocation(2, 75, n)) + - vector<SourceLocation>(28, SourceLocation(20, 72, n)) + + vector<SourceLocation>(18, SourceLocation(2, 75, n)) + + vector<SourceLocation>(31, SourceLocation(20, 72, n)) + vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + vector<SourceLocation>(4, SourceLocation(58, 67, n)) + vector<SourceLocation>(3, SourceLocation(20, 72, n)); diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index fc7a033f..fc103393 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -21,6 +21,7 @@ */ #include <test/libsolidity/SolidityExecutionFramework.h> +#include <libevmasm/EVMSchedule.h> #include <libevmasm/GasMeter.h> #include <libevmasm/KnownState.h> #include <libevmasm/PathGasMeter.h> @@ -45,7 +46,7 @@ public: GasMeterTestFramework() { } void compile(string const& _sourceCode) { - m_compiler.setSource(_sourceCode); + m_compiler.setSource("pragma solidity >= 0.0;" + _sourceCode); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(), "Compiling contract failed"); AssemblyItems const* items = m_compiler.runtimeAssemblyItems(""); @@ -81,7 +82,7 @@ public: { u256 gasUsed = 0; GasMeter::GasConsumption gas; - FixedHash<4> hash(dev::sha3(_sig)); + FixedHash<4> hash(dev::keccak256(_sig)); for (bytes const& arguments: _argumentVariants) { sendMessage(hash.asBytes() + arguments, false, 0); diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index 0736a853..1a9e16cc 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -39,106 +39,106 @@ BOOST_AUTO_TEST_SUITE(SolidityImports) BOOST_AUTO_TEST_CASE(smoke_test) { CompilerStack c; - c.addSource("a", "contract C {}"); + c.addSource("a", "contract C {} pragma solidity >=0.0;"); BOOST_CHECK(c.compile()); } BOOST_AUTO_TEST_CASE(regular_import) { CompilerStack c; - c.addSource("a", "contract C {}"); - c.addSource("b", "import \"a\"; contract D is C {}"); + c.addSource("a", "contract C {} pragma solidity >=0.0;"); + c.addSource("b", "import \"a\"; contract D is C {} pragma solidity >=0.0;"); BOOST_CHECK(c.compile()); } BOOST_AUTO_TEST_CASE(import_does_not_clutter_importee) { CompilerStack c; - c.addSource("a", "contract C { D d; }"); - c.addSource("b", "import \"a\"; contract D is C {}"); + c.addSource("a", "contract C { D d; } pragma solidity >=0.0;"); + c.addSource("b", "import \"a\"; contract D is C {} pragma solidity >=0.0;"); BOOST_CHECK(!c.compile()); } BOOST_AUTO_TEST_CASE(import_is_transitive) { CompilerStack c; - c.addSource("a", "contract C { }"); - c.addSource("b", "import \"a\";"); - c.addSource("c", "import \"b\"; contract D is C {}"); + c.addSource("a", "contract C { } pragma solidity >=0.0;"); + c.addSource("b", "import \"a\"; pragma solidity >=0.0;"); + c.addSource("c", "import \"b\"; contract D is C {} pragma solidity >=0.0;"); BOOST_CHECK(c.compile()); } BOOST_AUTO_TEST_CASE(circular_import) { CompilerStack c; - c.addSource("a", "import \"b\"; contract C { D d; }"); - c.addSource("b", "import \"a\"; contract D { C c; }"); + c.addSource("a", "import \"b\"; contract C { D d; } pragma solidity >=0.0;"); + c.addSource("b", "import \"a\"; contract D { C c; } pragma solidity >=0.0;"); BOOST_CHECK(c.compile()); } BOOST_AUTO_TEST_CASE(relative_import) { CompilerStack c; - c.addSource("a", "import \"./dir/b\"; contract A is B {}"); - c.addSource("dir/b", "contract B {}"); - c.addSource("dir/c", "import \"../a\"; contract C is A {}"); + c.addSource("a", "import \"./dir/b\"; contract A is B {} pragma solidity >=0.0;"); + c.addSource("dir/b", "contract B {} pragma solidity >=0.0;"); + c.addSource("dir/c", "import \"../a\"; contract C is A {} pragma solidity >=0.0;"); BOOST_CHECK(c.compile()); } BOOST_AUTO_TEST_CASE(relative_import_multiplex) { CompilerStack c; - c.addSource("a", "contract A {}"); - c.addSource("dir/a/b/c", "import \"../../.././a\"; contract B is A {}"); + c.addSource("a", "contract A {} pragma solidity >=0.0;"); + c.addSource("dir/a/b/c", "import \"../../.././a\"; contract B is A {} pragma solidity >=0.0;"); BOOST_CHECK(c.compile()); } BOOST_AUTO_TEST_CASE(simple_alias) { CompilerStack c; - c.addSource("a", "contract A {}"); - c.addSource("dir/a/b/c", "import \"../../.././a\" as x; contract B is x.A { function() { x.A r = x.A(20); } }"); + c.addSource("a", "contract A {} pragma solidity >=0.0;"); + c.addSource("dir/a/b/c", "import \"../../.././a\" as x; contract B is x.A { function() { x.A r = x.A(20); } } pragma solidity >=0.0;"); BOOST_CHECK(c.compile()); } BOOST_AUTO_TEST_CASE(library_name_clash) { CompilerStack c; - c.addSource("a", "library A {}"); - c.addSource("b", "library A {}"); + c.addSource("a", "library A {} pragma solidity >=0.0;"); + c.addSource("b", "library A {} pragma solidity >=0.0;"); BOOST_CHECK(!c.compile()); } BOOST_AUTO_TEST_CASE(library_name_clash_with_contract) { CompilerStack c; - c.addSource("a", "contract A {}"); - c.addSource("b", "library A {}"); + c.addSource("a", "contract A {} pragma solidity >=0.0;"); + c.addSource("b", "library A {} pragma solidity >=0.0;"); BOOST_CHECK(c.compile()); } BOOST_AUTO_TEST_CASE(complex_import) { CompilerStack c; - c.addSource("a", "contract A {} contract B {} contract C { struct S { uint a; } }"); + c.addSource("a", "contract A {} contract B {} contract C { struct S { uint a; } } pragma solidity >=0.0;"); c.addSource("b", "import \"a\" as x; import {B as b, C as c, C} from \"a\"; " - "contract D is b { function f(c.S var1, x.C.S var2, C.S var3) internal {} }"); + "contract D is b { function f(c.S var1, x.C.S var2, C.S var3) internal {} } pragma solidity >=0.0;"); BOOST_CHECK(c.compile()); } BOOST_AUTO_TEST_CASE(name_clash_in_import) { CompilerStack c; - c.addSource("a", "contract A {}"); - c.addSource("b", "import \"a\"; contract A {} "); + c.addSource("a", "contract A {} pragma solidity >=0.0;"); + c.addSource("b", "import \"a\"; contract A {} pragma solidity >=0.0;"); BOOST_CHECK(!c.compile()); - c.addSource("b", "import \"a\" as A; contract A {} "); + c.addSource("b", "import \"a\" as A; contract A {} pragma solidity >=0.0;"); BOOST_CHECK(!c.compile()); - c.addSource("b", "import {A as b} from \"a\"; contract b {} "); + c.addSource("b", "import {A as b} from \"a\"; contract b {} pragma solidity >=0.0;"); BOOST_CHECK(!c.compile()); - c.addSource("b", "import {A} from \"a\"; contract A {} "); + c.addSource("b", "import {A} from \"a\"; contract A {} pragma solidity >=0.0;"); BOOST_CHECK(!c.compile()); - c.addSource("b", "import {A} from \"a\"; contract B {} "); + c.addSource("b", "import {A} from \"a\"; contract B {} pragma solidity >=0.0;"); BOOST_CHECK(c.compile()); } @@ -146,10 +146,10 @@ BOOST_AUTO_TEST_CASE(remappings) { CompilerStack c; c.setRemappings(vector<string>{"s=s_1.4.6", "t=Tee"}); - c.addSource("a", "import \"s/s.sol\"; contract A is S {}"); - c.addSource("b", "import \"t/tee.sol\"; contract A is Tee {} "); - c.addSource("s_1.4.6/s.sol", "contract S {}"); - c.addSource("Tee/tee.sol", "contract Tee {}"); + c.addSource("a", "import \"s/s.sol\"; contract A is S {} pragma solidity >=0.0;"); + c.addSource("b", "import \"t/tee.sol\"; contract A is Tee {} pragma solidity >=0.0;"); + c.addSource("s_1.4.6/s.sol", "contract S {} pragma solidity >=0.0;"); + c.addSource("Tee/tee.sol", "contract Tee {} pragma solidity >=0.0;"); BOOST_CHECK(c.compile()); } @@ -157,10 +157,10 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings) { CompilerStack c; c.setRemappings(vector<string>{"a:s=s_1.4.6", "b:s=s_1.4.7"}); - c.addSource("a/a.sol", "import \"s/s.sol\"; contract A is SSix {}"); - c.addSource("b/b.sol", "import \"s/s.sol\"; contract B is SSeven {}"); - c.addSource("s_1.4.6/s.sol", "contract SSix {} "); - c.addSource("s_1.4.7/s.sol", "contract SSeven {} "); + c.addSource("a/a.sol", "import \"s/s.sol\"; contract A is SSix {} pragma solidity >=0.0;"); + c.addSource("b/b.sol", "import \"s/s.sol\"; contract B is SSeven {} pragma solidity >=0.0;"); + c.addSource("s_1.4.6/s.sol", "contract SSix {} pragma solidity >=0.0;"); + c.addSource("s_1.4.7/s.sol", "contract SSeven {} pragma solidity >=0.0;"); BOOST_CHECK(c.compile()); } diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 8d1a1d6c..136d72e6 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -87,9 +87,14 @@ BOOST_AUTO_TEST_CASE(simple_instructions) BOOST_CHECK(successParse("{ dup1 dup1 mul dup1 sub }")); } +BOOST_AUTO_TEST_CASE(suicide_selfdestruct) +{ + BOOST_CHECK(successParse("{ suicide selfdestruct }")); +} + BOOST_AUTO_TEST_CASE(keywords) { - BOOST_CHECK(successParse("{ byte return }")); + BOOST_CHECK(successParse("{ byte return address }")); } BOOST_AUTO_TEST_CASE(constants) diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp new file mode 100644 index 00000000..80bdf16f --- /dev/null +++ b/test/libsolidity/SemVerMatcher.cpp @@ -0,0 +1,223 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <chris@ethereum.org> + * @date 2016 + * Unit tests for the semantic versioning matcher. + */ + +#include <string> +#include <vector> +#include <tuple> +#include <libsolidity/parsing/Scanner.h> +#include <libsolidity/analysis/SemVerHandler.h> +#include "../TestHelper.h" + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(SemVerMatcher) + +SemVerMatchExpression parseExpression(string const& _input) +{ + Scanner scanner{CharStream(_input)}; + vector<string> literals; + vector<Token::Value> tokens; + while (scanner.currentToken() != Token::EOS) + { + auto token = scanner.currentToken(); + string literal = scanner.currentLiteral(); + if (literal.empty() && Token::toString(token)) + literal = Token::toString(token); + literals.push_back(literal); + tokens.push_back(token); + scanner.next(); + } + + auto expression = SemVerMatchExpressionParser(tokens, literals).parse(); + BOOST_CHECK_MESSAGE( + expression.isValid(), + "Expression \"" + _input + "\" did not parse properly." + ); + return expression; +} + +BOOST_AUTO_TEST_CASE(positive_range) +{ + // Positive range tests + vector<pair<string, string>> tests = { + {"*", "1.2.3-foo"}, + {"1.0.0 - 2.0.0", "1.2.3"}, + {"1.0.0", "1.0.0"}, + {">=*", "0.2.4"}, + {"*", "1.2.3"}, + {">=1.0.0", "1.0.0"}, + {">=1.0.0", "1.0.1"}, + {">=1.0.0", "1.1.0"}, + {">1.0.0", "1.0.1"}, + {">1.0.0", "1.1.0"}, + {"<=2.0.0", "2.0.0"}, + {"<=2.0.0", "1.9999.9999"}, + {"<=2.0.0", "0.2.9"}, + {"<2.0.0", "1.9999.9999"}, + {"<2.0.0", "0.2.9"}, + {">= 1.0.0", "1.0.0"}, + {">= 1.0.0", "1.0.1"}, + {">= 1.0.0", "1.1.0"}, + {"> 1.0.0", "1.0.1"}, + {"> 1.0.0", "1.1.0"}, + {"<= 2.0.0", "2.0.0"}, + {"<= 2.0.0", "1.9999.9999"}, + {"<= 2.0.0", "0.2.9"}, + {"< 2.0.0", "1.9999.9999"}, + {"<\t2.0.0", "0.2.9"}, + {">=0.1.97", "0.1.97"}, + {"0.1.20 || 1.2.4", "1.2.4"}, + {">=0.2.3 || <0.0.1", "0.0.0"}, + {">=0.2.3 || <0.0.1", "0.2.3"}, + {">=0.2.3 || <0.0.1", "0.2.4"}, + {"\"2.x.x\"", "2.1.3"}, + {"1.2.x", "1.2.3"}, + {"\"1.2.x\" || \"2.x\"", "2.1.3"}, + {"\"1.2.x\" || \"2.x\"", "1.2.3"}, + {"x", "1.2.3"}, + {"2.*.*", "2.1.3"}, + {"1.2.*", "1.2.3"}, + {"1.2.* || 2.*", "2.1.3"}, + {"1.2.* || 2.*", "1.2.3"}, + {"*", "1.2.3"}, + {"2", "2.1.2"}, + {"2.3", "2.3.1"}, + {"~2.4", "2.4.0"}, // >=2.4.0 <2.5.0 + {"~2.4", "2.4.5"}, + {"~1", "1.2.3"}, // >=1.0.0 <2.0.0 + {"~1.0", "1.0.2"}, // >=1.0.0 <1.1.0, + {"~ 1.0", "1.0.2"}, + {"~ 1.0.3", "1.0.12"}, + {">=1", "1.0.0"}, + {">= 1", "1.0.0"}, + {"<1.2", "1.1.1"}, + {"< 1.2", "1.1.1"}, + {"=0.7.x", "0.7.2"}, + {"<=0.7.x", "0.7.2"}, + {">=0.7.x", "0.7.2"}, + {"<=0.7.x", "0.6.2"}, + {"~1.2.1 >=1.2.3", "1.2.3"}, + {"~1.2.1 =1.2.3", "1.2.3"}, + {"~1.2.1 1.2.3", "1.2.3"}, + {"~1.2.1 >=1.2.3 1.2.3", "1.2.3"}, + {"~1.2.1 1.2.3 >=1.2.3", "1.2.3"}, + {">=\"1.2.1\" 1.2.3", "1.2.3"}, + {"1.2.3 >=1.2.1", "1.2.3"}, + {">=1.2.3 >=1.2.1", "1.2.3"}, + {">=1.2.1 >=1.2.3", "1.2.3"}, + {">=1.2", "1.2.8"}, + {"^1.2.3", "1.8.1"}, + {"^0.1.2", "0.1.2"}, + {"^0.1", "0.1.2"}, + {"^1.2", "1.4.2"}, + {"<=1.2.3", "1.2.3-beta"}, + {">1.2", "1.3.0-beta"}, + {"<1.2.3", "1.2.3-beta"}, + {"^1.2 ^1", "1.4.2"} + }; + for (auto const& t: tests) + { + SemVerVersion version(t.second); + SemVerMatchExpression expression = parseExpression(t.first); + BOOST_CHECK_MESSAGE( + expression.matches(version), + "Version \"" + t.second + "\" did not satisfy expression \"" + t.first + "\"" + ); + } +} + +BOOST_AUTO_TEST_CASE(negative_range) +{ + // Positive range tests + vector<pair<string, string>> tests = { + {"1.0.0 - 2.0.0", "2.2.3"}, + {"^1.2.3", "1.2.3-pre"}, + {"^1.2", "1.2.0-pre"}, + {"^1.2.3", "1.2.3-beta"}, + {"=0.7.x", "0.7.0-asdf"}, + {">=0.7.x", "0.7.0-asdf"}, + {"1.0.0", "1.0.1"}, + {">=1.0.0", "0.0.0"}, + {">=1.0.0", "0.0.1"}, + {">=1.0.0", "0.1.0"}, + {">1.0.0", "0.0.1"}, + {">1.0.0", "0.1.0"}, + {"<=2.0.0", "3.0.0"}, + {"<=2.0.0", "2.9999.9999"}, + {"<=2.0.0", "2.2.9"}, + {"<2.0.0", "2.9999.9999"}, + {"<2.0.0", "2.2.9"}, + {">=0.1.97", "0.1.93"}, + {"0.1.20 || 1.2.4", "1.2.3"}, + {">=0.2.3 || <0.0.1", "0.0.3"}, + {">=0.2.3 || <0.0.1", "0.2.2"}, + {"\"2.x.x\"", "1.1.3"}, + {"\"2.x.x\"", "3.1.3"}, + {"1.2.x", "1.3.3"}, + {"\"1.2.x\" || \"2.x\"", "3.1.3"}, + {"\"1.2.x\" || \"2.x\"", "1.1.3"}, + {"2.*.*", "1.1.3"}, + {"2.*.*", "3.1.3"}, + {"1.2.*", "1.3.3"}, + {"1.2.* || 2.*", "3.1.3"}, + {"1.2.* || 2.*", "1.1.3"}, + {"2", "1.1.2"}, + {"2.3", "2.4.1"}, + {"~2.4", "2.5.0"}, // >=2.4.0 <2.5.0 + {"~2.4", "2.3.9"}, + {"~1", "0.2.3"}, // >=1.0.0 <2.0.0 + {"~1.0", "1.1.0"}, // >=1.0.0 <1.1.0 + {"<1", "1.0.0"}, + {">=1.2", "1.1.1"}, + {"=0.7.x", "0.8.2"}, + {">=0.7.x", "0.6.2"}, + {"<0.7.x", "0.7.2"}, + {"=1.2.3", "1.2.3-beta"}, + {">1.2", "1.2.8"}, + {"^1.2.3", "2.0.0-alpha"}, + {"^1.2.3", "1.2.2"}, + {"^1.2", "1.1.9"} + }; + for (auto const& t: tests) + { + SemVerVersion version(t.second); + SemVerMatchExpression expression = parseExpression(t.first); + BOOST_CHECK_MESSAGE( + !expression.matches(version), + "Version \"" + t.second + "\" did satisfy expression \"" + t.first + "\" " + + "(although it should not)" + ); + } +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 77b39978..073d7d97 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -35,11 +35,11 @@ namespace test class JSONInterfaceChecker { public: - JSONInterfaceChecker(): m_compilerStack(false) {} + JSONInterfaceChecker(): m_compilerStack() {} void checkInterface(std::string const& _code, std::string const& _expectedInterfaceString) { - ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse(_code), "Parsing contract failed"); + ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse("pragma solidity >=0.0;\n" + _code), "Parsing contract failed"); std::string generatedInterfaceString = m_compilerStack.metadata("", DocumentationType::ABIInterface); Json::Value generatedInterface; m_reader.parse(generatedInterfaceString, generatedInterface); @@ -68,6 +68,7 @@ BOOST_AUTO_TEST_CASE(basic_test) { "name": "f", "constant": false, + "payable" : false, "type": "function", "inputs": [ { @@ -107,6 +108,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods) { "name": "f", "constant": false, + "payable" : false, "type": "function", "inputs": [ { @@ -124,6 +126,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods) { "name": "g", "constant": false, + "payable" : false, "type": "function", "inputs": [ { @@ -153,6 +156,7 @@ BOOST_AUTO_TEST_CASE(multiple_params) { "name": "f", "constant": false, + "payable" : false, "type": "function", "inputs": [ { @@ -188,6 +192,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order) { "name": "c", "constant": false, + "payable" : false, "type": "function", "inputs": [ { @@ -205,6 +210,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order) { "name": "f", "constant": false, + "payable" : false, "type": "function", "inputs": [ { @@ -235,6 +241,7 @@ BOOST_AUTO_TEST_CASE(const_function) { "name": "foo", "constant": false, + "payable" : false, "type": "function", "inputs": [ { @@ -256,6 +263,7 @@ BOOST_AUTO_TEST_CASE(const_function) { "name": "boo", "constant": true, + "payable" : false, "type": "function", "inputs": [{ "name": "a", @@ -273,15 +281,6 @@ BOOST_AUTO_TEST_CASE(const_function) checkInterface(sourceCode, interface); } -BOOST_AUTO_TEST_CASE(exclude_fallback_function) -{ - char const* sourceCode = "contract test { function() {} }"; - - char const* interface = "[]"; - - checkInterface(sourceCode, interface); -} - BOOST_AUTO_TEST_CASE(events) { char const* sourceCode = "contract test {\n" @@ -293,6 +292,7 @@ BOOST_AUTO_TEST_CASE(events) { "name": "f", "constant": false, + "payable" : false, "type": "function", "inputs": [ { @@ -370,6 +370,7 @@ BOOST_AUTO_TEST_CASE(inherited) { "name": "baseFunction", "constant": false, + "payable" : false, "type": "function", "inputs": [{ @@ -385,6 +386,7 @@ BOOST_AUTO_TEST_CASE(inherited) { "name": "derivedFunction", "constant": false, + "payable" : false, "type": "function", "inputs": [{ @@ -438,6 +440,7 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) { "name": "f", "constant": false, + "payable" : false, "type": "function", "inputs": [ { @@ -478,6 +481,7 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter) { "name": "f", "constant": false, + "payable" : false, "type": "function", "inputs": [ { @@ -545,6 +549,7 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi) [ { "constant" : false, + "payable" : false, "inputs" : [], "name" : "ret", "outputs" : [ @@ -582,6 +587,7 @@ BOOST_AUTO_TEST_CASE(strings_and_arrays) [ { "constant" : false, + "payable" : false, "name": "f", "inputs": [ { "name": "a", "type": "string" }, @@ -609,6 +615,7 @@ BOOST_AUTO_TEST_CASE(library_function) [ { "constant" : false, + "payable" : false, "name": "f", "inputs": [ { "name": "b", "type": "test.StructType storage" }, @@ -626,6 +633,76 @@ BOOST_AUTO_TEST_CASE(library_function) checkInterface(sourceCode, interface); } +BOOST_AUTO_TEST_CASE(include_fallback_function) +{ + char const* sourceCode = R"( + contract test { + function() {} + } + )"; + + char const* interface = R"( + [ + { + "payable": false, + "type" : "fallback" + } + ] + )"; + checkInterface(sourceCode, interface); +} + +BOOST_AUTO_TEST_CASE(payable_function) +{ + char const* sourceCode = R"( + contract test { + function f() {} + function g() payable {} + } + )"; + + char const* interface = R"( + [ + { + "constant" : false, + "payable": false, + "inputs": [], + "name": "f", + "outputs": [], + "type" : "function" + }, + { + "constant" : false, + "payable": true, + "inputs": [], + "name": "g", + "outputs": [], + "type" : "function" + } + ] + )"; + checkInterface(sourceCode, interface); +} + +BOOST_AUTO_TEST_CASE(payable_fallback_unction) +{ + char const* sourceCode = R"( + contract test { + function () payable {} + } + )"; + + char const* interface = R"( + [ + { + "payable": true, + "type" : "fallback" + } + ] + )"; + checkInterface(sourceCode, interface); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 8dcc878e..155f117f 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -21,14 +21,15 @@ * Unit tests for the solidity expression compiler, testing the behaviour of the code. */ +#include <functional> #include <string> #include <tuple> #include <boost/test/unit_test.hpp> -#include <libdevcore/Hash.h> #include <libsolidity/interface/Exceptions.h> #include <test/libsolidity/SolidityExecutionFramework.h> using namespace std; +using namespace std::placeholders; namespace dev { @@ -1259,7 +1260,7 @@ BOOST_AUTO_TEST_CASE(multiple_elementary_accessors) compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("data()") == encodeArgs(8)); BOOST_CHECK(callContractFunction("name()") == encodeArgs("Celina")); - BOOST_CHECK(callContractFunction("a_hash()") == encodeArgs(dev::sha3(bytes(1, 0x7b)))); + BOOST_CHECK(callContractFunction("a_hash()") == encodeArgs(dev::keccak256(bytes(1, 0x7b)))); BOOST_CHECK(callContractFunction("an_address()") == encodeArgs(toBigEndian(u160(0x1337)))); BOOST_CHECK(callContractFunction("super_secret_data()") == bytes()); } @@ -1319,7 +1320,7 @@ BOOST_AUTO_TEST_CASE(balance) BOOST_AUTO_TEST_CASE(blockchain) { char const* sourceCode = "contract test {\n" - " function someInfo() returns (uint256 value, address coinbase, uint256 blockNumber) {\n" + " function someInfo() payable returns (uint256 value, address coinbase, uint256 blockNumber) {\n" " value = msg.value;\n" " coinbase = block.coinbase;\n" " blockNumber = block.number;\n" @@ -1341,7 +1342,7 @@ BOOST_AUTO_TEST_CASE(msg_sig) } )"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunctionWithValue("foo(uint256)", 13) == encodeArgs(asString(FixedHash<4>(dev::sha3("foo(uint256)")).asBytes()))); + BOOST_CHECK(callContractFunction("foo(uint256)") == encodeArgs(asString(FixedHash<4>(dev::keccak256("foo(uint256)")).asBytes()))); } BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same) @@ -1357,7 +1358,7 @@ BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same) } )"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunctionWithValue("foo(uint256)", 13) == encodeArgs(asString(FixedHash<4>(dev::sha3("foo(uint256)")).asBytes()))); + BOOST_CHECK(callContractFunction("foo(uint256)") == encodeArgs(asString(FixedHash<4>(dev::keccak256("foo(uint256)")).asBytes()))); } BOOST_AUTO_TEST_CASE(now) @@ -1525,8 +1526,10 @@ BOOST_AUTO_TEST_CASE(convert_uint_to_fixed_bytes_greater_size) } })"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("UintToBytes(uint16)", u256("0x6162")) == - encodeArgs(string("\0\0\0\0\0\0ab", 8))); + BOOST_CHECK( + callContractFunction("UintToBytes(uint16)", u256("0x6162")) == + encodeArgs(string("\0\0\0\0\0\0ab", 8)) + ); } BOOST_AUTO_TEST_CASE(send_ether) @@ -1683,7 +1686,7 @@ BOOST_AUTO_TEST_CASE(sha3) compileAndRun(sourceCode); auto f = [&](u256 const& _x) -> u256 { - return dev::sha3(toBigEndian(_x)); + return dev::keccak256(toBigEndian(_x)); }; testSolidityAgainstCpp("a(bytes32)", f, u256(4)); testSolidityAgainstCpp("a(bytes32)", f, u256(5)); @@ -1698,9 +1701,15 @@ BOOST_AUTO_TEST_CASE(sha256) " }\n" "}\n"; compileAndRun(sourceCode); - auto f = [&](u256 const& _input) -> u256 + auto f = [&](u256 const& _x) -> bytes { - return dev::sha256(dev::ref(toBigEndian(_input))); + if (_x == u256(4)) + return fromHex("e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f"); + if (_x == u256(5)) + return fromHex("96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47"); + if (_x == u256(-1)) + return fromHex("af9613760f72635fbdb44a5a0a63c39f12af30f950a6ee5c971be188e89c4051"); + return fromHex(""); }; testSolidityAgainstCpp("a(bytes32)", f, u256(4)); testSolidityAgainstCpp("a(bytes32)", f, u256(5)); @@ -1715,9 +1724,15 @@ BOOST_AUTO_TEST_CASE(ripemd) " }\n" "}\n"; compileAndRun(sourceCode); - auto f = [&](u256 const& _input) -> u256 + auto f = [&](u256 const& _x) -> bytes { - return h256(dev::ripemd160(h256(_input).ref()), h256::AlignLeft); // This should be aligned right. i guess it's fixed elsewhere? + if (_x == u256(4)) + return fromHex("1b0f3c404d12075c68c938f9f60ebea4f74941a0000000000000000000000000"); + if (_x == u256(5)) + return fromHex("ee54aa84fc32d8fed5a5fe160442ae84626829d9000000000000000000000000"); + if (_x == u256(-1)) + return fromHex("1cf4e77f5966e13e109703cd8a0df7ceda7f3dc3000000000000000000000000"); + return fromHex(""); }; testSolidityAgainstCpp("a(bytes32)", f, u256(4)); testSolidityAgainstCpp("a(bytes32)", f, u256(5)); @@ -2040,10 +2055,11 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses) { char const* sourceCode = R"( contract helper { + function() payable { } // can receive ether } contract test { helper h; - function test() { h = new helper(); h.send(5); } + function test() payable { h = new helper(); h.send(5); } function getBalance() returns (uint256 myBalance, uint256 helperBalance) { myBalance = this.balance; helperBalance = h.balance; @@ -2051,6 +2067,7 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses) } )"; compileAndRun(sourceCode, 20); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 20 - 5); BOOST_REQUIRE(callContractFunction("getBalance()") == encodeArgs(u256(20 - 5), u256(5))); } @@ -2059,7 +2076,7 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic) char const* sourceCode = R"( contract helper { bool flag; - function getBalance() returns (uint256 myBalance) { + function getBalance() payable returns (uint256 myBalance) { return this.balance; } function setFlag() { flag = true; } @@ -2067,8 +2084,8 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic) } contract test { helper h; - function test() { h = new helper(); } - function sendAmount(uint amount) returns (uint256 bal) { + function test() payable { h = new helper(); } + function sendAmount(uint amount) payable returns (uint256 bal) { return h.getBalance.value(amount)(); } function outOfGas() returns (bool ret) { @@ -2084,8 +2101,8 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic) compileAndRun(sourceCode, 20); BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(5)); // call to helper should not succeed but amount should be transferred anyway - BOOST_REQUIRE(callContractFunction("outOfGas()", 5) == bytes()); - BOOST_REQUIRE(callContractFunction("checkState()", 5) == encodeArgs(false, 20 - 5)); + BOOST_REQUIRE(callContractFunction("outOfGas()") == bytes()); + BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5)); } BOOST_AUTO_TEST_CASE(gas_for_builtin) @@ -2107,14 +2124,14 @@ BOOST_AUTO_TEST_CASE(value_complex) { char const* sourceCode = R"( contract helper { - function getBalance() returns (uint256 myBalance) { + function getBalance() payable returns (uint256 myBalance) { return this.balance; } } contract test { helper h; - function test() { h = new helper(); } - function sendAmount(uint amount) returns (uint256 bal) { + function test() payable { h = new helper(); } + function sendAmount(uint amount) payable returns (uint256 bal) { var x1 = h.getBalance.value(amount); uint someStackElement = 20; var x2 = x1.gas(1000); @@ -2130,13 +2147,13 @@ BOOST_AUTO_TEST_CASE(value_insane) { char const* sourceCode = R"( contract helper { - function getBalance() returns (uint256 myBalance) { + function getBalance() payable returns (uint256 myBalance) { return this.balance; } } contract test { helper h; - function test() { h = new helper(); } + function test() payable { h = new helper(); } function sendAmount(uint amount) returns (uint256 bal) { var x1 = h.getBalance.value; var x2 = x1(amount).gas; @@ -2155,7 +2172,7 @@ BOOST_AUTO_TEST_CASE(value_for_constructor) contract Helper { bytes3 name; bool flag; - function Helper(bytes3 x, bool f) { + function Helper(bytes3 x, bool f) payable { name = x; flag = f; } @@ -2164,7 +2181,7 @@ BOOST_AUTO_TEST_CASE(value_for_constructor) } contract Main { Helper h; - function Main() { + function Main() payable { h = (new Helper).value(10)("abc", true); } function getFlag() returns (bool ret) { return h.getFlag(); } @@ -2338,8 +2355,8 @@ BOOST_AUTO_TEST_CASE(function_modifier) { char const* sourceCode = R"( contract C { - function getOne() nonFree returns (uint r) { return 1; } - modifier nonFree { if (msg.value > 0) _ } + function getOne() payable nonFree returns (uint r) { return 1; } + modifier nonFree { if (msg.value > 0) _; } } )"; compileAndRun(sourceCode); @@ -2351,8 +2368,8 @@ BOOST_AUTO_TEST_CASE(function_modifier_local_variables) { char const* sourceCode = R"( contract C { - modifier mod1 { var a = 1; var b = 2; _ } - modifier mod2(bool a) { if (a) return; else _ } + modifier mod1 { var a = 1; var b = 2; _; } + modifier mod2(bool a) { if (a) return; else _; } function f(bool a) mod1 mod2(a) returns (uint r) { return 3; } } )"; @@ -2365,7 +2382,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_loop) { char const* sourceCode = R"( contract C { - modifier repeat(uint count) { for (var i = 0; i < count; ++i) _ } + modifier repeat(uint count) { for (var i = 0; i < count; ++i) _; } function f() repeat(10) returns (uint r) { r += 1; } } )"; @@ -2377,7 +2394,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_multi_invocation) { char const* sourceCode = R"( contract C { - modifier repeat(bool twice) { if (twice) _ _ } + modifier repeat(bool twice) { if (twice) _; _; } function f(bool twice) repeat(twice) returns (uint r) { r += 1; } } )"; @@ -2388,16 +2405,17 @@ BOOST_AUTO_TEST_CASE(function_modifier_multi_invocation) BOOST_AUTO_TEST_CASE(function_modifier_multi_with_return) { - // Here, the explicit return prevents the second execution + // Note that return sets the return variable and jumps to the end of the current function or + // modifier code block. char const* sourceCode = R"( contract C { - modifier repeat(bool twice) { if (twice) _ _ } + modifier repeat(bool twice) { if (twice) _; _; } function f(bool twice) repeat(twice) returns (uint r) { r += 1; return r; } } )"; compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(1)); - BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(1)); + BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(2)); } BOOST_AUTO_TEST_CASE(function_modifier_overriding) @@ -2405,10 +2423,10 @@ BOOST_AUTO_TEST_CASE(function_modifier_overriding) char const* sourceCode = R"( contract A { function f() mod returns (bool r) { return true; } - modifier mod { _ } + modifier mod { _; } } contract C is A { - modifier mod { } + modifier mod { if (false) _; } } )"; compileAndRun(sourceCode); @@ -2424,12 +2442,12 @@ BOOST_AUTO_TEST_CASE(function_modifier_calling_functions_in_creation_context) function f1() mod2 { data |= 0x1; } function f2() { data |= 0x20; } function f3() { } - modifier mod1 { f2(); _ } - modifier mod2 { f3(); } + modifier mod1 { f2(); _; } + modifier mod2 { f3(); if (false) _; } function getData() returns (uint r) { return data; } } contract C is A { - modifier mod1 { f4(); _ } + modifier mod1 { f4(); _; } function f3() { data |= 0x300; } function f4() { data |= 0x4000; } } @@ -2444,32 +2462,17 @@ BOOST_AUTO_TEST_CASE(function_modifier_for_constructor) contract A { uint data; function A() mod1 { data |= 2; } - modifier mod1 { data |= 1; _ } + modifier mod1 { data |= 1; _; } function getData() returns (uint r) { return data; } } contract C is A { - modifier mod1 { data |= 4; _ } + modifier mod1 { data |= 4; _; } } )"; compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("getData()") == encodeArgs(4 | 2)); } -BOOST_AUTO_TEST_CASE(use_std_lib) -{ - char const* sourceCode = R"( - import "mortal"; - contract Icarus is mortal { } - )"; - m_addStandardSources = true; - u256 amount(130 * eth::ether); - compileAndRun(sourceCode, amount, "Icarus"); - u256 balanceBefore = balanceAt(m_sender); - BOOST_CHECK(callContractFunction("kill()") == bytes()); - BOOST_CHECK(!addressHasCode(m_contractAddress)); - BOOST_CHECK(balanceAt(m_sender) > balanceBefore); -} - BOOST_AUTO_TEST_CASE(crazy_elementary_typenames_on_stack) { char const* sourceCode = R"( @@ -2515,13 +2518,13 @@ BOOST_AUTO_TEST_CASE(fallback_function) char const* sourceCode = R"( contract A { uint data; - function() returns (uint r) { data = 1; return 2; } + function() { data = 1; } function getData() returns (uint r) { return data; } } )"; compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("getData()") == encodeArgs(0)); - BOOST_CHECK(callContractFunction("") == encodeArgs(2)); + BOOST_CHECK(callContractFunction("") == encodeArgs()); BOOST_CHECK(callContractFunction("getData()") == encodeArgs(1)); } @@ -2530,28 +2533,42 @@ BOOST_AUTO_TEST_CASE(inherited_fallback_function) char const* sourceCode = R"( contract A { uint data; - function() returns (uint r) { data = 1; return 2; } + function() { data = 1; } function getData() returns (uint r) { return data; } } contract B is A {} )"; compileAndRun(sourceCode, 0, "B"); BOOST_CHECK(callContractFunction("getData()") == encodeArgs(0)); - BOOST_CHECK(callContractFunction("") == encodeArgs(2)); + BOOST_CHECK(callContractFunction("") == encodeArgs()); BOOST_CHECK(callContractFunction("getData()") == encodeArgs(1)); } +BOOST_AUTO_TEST_CASE(default_fallback_throws) +{ + char const* sourceCode = R"( + contract A { + function f() returns (bool) { + return this.call(); + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(0)); +} + BOOST_AUTO_TEST_CASE(event) { char const* sourceCode = R"( contract ClientReceipt { event Deposit(address indexed _from, bytes32 indexed _id, uint _value); - function deposit(bytes32 _id, bool _manually) { + function deposit(bytes32 _id, bool _manually) payable { if (_manually) { bytes32 s = 0x19dacbf83c5de6658e14cbf7bcae5c15eca2eedecf1c66fbca928e4d351bea0f; log3(bytes32(msg.value), s, bytes32(msg.sender), _id); - } else + } else { Deposit(msg.sender, _id, msg.value); + } } } )"; @@ -2565,7 +2582,7 @@ BOOST_AUTO_TEST_CASE(event) BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(value))); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 3); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(address,bytes32,uint256)"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,bytes32,uint256)"))); BOOST_CHECK_EQUAL(m_logs[0].topics[1], h256(m_sender, h256::AlignRight)); BOOST_CHECK_EQUAL(m_logs[0].topics[2], h256(id)); } @@ -2587,7 +2604,7 @@ BOOST_AUTO_TEST_CASE(event_no_arguments) BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); BOOST_CHECK(m_logs[0].data.empty()); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit()"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()"))); } BOOST_AUTO_TEST_CASE(event_anonymous) @@ -2610,7 +2627,7 @@ BOOST_AUTO_TEST_CASE(event_anonymous_with_topics) char const* sourceCode = R"( contract ClientReceipt { event Deposit(address indexed _from, bytes32 indexed _id, uint indexed _value, uint indexed _value2, bytes32 data) anonymous; - function deposit(bytes32 _id, bool _manually) { + function deposit(bytes32 _id, bool _manually) payable { Deposit(msg.sender, _id, msg.value, 2, "abc"); } } @@ -2634,7 +2651,7 @@ BOOST_AUTO_TEST_CASE(event_lots_of_data) char const* sourceCode = R"( contract ClientReceipt { event Deposit(address _from, bytes32 _id, uint _value, bool _flag); - function deposit(bytes32 _id) { + function deposit(bytes32 _id) payable { Deposit(msg.sender, _id, msg.value, true); } } @@ -2647,7 +2664,7 @@ BOOST_AUTO_TEST_CASE(event_lots_of_data) BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); BOOST_CHECK(m_logs[0].data == encodeArgs((u160)m_sender, id, value, true)); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(address,bytes32,uint256,bool)"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,bytes32,uint256,bool)"))); } BOOST_AUTO_TEST_CASE(event_really_lots_of_data) @@ -2664,9 +2681,9 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data) callContractFunction("deposit()"); BOOST_REQUIRE_EQUAL(m_logs.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); - BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 4) + FixedHash<4>(dev::sha3("deposit()")).asBytes()); + BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 4) + FixedHash<4>(dev::keccak256("deposit()")).asBytes()); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)"))); } BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage) @@ -2690,7 +2707,7 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage) BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 3, string("ABC"))); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)"))); } BOOST_AUTO_TEST_CASE(event_indexed_string) @@ -2721,11 +2738,11 @@ BOOST_AUTO_TEST_CASE(event_indexed_string) dynx[i] = i; BOOST_CHECK(m_logs[0].data == bytes()); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 3); - BOOST_CHECK_EQUAL(m_logs[0].topics[1], dev::sha3(dynx)); - BOOST_CHECK_EQUAL(m_logs[0].topics[2], dev::sha3( + BOOST_CHECK_EQUAL(m_logs[0].topics[1], dev::keccak256(dynx)); + BOOST_CHECK_EQUAL(m_logs[0].topics[2], dev::keccak256( encodeArgs(u256(4), u256(5), u256(6), u256(7)) )); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("E(string,uint256[4])"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(string,uint256[4])"))); } BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) @@ -2767,7 +2784,7 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments) compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("foo(uint256,uint256,uint256)", 10, 12, 13) == encodeArgs( - dev::sha3( + dev::keccak256( toBigEndian(u256(10)) + toBigEndian(u256(12)) + toBigEndian(u256(13))))); @@ -2785,7 +2802,7 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_numeric_literals) compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("foo(uint256,uint16)", 10, 12) == encodeArgs( - dev::sha3( + dev::keccak256( toBigEndian(u256(10)) + bytes{0x0, 0xc} + bytes(1, 0x91)))); @@ -2806,10 +2823,10 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_string_literals) })"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("foo()") == encodeArgs(dev::sha3("foo"))); + BOOST_CHECK(callContractFunction("foo()") == encodeArgs(dev::keccak256("foo"))); BOOST_CHECK(callContractFunction("bar(uint256,uint16)", 10, 12) == encodeArgs( - dev::sha3( + dev::keccak256( toBigEndian(u256(10)) + bytes{0x0, 0xc} + bytes(1, 0x91) + @@ -2851,7 +2868,27 @@ BOOST_AUTO_TEST_CASE(iterated_sha3_with_bytes) )"; compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("foo()") == encodeArgs( - u256(dev::sha3(bytes{'b'} + dev::sha3("xyz").asBytes() + bytes{'a'})) + u256(dev::keccak256(bytes{'b'} + dev::keccak256("xyz").asBytes() + bytes{'a'})) + )); +} + +BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments) +{ + char const* sourceCode = R"( + contract c { + function foo(uint a, uint b, uint c) returns (bytes32 d) + { + d = keccak256(a, b, c); + } + })"; + compileAndRun(sourceCode); + + BOOST_CHECK(callContractFunction("foo(uint256,uint256,uint256)", 10, 12, 13) == encodeArgs( + dev::keccak256( + toBigEndian(u256(10)) + + toBigEndian(u256(12)) + + toBigEndian(u256(13)) + ) )); } @@ -2860,9 +2897,10 @@ BOOST_AUTO_TEST_CASE(generic_call) char const* sourceCode = R"**( contract receiver { uint public received; - function receive(uint256 x) { received = x; } + function receive(uint256 x) payable { received = x; } } contract sender { + function sender() payable {} function doSend(address rec) returns (uint d) { bytes4 signature = bytes4(bytes32(sha3("receive(uint256)"))); @@ -2883,10 +2921,11 @@ BOOST_AUTO_TEST_CASE(generic_callcode) char const* sourceCode = R"**( contract receiver { uint public received; - function receive(uint256 x) { received = x; } + function receive(uint256 x) payable { received = x; } } contract sender { uint public received; + function sender() payable { } function doSend(address rec) returns (uint d) { bytes4 signature = bytes4(bytes32(sha3("receive(uint256)"))); @@ -2916,16 +2955,16 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall) uint public received; address public sender; uint public value; - function receive(uint256 x) { received = x; sender = msg.sender; value = msg.value; } + function receive(uint256 x) payable { received = x; sender = msg.sender; value = msg.value; } } contract sender { uint public received; address public sender; uint public value; - function doSend(address rec) + function doSend(address rec) payable { bytes4 signature = bytes4(bytes32(sha3("receive(uint256)"))); - rec.delegatecall(signature, 23); + if (rec.delegatecall(signature, 23)) {} } } )**"; @@ -2988,15 +3027,15 @@ BOOST_AUTO_TEST_CASE(bytes_from_calldata_to_memory) { char const* sourceCode = R"( contract C { - function() returns (bytes32) { + function f() returns (bytes32) { return sha3("abc", msg.data); } } )"; compileAndRun(sourceCode); - bytes calldata1 = bytes(61, 0x22) + bytes(12, 0x12); + bytes calldata1 = FixedHash<4>(dev::keccak256("f()")).asBytes() + bytes(61, 0x22) + bytes(12, 0x12); sendMessage(calldata1, false); - BOOST_CHECK(m_output == encodeArgs(dev::sha3(bytes{'a', 'b', 'c'} + calldata1))); + BOOST_CHECK(m_output == encodeArgs(dev::keccak256(bytes{'a', 'b', 'c'} + calldata1))); } BOOST_AUTO_TEST_CASE(call_forward_bytes) @@ -3010,7 +3049,7 @@ BOOST_AUTO_TEST_CASE(call_forward_bytes) contract sender { function sender() { rec = new receiver(); } function() { savedData = msg.data; } - function forward() returns (bool) { rec.call(savedData); return true; } + function forward() returns (bool) { !rec.call(savedData); return true; } function clear() returns (bool) { delete savedData; return true; } function val() returns (uint) { return rec.received(); } receiver rec; @@ -3370,8 +3409,8 @@ BOOST_AUTO_TEST_CASE(bytes_in_arguments) )"; compileAndRun(sourceCode); - string innercalldata1 = asString(FixedHash<4>(dev::sha3("f(uint256,uint256)")).asBytes() + encodeArgs(8, 9)); - string innercalldata2 = asString(FixedHash<4>(dev::sha3("g(uint256)")).asBytes() + encodeArgs(3)); + string innercalldata1 = asString(FixedHash<4>(dev::keccak256("f(uint256,uint256)")).asBytes() + encodeArgs(8, 9)); + string innercalldata2 = asString(FixedHash<4>(dev::keccak256("g(uint256)")).asBytes() + encodeArgs(3)); bytes calldata = encodeArgs( 12, 32 * 4, u256(32 * 4 + 32 + (innercalldata1.length() + 31) / 32 * 32), 13, u256(innercalldata1.length()), innercalldata1, @@ -4328,12 +4367,12 @@ BOOST_AUTO_TEST_CASE(external_types_in_calls) y = this.t1(C1(7)); } function t1(C1 a) returns (C1) { return a; } - function() returns (C1) { return C1(9); } + function t2() returns (C1) { return C1(9); } } )"; compileAndRun(sourceCode, 0, "C"); BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(9), u256(7))); - BOOST_CHECK(callContractFunction("nonexisting") == encodeArgs(u256(9))); + BOOST_CHECK(callContractFunction("t2()") == encodeArgs(u256(9))); } BOOST_AUTO_TEST_CASE(proper_order_of_overwriting_of_attributes) @@ -4606,6 +4645,26 @@ BOOST_AUTO_TEST_CASE(failing_send) BOOST_REQUIRE(callContractFunction("callHelper(address)", c_helperAddress) == encodeArgs(true, 20)); } +BOOST_AUTO_TEST_CASE(send_zero_ether) +{ + // Sending zero ether to a contract should still invoke the fallback function + // (it previously did not because the gas stipend was not provided by the EVM) + char const* sourceCode = R"( + contract Receiver { + function () payable { + } + } + contract Main { + function s() returns (bool) { + var r = new Receiver(); + return r.send(0); + } + } + )"; + compileAndRun(sourceCode, 20, "Main"); + BOOST_REQUIRE(callContractFunction("s()") == encodeArgs(true)); +} + BOOST_AUTO_TEST_CASE(reusing_memory) { // Invoke some features that use memory and test that they do not interfere with each other. @@ -4629,7 +4688,7 @@ BOOST_AUTO_TEST_CASE(reusing_memory) } )"; compileAndRun(sourceCode, 0, "Main"); - BOOST_REQUIRE(callContractFunction("f(uint256)", 0x34) == encodeArgs(dev::sha3(dev::toBigEndian(u256(0x34))))); + BOOST_REQUIRE(callContractFunction("f(uint256)", 0x34) == encodeArgs(dev::keccak256(dev::toBigEndian(u256(0x34))))); } BOOST_AUTO_TEST_CASE(return_string) @@ -5921,9 +5980,9 @@ BOOST_AUTO_TEST_CASE(version_stamp_for_libraries) m_optimize = true; bytes runtimeCode = compileAndRun(sourceCode, 0, "lib"); BOOST_CHECK(runtimeCode.size() >= 8); - BOOST_CHECK_EQUAL(runtimeCode[0], int(eth::Instruction::PUSH6)); // might change once we switch to 1.x.x - BOOST_CHECK_EQUAL(runtimeCode[1], 3); // might change once we switch away from x.3.x - BOOST_CHECK_EQUAL(runtimeCode[7], int(eth::Instruction::POP)); + BOOST_CHECK_EQUAL(runtimeCode[0], int(Instruction::PUSH6)); // might change once we switch to 1.x.x + BOOST_CHECK_EQUAL(runtimeCode[1], 4); // might change once we switch away from x.4.x + BOOST_CHECK_EQUAL(runtimeCode[7], int(Instruction::POP)); } BOOST_AUTO_TEST_CASE(contract_binary_dependencies) @@ -5944,6 +6003,7 @@ BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library) function f(address x) returns (bool) { return x.send(1); } + function () payable {} } )"; compileAndRun(sourceCode, 0, "lib"); @@ -6206,6 +6266,27 @@ BOOST_AUTO_TEST_CASE(addmod_mulmod) BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(0))); } +BOOST_AUTO_TEST_CASE(divisiod_by_zero) +{ + char const* sourceCode = R"( + contract C { + function div(uint a, uint b) returns (uint) { + return a / b; + } + function mod(uint a, uint b) returns (uint) { + return a % b; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("div(uint256,uint256)", 7, 2) == encodeArgs(u256(3))); + // throws + BOOST_CHECK(callContractFunction("div(uint256,uint256)", 7, 0) == encodeArgs()); + BOOST_CHECK(callContractFunction("mod(uint256,uint256)", 7, 2) == encodeArgs(u256(1))); + // throws + BOOST_CHECK(callContractFunction("mod(uint256,uint256)", 7, 0) == encodeArgs()); +} + BOOST_AUTO_TEST_CASE(string_allocation_bug) { char const* sourceCode = R"( @@ -6837,6 +6918,339 @@ BOOST_AUTO_TEST_CASE(skip_dynamic_types_for_structs) BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(2), u256(6))); } +BOOST_AUTO_TEST_CASE(failed_create) +{ + char const* sourceCode = R"( + contract D { function D() payable {} } + contract C { + uint public x; + function f(uint amount) returns (address) { + x++; + return (new D).value(amount)(); + } + function stack(uint depth) returns (address) { + if (depth < 1024) + return this.stack(depth - 1); + else + return f(0); + } + } + )"; + compileAndRun(sourceCode, 20, "C"); + BOOST_CHECK(callContractFunction("f(uint256)", 20) != encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("f(uint256)", 20) == encodeArgs()); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("stack(uint256)", 1023) == encodeArgs()); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(1))); +} + +BOOST_AUTO_TEST_CASE(create_dynamic_array_with_zero_length) +{ + char const* sourceCode = R"( + contract C { + function f() returns (uint) { + var a = new uint[][](0); + return 7; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(7))); +} + +BOOST_AUTO_TEST_CASE(return_does_not_skip_modifier) +{ + char const* sourceCode = R"( + contract C { + uint public x; + modifier setsx { + _; + x = 9; + } + function f() setsx returns (uint) { + return 2; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(2))); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(9))); +} + +BOOST_AUTO_TEST_CASE(break_in_modifier) +{ + char const* sourceCode = R"( + contract C { + uint public x; + modifier run() { + for (uint i = 0; i < 10; i++) { + _; + break; + } + } + function f() run { + x++; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("f()") == encodeArgs()); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(1))); +} + +BOOST_AUTO_TEST_CASE(stacked_return_with_modifiers) +{ + char const* sourceCode = R"( + contract C { + uint public x; + modifier run() { + for (uint i = 0; i < 10; i++) { + _; + break; + } + } + function f() run { + x++; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("f()") == encodeArgs()); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(1))); +} + +BOOST_AUTO_TEST_CASE(mutex) +{ + char const* sourceCode = R"( + contract mutexed { + bool locked; + modifier protected { + if (locked) throw; + locked = true; + _; + locked = false; + } + } + contract Fund is mutexed { + uint shares; + function Fund() { shares = msg.value; } + function withdraw(uint amount) protected returns (uint) { + // NOTE: It is very bad practice to write this function this way. + // Please refer to the documentation of how to do this properly. + if (amount > shares) throw; + if (!msg.sender.call.value(amount)()) throw; + shares -= amount; + return shares; + } + function withdrawUnprotected(uint amount) returns (uint) { + // NOTE: It is very bad practice to write this function this way. + // Please refer to the documentation of how to do this properly. + if (amount > shares) throw; + if (!msg.sender.call.value(amount)()) throw; + shares -= amount; + return shares; + } + } + contract Attacker { + Fund public fund; + uint callDepth; + bool protected; + function setProtected(bool _protected) { protected = _protected; } + function Attacker(Fund _fund) { fund = _fund; } + function attack() returns (uint) { + callDepth = 0; + return attackInternal(); + } + function attackInternal() internal returns (uint) { + if (protected) + return fund.withdraw(10); + else + return fund.withdrawUnprotected(10); + } + function() payable { + callDepth++; + if (callDepth < 4) + attackInternal(); + } + } + )"; + compileAndRun(sourceCode, 500, "Fund"); + auto fund = m_contractAddress; + BOOST_CHECK_EQUAL(balanceAt(fund), 500); + compileAndRun(sourceCode, 0, "Attacker", encodeArgs(u160(fund))); + BOOST_CHECK(callContractFunction("setProtected(bool)", true) == encodeArgs()); + BOOST_CHECK(callContractFunction("attack()") == encodeArgs()); + BOOST_CHECK_EQUAL(balanceAt(fund), 500); + BOOST_CHECK(callContractFunction("setProtected(bool)", false) == encodeArgs()); + BOOST_CHECK(callContractFunction("attack()") == encodeArgs(u256(460))); + BOOST_CHECK_EQUAL(balanceAt(fund), 460); +} + +BOOST_AUTO_TEST_CASE(failing_ecrecover_invalid_input) +{ + // ecrecover should return zero for malformed input + // (v should be 27 or 28, not 1) + // Note that the precompile does not return zero but returns nothing. + char const* sourceCode = R"( + contract C { + function f() returns (address) { + return ecrecover(bytes32(uint(-1)), 1, 2, 3); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0))); +} + +BOOST_AUTO_TEST_CASE(calling_nonexisting_contract_throws) +{ + char const* sourceCode = R"( + contract D { function g(); } + contract C { + D d = D(0x1212); + function f() returns (uint) { + d.g(); + return 7; + } + function g() returns (uint) { + d.g.gas(200)(); + return 7; + } + function h() returns (uint) { + d.call(); // this does not throw (low-level) + return 7; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs()); + BOOST_CHECK(callContractFunction("g()") == encodeArgs()); + BOOST_CHECK(callContractFunction("h()") == encodeArgs(u256(7))); +} + +BOOST_AUTO_TEST_CASE(payable_constructor) +{ + char const* sourceCode = R"( + contract C { + function C() payable { } + } + )"; + compileAndRun(sourceCode, 27, "C"); +} + +BOOST_AUTO_TEST_CASE(payable_function) +{ + char const* sourceCode = R"( + contract C { + uint public a; + function f() payable returns (uint) { + return msg.value; + } + function() payable { + a = msg.value + 1; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs(u256(27))); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27); + BOOST_CHECK(callContractFunctionWithValue("", 27) == encodeArgs()); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27 + 27); + BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(28))); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27 + 27); +} + +BOOST_AUTO_TEST_CASE(payable_function_calls_library) +{ + char const* sourceCode = R"( + library L { + function f() returns (uint) { return 7; } + } + contract C { + function f() payable returns (uint) { + return L.f(); + } + } + )"; + compileAndRun(sourceCode, 0, "L"); + compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"L", m_contractAddress}}); + BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs(u256(7))); +} + +BOOST_AUTO_TEST_CASE(non_payable_throw) +{ + char const* sourceCode = R"( + contract C { + uint public a; + function f() returns (uint) { + return msg.value; + } + function() { + a = msg.value + 1; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs()); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); + BOOST_CHECK(callContractFunction("") == encodeArgs()); + BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunctionWithValue("", 27) == encodeArgs()); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); + BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunctionWithValue("a()", 27) == encodeArgs()); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); +} + +BOOST_AUTO_TEST_CASE(no_nonpayable_circumvention_by_modifier) +{ + char const* sourceCode = R"( + contract C { + modifier tryCircumvent { + if (false) _; // avoid the function, we should still not accept ether + } + function f() tryCircumvent returns (uint) { + return msg.value; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs()); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); +} + +BOOST_AUTO_TEST_CASE(mem_resize_is_not_paid_at_call) +{ + // This tests that memory resize for return values is not paid during the call, which would + // make the gas calculation overly complex. We access the end of the output area before + // the call is made. + // Tests that this also survives the optimizer. + char const* sourceCode = R"( + contract C { + function f() returns (uint[200]) {} + } + contract D { + function f(C c) returns (uint) { c.f(); return 7; } + } + )"; + + compileAndRun(sourceCode, 0, "C"); + u160 cAddr = m_contractAddress; + compileAndRun(sourceCode, 0, "D"); + BOOST_CHECK(callContractFunction("f(address)", cAddr) == encodeArgs(u256(7))); + + m_optimize = true; + + compileAndRun(sourceCode, 0, "C"); + u160 cAddrOpt = m_contractAddress; + compileAndRun(sourceCode, 0, "D"); + BOOST_CHECK(callContractFunction("f(address)", cAddrOpt) == encodeArgs(u256(7))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index da9cf5f1..02548121 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -22,6 +22,7 @@ #include <cstdlib> #include <boost/test/framework.hpp> +#include <libdevcore/CommonIO.h> #include <test/libsolidity/SolidityExecutionFramework.h> using namespace std; @@ -29,26 +30,17 @@ using namespace dev; using namespace dev::solidity; using namespace dev::solidity::test; -string getIPCSocketPath() +namespace // anonymous { - string ipcPath; + h256 const EmptyTrie("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); +} - size_t argc = boost::unit_test::framework::master_test_suite().argc; - char** argv = boost::unit_test::framework::master_test_suite().argv; - for (size_t i = 0; i < argc; i++) - { - string arg = argv[i]; - if (arg == "--ipc" && i + 1 < argc) - { - ipcPath = argv[i + 1]; - i++; - } - } - if (ipcPath.empty()) - if (auto path = getenv("ETH_TEST_IPC")) - ipcPath = path; +string getIPCSocketPath() +{ + string ipcPath = dev::test::Options::get().ipcPath; if (ipcPath.empty()) - BOOST_FAIL("ERROR: ipcPath not set! (use --ipc <path> or the environment variable ETH_TEST_IPC)"); + BOOST_FAIL("ERROR: ipcPath not set! (use --ipcpath <path> or the environment variable ETH_TEST_IPC)"); + return ipcPath; } @@ -56,9 +48,6 @@ ExecutionFramework::ExecutionFramework() : m_rpc(RPCSession::instance(getIPCSocketPath())), m_sender(m_rpc.account(0)) { - if (g_logVerbosity != -1) - g_logVerbosity = 0; - m_rpc.test_rewindToBlock(0); } diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 2b589498..1cd45603 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -22,25 +22,34 @@ #pragma once -#include <string> -#include <tuple> -#include <fstream> +#include <functional> + #include "../TestHelper.h" #include "../RPCSession.h" -#include <libethcore/ABI.h> -#include <libethcore/SealEngine.h> -#include <libethereum/State.h> -#include <libethereum/Executive.h> -#include <libethereum/ChainParams.h> + +#include <libdevcore/ABI.h> +#include <libdevcore/FixedHash.h> +#include <libevmasm/Instruction.h> + #include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/Exceptions.h> -#include <libethcore/BasicAuthority.h> -#include <libethcore/SealEngine.h> +#include <libsolidity/interface/SourceReferenceFormatter.h> namespace dev { namespace solidity { + /// An Ethereum address: 20 bytes. + /// @NOTE This is not endian-specific; it's just a bunch of bytes. + using Address = h160; + + // The various denominations; here for ease of use where needed within code. + static const u256 ether = exp10<18>(); + static const u256 finney = exp10<15>(); + static const u256 szabo = exp10<12>(); + static const u256 shannon = exp10<9>(); + static const u256 wei = exp10<0>(); + namespace test { @@ -58,9 +67,21 @@ public: std::map<std::string, Address> const& _libraryAddresses = std::map<std::string, Address>() ) { - m_compiler.reset(false, m_addStandardSources); - m_compiler.addSource("", _sourceCode); - ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); + // Silence compiler version warning + std::string sourceCode = "pragma solidity >=0.0;\n" + _sourceCode; + m_compiler.reset(false); + m_compiler.addSource("", sourceCode); + if (!m_compiler.compile(m_optimize, m_optimizeRuns)) + { + for (auto const& error: m_compiler.errors()) + SourceReferenceFormatter::printExceptionInformation( + std::cerr, + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error", + [&](std::string const& _sourceName) -> solidity::Scanner const& { return m_compiler.scanner(_sourceName); } + ); + BOOST_ERROR("Compiling contract failed"); + } eth::LinkerObject obj = m_compiler.object(_contractName); obj.link(_libraryAddresses); BOOST_REQUIRE(obj.linkReferences.empty()); @@ -84,7 +105,7 @@ public: template <class... Args> bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, Args const&... _arguments) { - FixedHash<4> hash(dev::sha3(_sig)); + FixedHash<4> hash(dev::keccak256(_sig)); sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value); return m_output; } @@ -271,12 +292,10 @@ protected: size_t m_optimizeRuns = 200; bool m_optimize = false; - bool m_addStandardSources = false; dev::solidity::CompilerStack m_compiler; Address m_sender; Address m_contractAddress; - eth::EnvInfo m_envInfo; - u256 const m_gasPrice = 100 * eth::szabo; + u256 const m_gasPrice = 100 * szabo; u256 const m_gas = 100000000; bytes m_output; std::vector<LogEntry> m_logs; diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 967b2907..e9a05745 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -323,7 +323,15 @@ BOOST_AUTO_TEST_CASE(arithmetics) byte(Instruction::OR), byte(Instruction::SUB), byte(Instruction::ADD), + byte(Instruction::DUP2), + byte(Instruction::ISZERO), + byte(Instruction::PUSH1), 0x2, + byte(Instruction::JUMPI), byte(Instruction::MOD), + byte(Instruction::DUP2), + byte(Instruction::ISZERO), + byte(Instruction::PUSH1), 0x2, + byte(Instruction::JUMPI), byte(Instruction::DIV), byte(Instruction::MUL)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); diff --git a/test/libsolidity/SolidityInterface.cpp b/test/libsolidity/SolidityInterface.cpp deleted file mode 100644 index 9a1c104d..00000000 --- a/test/libsolidity/SolidityInterface.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum 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. - - cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. - */ -/** - * @author Christian <c@ethdev.com> - * @date 2015 - * Unit tests for generating source interfaces for Solidity contracts. - */ - -#include "../TestHelper.h" -#include <libsolidity/interface/CompilerStack.h> -#include <libsolidity/ast/AST.h> - -using namespace std; - -namespace dev -{ -namespace solidity -{ -namespace test -{ - -class SolidityInterfaceChecker -{ -public: - SolidityInterfaceChecker(): m_compilerStack(false) {} - - /// Compiles the given code, generates the interface and parses that again. - ContractDefinition const& checkInterface(string const& _code, string const& _contractName = "") - { - m_code = _code; - ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse(_code), "Parsing failed"); - m_interface = m_compilerStack.metadata("", DocumentationType::ABISolidityInterface); - ETH_TEST_REQUIRE_NO_THROW(m_reCompiler.parse(m_interface), "Interface parsing failed"); - return m_reCompiler.contractDefinition(_contractName); - } - - string sourcePart(ASTNode const& _node) const - { - SourceLocation location = _node.location(); - BOOST_REQUIRE(!location.isEmpty()); - return m_interface.substr(location.start, location.end - location.start); - } - -protected: - string m_code; - string m_interface; - CompilerStack m_compilerStack; - CompilerStack m_reCompiler; -}; - -BOOST_FIXTURE_TEST_SUITE(SolidityInterface, SolidityInterfaceChecker) - -BOOST_AUTO_TEST_CASE(empty_contract) -{ - ContractDefinition const& contract = checkInterface("contract test {}"); - BOOST_CHECK_EQUAL(sourcePart(contract), "contract test{}"); -} - -BOOST_AUTO_TEST_CASE(single_function) -{ - ContractDefinition const& contract = checkInterface( - "contract test {\n" - " function f(uint a) returns(uint d) { return a * 7; }\n" - "}\n"); - BOOST_REQUIRE_EQUAL(1, contract.definedFunctions().size()); - BOOST_CHECK_EQUAL(sourcePart(*contract.definedFunctions().front()), - "function f(uint256 a)returns(uint256 d);"); -} - -BOOST_AUTO_TEST_CASE(single_constant_function) -{ - ContractDefinition const& contract = checkInterface( - "contract test { function f(uint a) constant returns(bytes1 x) { 1==2; } }"); - BOOST_REQUIRE_EQUAL(1, contract.definedFunctions().size()); - BOOST_CHECK_EQUAL(sourcePart(*contract.definedFunctions().front()), - "function f(uint256 a)constant returns(bytes1 x);"); -} - -BOOST_AUTO_TEST_CASE(multiple_functions) -{ - char const* sourceCode = "contract test {\n" - " function f(uint a) returns(uint d) { return a * 7; }\n" - " function g(uint b) returns(uint e) { return b * 8; }\n" - "}\n"; - ContractDefinition const& contract = checkInterface(sourceCode); - set<string> expectation({"function f(uint256 a)returns(uint256 d);", - "function g(uint256 b)returns(uint256 e);"}); - BOOST_REQUIRE_EQUAL(2, contract.definedFunctions().size()); - BOOST_CHECK(expectation == set<string>({sourcePart(*contract.definedFunctions().at(0)), - sourcePart(*contract.definedFunctions().at(1))})); -} - -BOOST_AUTO_TEST_CASE(exclude_fallback_function) -{ - char const* sourceCode = "contract test { function() {} }"; - ContractDefinition const& contract = checkInterface(sourceCode); - BOOST_CHECK_EQUAL(sourcePart(contract), "contract test{}"); -} - -BOOST_AUTO_TEST_CASE(events) -{ - char const* sourceCode = "contract test {\n" - " function f(uint a) returns(uint d) { return a * 7; }\n" - " event e1(uint b, address indexed c); \n" - " event e2(); \n" - "}\n"; - ContractDefinition const& contract = checkInterface(sourceCode); - // events should not appear in the Solidity Interface - BOOST_REQUIRE_EQUAL(0, contract.events().size()); -} - -BOOST_AUTO_TEST_CASE(inheritance) -{ - char const* sourceCode = - " contract Base { \n" - " function baseFunction(uint p) returns (uint i) { return p; } \n" - " event baseEvent(bytes32 indexed evtArgBase); \n" - " } \n" - " contract Derived is Base { \n" - " function derivedFunction(bytes32 p) returns (bytes32 i) { return p; } \n" - " event derivedEvent(uint indexed evtArgDerived); \n" - " }"; - ContractDefinition const& contract = checkInterface(sourceCode); - set<string> expectedFunctions({"function baseFunction(uint256 p)returns(uint256 i);", - "function derivedFunction(bytes32 p)returns(bytes32 i);"}); - BOOST_REQUIRE_EQUAL(2, contract.definedFunctions().size()); - BOOST_CHECK(expectedFunctions == set<string>({sourcePart(*contract.definedFunctions().at(0)), - sourcePart(*contract.definedFunctions().at(1))})); -} - -BOOST_AUTO_TEST_CASE(libraries) -{ - char const* sourceCode = R"( - library Lib { - struct Str { uint a; } - enum E { E1, E2 } - function f(uint[] x,Str storage y,E z) external; - } - )"; - ContractDefinition const& contract = checkInterface(sourceCode); - BOOST_CHECK(contract.isLibrary()); - set<string> expectedFunctions({"function f(uint256[] x,Lib.Str storage y,Lib.E z);"}); - BOOST_REQUIRE_EQUAL(1, contract.definedFunctions().size()); - BOOST_CHECK(expectedFunctions == set<string>({sourcePart(*contract.definedFunctions().at(0))})); -} - -BOOST_AUTO_TEST_SUITE_END() - -} -} -} diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 7e81bd7e..76141f41 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -45,15 +45,17 @@ namespace { pair<ASTPointer<SourceUnit>, std::shared_ptr<Error::Type const>> -parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false) +parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, bool _insertVersionPragma = true) { + // Silence compiler version warning + string source = _insertVersionPragma ? "pragma solidity >=0.0;\n" + _source : _source; ErrorList errors; Parser parser(errors); ASTPointer<SourceUnit> sourceUnit; // catch exceptions for a transition period try { - sourceUnit = parser.parse(std::make_shared<Scanner>(CharStream(_source))); + sourceUnit = parser.parse(std::make_shared<Scanner>(CharStream(source))); if(!sourceUnit) return make_pair(sourceUnit, nullptr); @@ -152,7 +154,7 @@ static FunctionTypePointer retrieveFunctionBySignature( std::string const& _signature ) { - FixedHash<4> hash(dev::sha3(_signature)); + FixedHash<4> hash(dev::keccak256(_signature)); return _contract->interfaceFunctions()[hash]; } @@ -445,8 +447,8 @@ BOOST_AUTO_TEST_CASE(function_no_implementation) "}\n"; ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); - ContractDefinition* contract = dynamic_cast<ContractDefinition*>(nodes[0].get()); - BOOST_CHECK(contract); + ContractDefinition* contract = dynamic_cast<ContractDefinition*>(nodes[1].get()); + BOOST_REQUIRE(contract); BOOST_CHECK(!contract->annotation().isFullyImplemented); BOOST_CHECK(!contract->definedFunctions()[0]->isImplemented()); } @@ -460,12 +462,12 @@ BOOST_AUTO_TEST_CASE(abstract_contract) )"; ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); - ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[0].get()); - ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[1].get()); - BOOST_CHECK(base); + ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[1].get()); + ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get()); + BOOST_REQUIRE(base); BOOST_CHECK(!base->annotation().isFullyImplemented); BOOST_CHECK(!base->definedFunctions()[0]->isImplemented()); - BOOST_CHECK(derived); + BOOST_REQUIRE(derived); BOOST_CHECK(derived->annotation().isFullyImplemented); BOOST_CHECK(derived->definedFunctions()[0]->isImplemented()); } @@ -479,8 +481,8 @@ BOOST_AUTO_TEST_CASE(abstract_contract_with_overload) )"; ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); - ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[0].get()); - ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[1].get()); + ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[1].get()); + ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get()); BOOST_REQUIRE(base); BOOST_CHECK(!base->annotation().isFullyImplemented); BOOST_REQUIRE(derived); @@ -527,9 +529,9 @@ BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_not_provided) )"; ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name resolving failed"); std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); - BOOST_CHECK_EQUAL(nodes.size(), 3); - ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get()); - BOOST_CHECK(derived); + BOOST_CHECK_EQUAL(nodes.size(), 4); + ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[3].get()); + BOOST_REQUIRE(derived); BOOST_CHECK(!derived->annotation().isFullyImplemented); } @@ -553,9 +555,9 @@ BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor) )"; ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name resolving failed"); std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); - BOOST_CHECK_EQUAL(nodes.size(), 2); - ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[1].get()); - BOOST_CHECK(derived); + BOOST_CHECK_EQUAL(nodes.size(), 3); + ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get()); + BOOST_REQUIRE(derived); BOOST_CHECK(!derived->annotation().isFullyImplemented); } @@ -857,8 +859,8 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation) char const* text = R"( contract B { function f() mod1(2, true) mod2("0123456") { } - modifier mod1(uint a, bool b) { if (b) _ } - modifier mod2(bytes7 a) { while (a == "1234567") _ } + modifier mod1(uint a, bool b) { if (b) _; } + modifier mod2(bytes7 a) { while (a == "1234567") _; } } )"; BOOST_CHECK(success(text)); @@ -869,7 +871,7 @@ BOOST_AUTO_TEST_CASE(invalid_function_modifier_type) char const* text = R"( contract B { function f() mod1(true) { } - modifier mod1(uint a) { if (a > 0) _ } + modifier mod1(uint a) { if (a > 0) _; } } )"; BOOST_CHECK(expectError(text) == Error::Type::TypeError); @@ -880,8 +882,8 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_parameters) char const* text = R"( contract B { function f(uint8 a) mod1(a, true) mod2(r) returns (bytes7 r) { } - modifier mod1(uint a, bool b) { if (b) _ } - modifier mod2(bytes7 a) { while (a == "1234567") _ } + modifier mod1(uint a, bool b) { if (b) _; } + modifier mod2(bytes7 a) { while (a == "1234567") _; } } )"; BOOST_CHECK(success(text)); @@ -892,7 +894,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables) char const* text = R"( contract B { function f() mod(x) { uint x = 7; } - modifier mod(uint a) { if (a > 0) _ } + modifier mod(uint a) { if (a > 0) _; } } )"; BOOST_CHECK(success(text)); @@ -901,8 +903,8 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables) BOOST_AUTO_TEST_CASE(legal_modifier_override) { char const* text = R"( - contract A { modifier mod(uint a) {} } - contract B is A { modifier mod(uint a) {} } + contract A { modifier mod(uint a) { _; } } + contract B is A { modifier mod(uint a) { _; } } )"; BOOST_CHECK(success(text)); } @@ -910,8 +912,8 @@ BOOST_AUTO_TEST_CASE(legal_modifier_override) BOOST_AUTO_TEST_CASE(illegal_modifier_override) { char const* text = R"( - contract A { modifier mod(uint a) {} } - contract B is A { modifier mod(uint8 a) {} } + contract A { modifier mod(uint a) { _; } } + contract B is A { modifier mod(uint8 a) { _; } } )"; BOOST_CHECK(expectError(text) == Error::Type::TypeError); } @@ -919,8 +921,8 @@ BOOST_AUTO_TEST_CASE(illegal_modifier_override) BOOST_AUTO_TEST_CASE(modifier_overrides_function) { char const* text = R"( - contract A { modifier mod(uint a) {} } - contract B is A { function mod(uint a) {} } + contract A { modifier mod(uint a) { _; } } + contract B is A { function mod(uint a) { } } )"; BOOST_CHECK(expectError(text) == Error::Type::TypeError); } @@ -928,8 +930,8 @@ BOOST_AUTO_TEST_CASE(modifier_overrides_function) BOOST_AUTO_TEST_CASE(function_overrides_modifier) { char const* text = R"( - contract A { function mod(uint a) {} } - contract B is A { modifier mod(uint a) {} } + contract A { function mod(uint a) { } } + contract B is A { modifier mod(uint a) { _; } } )"; BOOST_CHECK(expectError(text) == Error::Type::TypeError); } @@ -938,8 +940,8 @@ BOOST_AUTO_TEST_CASE(modifier_returns_value) { char const* text = R"( contract A { - function f(uint a) mod(2) returns (uint r) {} - modifier mod(uint a) { return 7; } + function f(uint a) mod(2) returns (uint r) { } + modifier mod(uint a) { _; return 7; } } )"; BOOST_CHECK(expectError(text) == Error::Type::TypeError); @@ -1102,6 +1104,37 @@ BOOST_AUTO_TEST_CASE(fallback_function_with_arguments) BOOST_CHECK(expectError(text) == Error::Type::TypeError); } +BOOST_AUTO_TEST_CASE(fallback_function_in_library) +{ + char const* text = R"( + library C { + function() {} + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(fallback_function_with_return_parameters) +{ + char const* text = R"( + contract C { + function() returns (uint) { } + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(fallback_function_with_constant_modifier) +{ + char const* text = R"( + contract C { + uint x; + function() constant { x = 2; } + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + BOOST_AUTO_TEST_CASE(fallback_function_twice) { char const* text = R"( @@ -3823,6 +3856,170 @@ BOOST_AUTO_TEST_CASE(unused_return_value_delegatecall) BOOST_CHECK(expectError(text, true) == Error::Type::Warning); } +BOOST_AUTO_TEST_CASE(modifier_without_underscore) +{ + char const* text = R"( + contract test { + modifier m() {} + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::SyntaxError); +} + +BOOST_AUTO_TEST_CASE(payable_in_library) +{ + char const* text = R"( + library test { + function f() payable {} + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(payable_external) +{ + char const* text = R"( + contract test { + function f() payable external {} + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(payable_internal) +{ + char const* text = R"( + contract test { + function f() payable internal {} + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(payable_private) +{ + char const* text = R"( + contract test { + function f() payable private {} + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(illegal_override_payable) +{ + char const* text = R"( + contract B { function f() payable {} } + contract C is B { function f() {} } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable) +{ + char const* text = R"( + contract B { function f() {} } + contract C is B { function f() payable {} } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(payable_constant_conflict) +{ + char const* text = R"( + contract C { function f() payable constant {} } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(calling_payable) +{ + char const* text = R"( + contract receiver { function pay() payable {} } + contract test { + funciton f() { (new receiver()).pay.value(10)(); } + recevier r = new receiver(); + function g() { r.pay.value(10)(); } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(calling_nonpayable) +{ + char const* text = R"( + contract receiver { function nopay() {} } + contract test { + function f() { (new receiver()).nopay.value(10)(); } + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(non_payable_constructor) +{ + char const* text = R"( + contract C { + function C() { } + } + contract D { + function f() returns (uint) { + (new C).value(2)(); + return 2; + } + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(warn_nonpresent_pragma) +{ + char const* text = "contract C {}"; + auto sourceAndError = parseAnalyseAndReturnError(text, true, false); + BOOST_REQUIRE(!!sourceAndError.second); + BOOST_REQUIRE(!!sourceAndError.first); + BOOST_CHECK(*sourceAndError.second == Error::Type::Warning); +} + +BOOST_AUTO_TEST_CASE(unsatisfied_version) +{ + char const* text = R"( + pragma solidity ^99.99.0; + )"; + BOOST_CHECK(expectError(text, true) == Error::Type::SyntaxError); +} + +BOOST_AUTO_TEST_CASE(constant_constructor) +{ + char const* text = R"( + contract test { + function test() constant {} + } + )"; + BOOST_CHECK(expectError(text, false) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(external_constructor) +{ + char const* text = R"( + contract test { + function test() external {} + } + )"; + BOOST_CHECK(expectError(text, false) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(invalid_array_as_statement) +{ + char const* text = R"( + contract test { + struct S { uint x; } + function test(uint k) { S[k]; } + } + )"; + BOOST_CHECK(expectError(text, false) == Error::Type::TypeError); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 8c0c2098..1f74e928 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -37,7 +37,7 @@ namespace test class DocumentationChecker { public: - DocumentationChecker(): m_compilerStack(false) {} + DocumentationChecker(): m_compilerStack() {} void checkNatspec( std::string const& _code, @@ -46,7 +46,7 @@ public: ) { std::string generatedDocumentationString; - ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse(_code), "Parsing failed"); + ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse("pragma solidity >=0.0;\n" + _code), "Parsing failed"); if (_userDocumentation) generatedDocumentationString = m_compilerStack.metadata("", DocumentationType::NatspecUser); diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index ad573823..562b7859 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -79,6 +79,8 @@ public: bytes nonOptimizedOutput = callContractFunction(_sig, _arguments...); m_contractAddress = m_optimizedContract; bytes optimizedOutput = callContractFunction(_sig, _arguments...); + BOOST_CHECK_MESSAGE(!optimizedOutput.empty(), "No optimized output for " + _sig); + BOOST_CHECK_MESSAGE(!nonOptimizedOutput.empty(), "No un-optimized output for " + _sig); BOOST_CHECK_MESSAGE(nonOptimizedOutput == optimizedOutput, "Computed values do not match." "\nNon-Optimized: " + toHex(nonOptimizedOutput) + "\nOptimized: " + toHex(optimizedOutput)); @@ -176,7 +178,7 @@ BOOST_AUTO_TEST_CASE(identities) } })"; compileBothVersions(sourceCode); - compareVersions("f(uint256)", u256(0x12334664)); + compareVersions("f(int256)", u256(0x12334664)); } BOOST_AUTO_TEST_CASE(unused_expressions) @@ -230,6 +232,7 @@ BOOST_AUTO_TEST_CASE(array_copy) bytes2[] data1; bytes5[] data2; function f(uint x) returns (uint l, uint y) { + data1.length = msg.data.length; for (uint i = 0; i < msg.data.length; ++i) data1[i] = msg.data[i]; data2 = data1; @@ -241,7 +244,7 @@ BOOST_AUTO_TEST_CASE(array_copy) compileBothVersions(sourceCode); compareVersions("f(uint256)", 0); compareVersions("f(uint256)", 10); - compareVersions("f(uint256)", 36); + compareVersions("f(uint256)", 35); } BOOST_AUTO_TEST_CASE(function_calls) @@ -279,6 +282,8 @@ BOOST_AUTO_TEST_CASE(storage_write_in_loops) compareVersions("f(uint256)", 36); } +// Test disabled with https://github.com/ethereum/solidity/pull/762 +// Information in joining branches is not retained anymore. BOOST_AUTO_TEST_CASE(retain_information_in_branches) { // This tests that the optimizer knows that we already have "z == sha3(y)" inside both branches. @@ -312,7 +317,8 @@ BOOST_AUTO_TEST_CASE(retain_information_in_branches) if (_instr == Instruction::SHA3) numSHA3s++; }); - BOOST_CHECK_EQUAL(1, numSHA3s); +// TEST DISABLED - OPTIMIZER IS NOT EFFECTIVE ON THIS ONE ANYMORE +// BOOST_CHECK_EQUAL(1, numSHA3s); } BOOST_AUTO_TEST_CASE(store_tags_as_unions) @@ -346,7 +352,7 @@ BOOST_AUTO_TEST_CASE(store_tags_as_unions) } )"; compileBothVersions(sourceCode); - compareVersions("f()", 7, "abc"); + compareVersions("f(uint256,bytes32)", 7, "abc"); m_optimize = true; bytes optimizedBytecode = compileAndRun(sourceCode, 0, "test"); @@ -799,7 +805,7 @@ BOOST_AUTO_TEST_CASE(cse_empty_sha3) Instruction::SHA3 }; checkCSE(input, { - u256(sha3(bytesConstRef())) + u256(dev::keccak256(bytesConstRef())) }); } @@ -817,7 +823,7 @@ BOOST_AUTO_TEST_CASE(cse_partial_sha3) u256(0xabcd) << (256 - 16), u256(0), Instruction::MSTORE, - u256(sha3(bytes{0xab, 0xcd})) + u256(dev::keccak256(bytes{0xab, 0xcd})) }); } @@ -1174,6 +1180,64 @@ BOOST_AUTO_TEST_CASE(computing_constants) ) == optimizedBytecode.cend()); } +BOOST_AUTO_TEST_CASE(inconsistency) +{ + // This is a test of a bug in the optimizer. + char const* sourceCode = R"( + contract Inconsistency { + struct Value { + uint badnum; + uint number; + } + + struct Container { + uint[] valueIndices; + Value[] values; + } + + Container[] containers; + uint[] valueIndices; + uint INDEX_ZERO = 0; + uint debug; + + // Called with params: containerIndex=0, valueIndex=0 + function levelIII(uint containerIndex, uint valueIndex) private { + Container container = containers[containerIndex]; + Value value = container.values[valueIndex]; + debug = container.valueIndices[value.number]; + } + function levelII() private { + for (uint i = 0; i < valueIndices.length; i++) { + levelIII(INDEX_ZERO, valueIndices[i]); + } + } + + function trigger() public returns (uint) { + containers.length++; + Container container = containers[0]; + + container.values.push(Value({ + badnum: 9000, + number: 0 + })); + + container.valueIndices.length++; + valueIndices.length++; + + levelII(); + return debug; + } + + function DoNotCallButDoNotDelete() public { + levelII(); + levelIII(1, 2); + } + } + )"; + compileBothVersions(sourceCode); + compareVersions("trigger()"); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 909d18c9..a81a9828 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -540,7 +540,7 @@ BOOST_AUTO_TEST_CASE(if_statement) { char const* text = "contract test {\n" " function fun(uint256 a) {\n" - " if (a >= 8) return 2; else { var b = 7; }\n" + " if (a >= 8) { return 2; } else { var b = 7; }\n" " }\n" "}\n"; BOOST_CHECK(successParse(text)); @@ -681,15 +681,23 @@ BOOST_AUTO_TEST_CASE(placeholder_in_function_context) BOOST_AUTO_TEST_CASE(modifier) { char const* text = "contract c {\n" - " modifier mod { if (msg.sender == 0) _ }\n" + " modifier mod { if (msg.sender == 0) _; }\n" "}\n"; BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(modifier_without_semicolon) +{ + char const* text = "contract c {\n" + " modifier mod { if (msg.sender == 0) _ }\n" + "}\n"; + BOOST_CHECK(!successParse(text)); +} + BOOST_AUTO_TEST_CASE(modifier_arguments) { char const* text = "contract c {\n" - " modifier mod(uint a) { if (msg.sender == a) _ }\n" + " modifier mod(uint a) { if (msg.sender == a) _; }\n" "}\n"; BOOST_CHECK(successParse(text)); } @@ -697,8 +705,8 @@ BOOST_AUTO_TEST_CASE(modifier_arguments) BOOST_AUTO_TEST_CASE(modifier_invocation) { char const* text = "contract c {\n" - " modifier mod1(uint a) { if (msg.sender == a) _ }\n" - " modifier mod2 { if (msg.sender == 2) _ }\n" + " modifier mod1(uint a) { if (msg.sender == a) _; }\n" + " modifier mod2 { if (msg.sender == 2) _; }\n" " function f() mod1(7) mod2 { }\n" "}\n"; BOOST_CHECK(successParse(text)); @@ -1223,6 +1231,16 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_conversion_leading_zeroes_check) BOOST_CHECK(!successParse(text)); } +BOOST_AUTO_TEST_CASE(payable_accessor) +{ + char const* text = R"( + contract test { + uint payable x; + } + )"; + BOOST_CHECK(!successParse(text)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index 10f30b8e..31b75f25 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -275,12 +275,6 @@ BOOST_AUTO_TEST_CASE(time_subdenominations) BOOST_CHECK_EQUAL(scanner.next(), Token::SubYear); } -BOOST_AUTO_TEST_CASE(time_after) -{ - Scanner scanner(CharStream("after 1")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::After); -} - BOOST_AUTO_TEST_CASE(empty_comment) { Scanner scanner(CharStream("//\ncontract{}")); @@ -291,6 +285,82 @@ BOOST_AUTO_TEST_CASE(empty_comment) } +BOOST_AUTO_TEST_CASE(valid_unicode_string_escape) +{ + Scanner scanner(CharStream("{ \"\\u00DAnicode\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\xC3\x9Anicode", 8)); +} + +BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_7f) +{ + Scanner scanner(CharStream("{ \"\\u007Fnicode\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\x7Fnicode", 7)); +} + +BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_7ff) +{ + Scanner scanner(CharStream("{ \"\\u07FFnicode\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\xDF\xBFnicode", 8)); +} + +BOOST_AUTO_TEST_CASE(valid_unicode_string_escape_ffff) +{ + Scanner scanner(CharStream("{ \"\\uFFFFnicode\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\xEF\xBF\xBFnicode", 9)); +} + +BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) +{ + Scanner scanner(CharStream("{ \"\\uFFnicode\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); +} + +BOOST_AUTO_TEST_CASE(valid_hex_literal) +{ + Scanner scanner(CharStream("{ hex\"00112233FF\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\x00\x11\x22\x33\xFF", 5)); +} + +BOOST_AUTO_TEST_CASE(invalid_short_hex_literal) +{ + Scanner scanner(CharStream("{ hex\"00112233F\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); +} + +BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_space) +{ + Scanner scanner(CharStream("{ hex\"00112233FF \"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); +} + +BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_wrong_quotes) +{ + Scanner scanner(CharStream("{ hex\"00112233FF'")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); +} + +BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) +{ + Scanner scanner(CharStream("{ hex\"hello\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); +} + + BOOST_AUTO_TEST_SUITE_END() } |