From dab09c595a46a1ab72e9b56578cd203095a61e92 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 31 Oct 2014 17:47:43 +0100 Subject: [PATCH] Some tests and bugfixes for the compiler. --- solidityEndToEndTest.cpp | 210 +++++++++++++++++++++++++++++++++ solidityExpressionCompiler.cpp | 3 +- 2 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 solidityEndToEndTest.cpp diff --git a/solidityEndToEndTest.cpp b/solidityEndToEndTest.cpp new file mode 100644 index 000000000..7bb01fa12 --- /dev/null +++ b/solidityEndToEndTest.cpp @@ -0,0 +1,210 @@ + +/* + 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 . +*/ +/** + * @author Christian + * @date 2014 + * Unit tests for the solidity expression compiler, testing the behaviour of the code. + */ + +#include +#include +#include +#include +#include + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +class ExecutionFramework +{ +public: + ExecutionFramework() { g_logVerbosity = 0; } + + bytes compileAndRun(std::string const& _sourceCode) + { + bytes code = dev::solidity::CompilerStack::compile(_sourceCode); + eth::Executive ex(m_state); + BOOST_REQUIRE(!ex.create(Address(), 0, m_gasPrice, m_gas, &code, Address())); + BOOST_REQUIRE(ex.go()); + ex.finalize(); + m_contractAddress = ex.newAddress(); + return ex.out().toBytes(); + } + + bytes callFunction(byte _index, bytes const& _data) + { + bytes data = bytes(1, _index) + _data; + eth::Executive ex(m_state); + BOOST_REQUIRE(!ex.call(m_contractAddress, Address(), 0, m_gasPrice, &data, m_gas, Address())); + BOOST_REQUIRE(ex.go()); + ex.finalize(); + return ex.out().toBytes(); + } + + bytes callFunction(byte _index, u256 const& _argument) + { + return callFunction(_index, toBigEndian(_argument)); + } + +private: + Address m_contractAddress; + eth::State m_state; + u256 const m_gasPrice = 100 * eth::szabo; + u256 const m_gas = 1000000; +}; + +BOOST_AUTO_TEST_SUITE(SolidityCompilerEndToEndTest) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + char const* sourceCode = "contract test {\n" + " function f(uint a) returns(uint d) { return a * 7; }\n" + "}\n"; + ExecutionFramework framework; + framework.compileAndRun(sourceCode); + u256 a = 0x200030004; + bytes result = framework.callFunction(0, a); + BOOST_CHECK(result == toBigEndian(a * 7)); +} + +BOOST_AUTO_TEST_CASE(empty_contract) +{ + char const* sourceCode = "contract test {\n" + "}\n"; + ExecutionFramework framework; + framework.compileAndRun(sourceCode); + BOOST_CHECK(framework.callFunction(0, bytes()).empty()); +} + +BOOST_AUTO_TEST_CASE(recursive_calls) +{ + char const* sourceCode = "contract test {\n" + " function f(uint n) returns(uint nfac) {\n" + " if (n <= 1) return 1;\n" + " else return n * f(n - 1);\n" + " }\n" + "}\n"; + ExecutionFramework framework; + framework.compileAndRun(sourceCode); + BOOST_CHECK(framework.callFunction(0, u256(0)) == toBigEndian(u256(1))); + BOOST_CHECK(framework.callFunction(0, u256(1)) == toBigEndian(u256(1))); + BOOST_CHECK(framework.callFunction(0, u256(2)) == toBigEndian(u256(2))); + BOOST_CHECK(framework.callFunction(0, u256(3)) == toBigEndian(u256(6))); + BOOST_CHECK(framework.callFunction(0, u256(4)) == toBigEndian(u256(24))); +} + +BOOST_AUTO_TEST_CASE(while_loop) +{ + char const* sourceCode = "contract test {\n" + " function f(uint n) returns(uint nfac) {\n" + " nfac = 1;\n" + " var i = 2;\n" + " while (i <= n) nfac *= i++;\n" + " }\n" + "}\n"; + ExecutionFramework framework; + framework.compileAndRun(sourceCode); + BOOST_CHECK(framework.callFunction(0, u256(0)) == toBigEndian(u256(1))); + BOOST_CHECK(framework.callFunction(0, u256(1)) == toBigEndian(u256(1))); + BOOST_CHECK(framework.callFunction(0, u256(2)) == toBigEndian(u256(2))); + BOOST_CHECK(framework.callFunction(0, u256(3)) == toBigEndian(u256(6))); + BOOST_CHECK(framework.callFunction(0, u256(4)) == toBigEndian(u256(24))); +} + +BOOST_AUTO_TEST_CASE(calling_other_functions) +{ + // note that the index of a function is its index in the sorted sequence of functions + char const* sourceCode = "contract collatz {\n" + " function run(uint x) returns(uint y) {\n" + " while ((y = x) > 1) {\n" + " if (x % 2 == 0) x = evenStep(x);\n" + " else x = oddStep(x);\n" + " }\n" + " }\n" + " function evenStep(uint x) returns(uint y) {\n" + " return x / 2;\n" + " }\n" + " function oddStep(uint x) returns(uint y) {\n" + " return 3 * x + 1;\n" + " }\n" + "}\n"; + ExecutionFramework framework; + framework.compileAndRun(sourceCode); + BOOST_CHECK(framework.callFunction(2, u256(0)) == toBigEndian(u256(0))); + BOOST_CHECK(framework.callFunction(2, u256(1)) == toBigEndian(u256(1))); + BOOST_CHECK(framework.callFunction(2, u256(2)) == toBigEndian(u256(1))); + BOOST_CHECK(framework.callFunction(2, u256(8)) == toBigEndian(u256(1))); + BOOST_CHECK(framework.callFunction(2, u256(127)) == toBigEndian(u256(1))); +} + +BOOST_AUTO_TEST_CASE(many_local_variables) +{ + char const* sourceCode = "contract test {\n" + " function run(uint x1, uint x2, uint x3) returns(uint y) {\n" + " var a = 0x1; var b = 0x10; var c = 0x100;\n" + " y = a + b + c + x1 + x2 + x3;\n" + " y += b + x2;\n" + " }\n" + "}\n"; + ExecutionFramework framework; + framework.compileAndRun(sourceCode); + BOOST_CHECK(framework.callFunction(0, toBigEndian(u256(0x1000)) + toBigEndian(u256(0x10000)) + toBigEndian(u256(0x100000))) + == toBigEndian(u256(0x121121))); +} + +BOOST_AUTO_TEST_CASE(multiple_return_values) +{ + char const* sourceCode = "contract test {\n" + " function run(bool x1, uint x2) returns(uint y1, bool y2, uint y3) {\n" + " y1 = x2; y2 = x1;\n" + " }\n" + "}\n"; + ExecutionFramework framework; + framework.compileAndRun(sourceCode); + BOOST_CHECK(framework.callFunction(0, bytes(1, 1) + toBigEndian(u256(0xcd))) + == toBigEndian(u256(0xcd)) + bytes(1, 1) + toBigEndian(u256(0))); +} + +BOOST_AUTO_TEST_CASE(short_circuiting) +{ + char const* sourceCode = "contract test {\n" + " function run(uint x) returns(uint y) {\n" + " x == 0 || ((x = 8) > 0);\n" + " return x;" + " }\n" + "}\n"; + ExecutionFramework framework; + framework.compileAndRun(sourceCode); + BOOST_CHECK(framework.callFunction(0, u256(0)) == toBigEndian(u256(0))); + BOOST_CHECK(framework.callFunction(0, u256(1)) == toBigEndian(u256(8))); +} + +//@todo test smaller types + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces + diff --git a/solidityExpressionCompiler.cpp b/solidityExpressionCompiler.cpp index 83a7b2bbf..043a69496 100644 --- a/solidityExpressionCompiler.cpp +++ b/solidityExpressionCompiler.cpp @@ -177,8 +177,9 @@ BOOST_AUTO_TEST_CASE(short_circuiting) byte(eth::Instruction::GT), byte(eth::Instruction::ISZERO), // after this we have 10 + 8 >= 4 byte(eth::Instruction::DUP1), - byte(eth::Instruction::PUSH1), 0x14, + byte(eth::Instruction::PUSH1), 0x15, byte(eth::Instruction::JUMPI), // short-circuit if it is true + byte(eth::Instruction::POP), byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0x9, byte(eth::Instruction::EQ),