6 changed files with 227 additions and 22 deletions
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Unit tests for the solidity expression compiler, testing the behaviour of the code. |
||||
|
*/ |
||||
|
|
||||
|
#include <string> |
||||
|
#include <boost/test/unit_test.hpp> |
||||
|
#include <libethereum/State.h> |
||||
|
#include <libethereum/Executive.h> |
||||
|
#include <libsolidity/CompilerStack.h> |
||||
|
|
||||
|
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
|
||||
|
|
Loading…
Reference in new issue