diff --git a/CMakeLists.txt b/CMakeLists.txt
index eb424e745..ae0274f6a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -229,9 +229,6 @@ if (NOT JUSTTESTS)
endif()
-enable_testing()
-add_test(NAME alltests WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test COMMAND testeth)
-
#unset(TARGET_PLATFORM CACHE)
if (WIN32)
diff --git a/mix/qml/html/cm/codemirror.css b/mix/qml/html/cm/codemirror.css
index bdb3d5347..625baa942 100644
--- a/mix/qml/html/cm/codemirror.css
+++ b/mix/qml/html/cm/codemirror.css
@@ -304,12 +304,12 @@ div.CodeMirror-cursors {
@media print {
/* Hide the cursor when printing */
.CodeMirror div.CodeMirror-cursors {
- visibility: hidden;
+ visibility: hidden;
}
}
/* See issue #2901 */
.cm-tab-wrap-hack:after { content: ''; }
-/* Help users use markselection to safely style text background */
-span.CodeMirror-selectedtext { background: none; }
+/* Help users use markselection to safely style text background
+span.CodeMirror-selectedtext { color: #586e75; } */
diff --git a/mix/qml/html/cm/mark-selection.js b/mix/qml/html/cm/mark-selection.js
new file mode 100644
index 000000000..22e46ef32
--- /dev/null
+++ b/mix/qml/html/cm/mark-selection.js
@@ -0,0 +1,118 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// Because sometimes you need to mark the selected *text*.
+//
+// Adds an option 'styleSelectedText' which, when enabled, gives
+// selected text the CSS class given as option value, or
+// "CodeMirror-selectedtext" when the value is not a string.
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) {
+ var prev = old && old != CodeMirror.Init;
+ if (val && !prev) {
+ cm.state.markedSelection = [];
+ cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext";
+ reset(cm);
+ cm.on("cursorActivity", onCursorActivity);
+ cm.on("change", onChange);
+ } else if (!val && prev) {
+ cm.off("cursorActivity", onCursorActivity);
+ cm.off("change", onChange);
+ clear(cm);
+ cm.state.markedSelection = cm.state.markedSelectionStyle = null;
+ }
+ });
+
+ function onCursorActivity(cm) {
+ cm.operation(function() { update(cm); });
+ }
+
+ function onChange(cm) {
+ if (cm.state.markedSelection.length)
+ cm.operation(function() { clear(cm); });
+ }
+
+ var CHUNK_SIZE = 8;
+ var Pos = CodeMirror.Pos;
+ var cmp = CodeMirror.cmpPos;
+
+ function coverRange(cm, from, to, addAt) {
+ if (cmp(from, to) == 0) return;
+ var array = cm.state.markedSelection;
+ var cls = cm.state.markedSelectionStyle;
+ for (var line = from.line;;) {
+ var start = line == from.line ? from : Pos(line, 0);
+ var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line;
+ var end = atEnd ? to : Pos(endLine, 0);
+ var mark = cm.markText(start, end, {className: cls});
+ if (addAt == null) array.push(mark);
+ else array.splice(addAt++, 0, mark);
+ if (atEnd) break;
+ line = endLine;
+ }
+ }
+
+ function clear(cm) {
+ var array = cm.state.markedSelection;
+ for (var i = 0; i < array.length; ++i) array[i].clear();
+ array.length = 0;
+ }
+
+ function reset(cm) {
+ clear(cm);
+ var ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++)
+ coverRange(cm, ranges[i].from(), ranges[i].to());
+ }
+
+ function update(cm) {
+ if (!cm.somethingSelected()) return clear(cm);
+ if (cm.listSelections().length > 1) return reset(cm);
+
+ var from = cm.getCursor("start"), to = cm.getCursor("end");
+
+ var array = cm.state.markedSelection;
+ if (!array.length) return coverRange(cm, from, to);
+
+ var coverStart = array[0].find(), coverEnd = array[array.length - 1].find();
+ if (!coverStart || !coverEnd || to.line - from.line < CHUNK_SIZE ||
+ cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0)
+ return reset(cm);
+
+ while (cmp(from, coverStart.from) > 0) {
+ array.shift().clear();
+ coverStart = array[0].find();
+ }
+ if (cmp(from, coverStart.from) < 0) {
+ if (coverStart.to.line - from.line < CHUNK_SIZE) {
+ array.shift().clear();
+ coverRange(cm, from, coverStart.to, 0);
+ } else {
+ coverRange(cm, from, coverStart.from, 0);
+ }
+ }
+
+ while (cmp(to, coverEnd.to) < 0) {
+ array.pop().clear();
+ coverEnd = array[array.length - 1].find();
+ }
+ if (cmp(to, coverEnd.to) > 0) {
+ if (to.line - coverEnd.from.line < CHUNK_SIZE) {
+ array.pop().clear();
+ coverRange(cm, coverEnd.from, to);
+ } else {
+ coverRange(cm, coverEnd.to, to);
+ }
+ }
+ }
+});
diff --git a/mix/qml/html/cm/solarized.css b/mix/qml/html/cm/solarized.css
index 05836b1bf..d8c31bfb5 100644
--- a/mix/qml/html/cm/solarized.css
+++ b/mix/qml/html/cm/solarized.css
@@ -166,7 +166,7 @@ view-port
/* Code execution */
.CodeMirror-exechighlight {
- background: rgba(255, 255, 255, 0.10);
+ background: #eee8d5;
}
/* Error annotation */
@@ -182,3 +182,4 @@ view-port
padding: 2px;
}
+span.CodeMirror-selectedtext { color: #586e75 !important; }
diff --git a/mix/qml/html/codeeditor.html b/mix/qml/html/codeeditor.html
index eec13f109..c9d4ff96a 100644
--- a/mix/qml/html/codeeditor.html
+++ b/mix/qml/html/codeeditor.html
@@ -22,6 +22,7 @@
+
diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js
index da87e63e0..299691ec0 100644
--- a/mix/qml/html/codeeditor.js
+++ b/mix/qml/html/codeeditor.js
@@ -4,7 +4,8 @@ var editor = CodeMirror(document.body, {
matchBrackets: true,
autofocus: true,
gutters: ["CodeMirror-linenumbers", "breakpoints"],
- autoCloseBrackets: true
+ autoCloseBrackets: true,
+ styleSelectedText: true
});
var ternServer;
diff --git a/mix/web.qrc b/mix/web.qrc
index aa5b38a67..63a3a668f 100644
--- a/mix/web.qrc
+++ b/mix/web.qrc
@@ -39,5 +39,6 @@
qml/html/cm/acorn.js
qml/html/cm/acorn_loose.js
qml/html/cm/walk.js
+ qml/html/cm/mark-selection.js
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index ef292c2bb..01681dbef 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -16,6 +16,21 @@ include_directories(${Boost_INCLUDE_DIRS})
include_directories(${CRYPTOPP_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
+# search for test names and create ctest tests
+enable_testing()
+foreach(file ${SRC_LIST})
+ file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/${file} test_list_raw REGEX "BOOST_.*TEST_(SUITE|CASE)")
+ set(TestSuite "DEFAULT")
+ foreach(test_raw ${test_list_raw})
+ string(REGEX REPLACE ".*TEST_(SUITE|CASE)\\(([^ ,\\)]*).*" "\\1 \\2" test ${test_raw})
+ if(test MATCHES "^SUITE .*")
+ string(SUBSTRING ${test} 6 -1 TestSuite)
+ elseif(test MATCHES "^CASE .*")
+ string(SUBSTRING ${test} 5 -1 TestCase)
+ add_test(NAME ${TestSuite}/${TestCase} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test COMMAND testeth -t ${TestSuite}/${TestCase})
+ endif(test MATCHES "^SUITE .*")
+ endforeach(test_raw)
+endforeach(file)
file(GLOB HEADERS "*.h")
add_executable(testeth ${SRC_LIST} ${HEADERS})
@@ -30,6 +45,7 @@ target_link_libraries(testeth ethereum)
target_link_libraries(testeth ethcore)
target_link_libraries(testeth secp256k1)
target_link_libraries(testeth solidity)
+target_link_libraries(testeth testutils)
if (NOT HEADLESS AND NOT JUSTTESTS)
target_link_libraries(testeth webthree)
target_link_libraries(testeth natspec)
@@ -42,13 +58,36 @@ endif()
target_link_libraries(createRandomVMTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(createRandomVMTest ethereum)
target_link_libraries(createRandomVMTest ethcore)
+target_link_libraries(createRandomVMTest testutils)
target_link_libraries(createRandomStateTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(createRandomStateTest ethereum)
target_link_libraries(createRandomStateTest ethcore)
+target_link_libraries(createRandomStateTest testutils)
target_link_libraries(checkRandomVMTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(checkRandomVMTest ethereum)
target_link_libraries(checkRandomVMTest ethcore)
+target_link_libraries(checkRandomVMTest testutils)
target_link_libraries(checkRandomStateTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(checkRandomStateTest ethereum)
target_link_libraries(checkRandomStateTest ethcore)
+target_link_libraries(checkRandomStateTest testutils)
+
+enable_testing()
+set(CTEST_OUTPUT_ON_FAILURE TRUE)
+
+include(EthUtils)
+
+eth_add_test(ClientBase
+ ARGS --eth_testfile=BlockTests/bcJS_API_Test --eth_threads=1
+ ARGS --eth_testfile=BlockTests/bcJS_API_Test --eth_threads=3
+ ARGS --eth_testfile=BlockTests/bcJS_API_Test --eth_threads=10
+ ARGS --eth_testfile=BlockTests/bcValidBlockTest --eth_threads=1
+ ARGS --eth_testfile=BlockTests/bcValidBlockTest --eth_threads=3
+ ARGS --eth_testfile=BlockTests/bcValidBlockTest --eth_threads=10
+)
+
+eth_add_test(JsonRpc
+ ARGS --eth_testfile=BlockTests/bcJS_API_Test
+ ARGS --eth_testfile=BlockTests/bcValidBlockTest
+)
diff --git a/test/ClientBase.cpp b/test/ClientBase.cpp
new file mode 100644
index 000000000..2197ac830
--- /dev/null
+++ b/test/ClientBase.cpp
@@ -0,0 +1,202 @@
+/*
+ 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 .
+ */
+/** @file ClientBase.cpp
+ * @author Marek Kotewicz
+ * @date 2015
+ */
+
+#include
+#include
+#include "TestUtils.h"
+
+using namespace std;
+using namespace dev;
+using namespace dev::eth;
+using namespace dev::test;
+
+BOOST_FIXTURE_TEST_SUITE(ClientBase, ParallelClientBaseFixture)
+
+BOOST_AUTO_TEST_CASE(blocks)
+{
+ enumerateClients([](Json::Value const& _json, dev::eth::ClientBase& _client) -> void
+ {
+ for (string const& name: _json["postState"].getMemberNames())
+ {
+ Json::Value o = _json["postState"][name];
+ Address address(name);
+
+ // balanceAt
+ u256 expectedBalance = u256(o["balance"].asString());
+ u256 balance = _client.balanceAt(address);
+ ETH_CHECK_EQUAL(expectedBalance, balance);
+
+ // countAt
+ u256 expectedCount = u256(o["nonce"].asString());
+ u256 count = _client.countAt(address);
+ ETH_CHECK_EQUAL(expectedCount, count);
+
+ // stateAt
+ for (string const& pos: o["storage"].getMemberNames())
+ {
+ u256 expectedState = u256(o["storage"][pos].asString());
+ u256 state = _client.stateAt(address, u256(pos));
+ ETH_CHECK_EQUAL(expectedState, state);
+ }
+
+ // codeAt
+ bytes expectedCode = fromHex(o["code"].asString());
+ bytes code = _client.codeAt(address);
+ ETH_CHECK_EQUAL_COLLECTIONS(expectedCode.begin(), expectedCode.end(),
+ code.begin(), code.end());
+ }
+
+ // number
+ unsigned expectedNumber = _json["blocks"].size();
+ unsigned number = _client.number();
+ ETH_CHECK_EQUAL(expectedNumber, number);
+
+ u256 totalDifficulty = u256(_json["genesisBlockHeader"]["difficulty"].asString());
+ for (Json::Value const& block: _json["blocks"])
+ {
+ Json::Value blockHeader = block["blockHeader"];
+ Json::Value uncles = block["uncleHeaders"];
+ Json::Value transactions = block["transactions"];
+ h256 blockHash = h256(fromHex(blockHeader["hash"].asString()));
+
+ // just update the difficulty
+ for (Json::Value const& uncle: uncles)
+ {
+ totalDifficulty += u256(uncle["difficulty"].asString());
+ }
+
+ // hashFromNumber
+ h256 expectedHashFromNumber = h256(fromHex(blockHeader["hash"].asString()));
+ h256 hashFromNumber = _client.hashFromNumber(jsToInt(blockHeader["number"].asString()));
+ ETH_CHECK_EQUAL(expectedHashFromNumber, hashFromNumber);
+
+ // blockInfo
+ auto compareBlockInfos = [](Json::Value const& _b, BlockInfo _blockInfo) -> void
+ {
+ LogBloom expectedBlockInfoBloom = LogBloom(fromHex(_b["bloom"].asString()));
+ Address expectedBlockInfoCoinbase = Address(fromHex(_b["coinbase"].asString()));
+ u256 expectedBlockInfoDifficulty = u256(_b["difficulty"].asString());
+ bytes expectedBlockInfoExtraData = fromHex(_b["extraData"].asString());
+ u256 expectedBlockInfoGasLimit = u256(_b["gasLimit"].asString());
+ u256 expectedBlockInfoGasUsed = u256(_b["gasUsed"].asString());
+ h256 expectedBlockInfoHash = h256(fromHex(_b["hash"].asString()));
+ h256 expectedBlockInfoMixHash = h256(fromHex(_b["mixHash"].asString()));
+ Nonce expectedBlockInfoNonce = Nonce(fromHex(_b["nonce"].asString()));
+ u256 expectedBlockInfoNumber = u256(_b["number"].asString());
+ h256 expectedBlockInfoParentHash = h256(fromHex(_b["parentHash"].asString()));
+ h256 expectedBlockInfoReceiptsRoot = h256(fromHex(_b["receiptTrie"].asString()));
+ u256 expectedBlockInfoTimestamp = u256(_b["timestamp"].asString());
+ h256 expectedBlockInfoTransactionsRoot = h256(fromHex(_b["transactionsTrie"].asString()));
+ h256 expectedBlockInfoUncldeHash = h256(fromHex(_b["uncleHash"].asString()));
+ ETH_CHECK_EQUAL(expectedBlockInfoBloom, _blockInfo.logBloom);
+ ETH_CHECK_EQUAL(expectedBlockInfoCoinbase, _blockInfo.coinbaseAddress);
+ ETH_CHECK_EQUAL(expectedBlockInfoDifficulty, _blockInfo.difficulty);
+ ETH_CHECK_EQUAL_COLLECTIONS(expectedBlockInfoExtraData.begin(), expectedBlockInfoExtraData.end(),
+ _blockInfo.extraData.begin(), _blockInfo.extraData.end());
+ ETH_CHECK_EQUAL(expectedBlockInfoGasLimit, _blockInfo.gasLimit);
+ ETH_CHECK_EQUAL(expectedBlockInfoGasUsed, _blockInfo.gasUsed);
+ ETH_CHECK_EQUAL(expectedBlockInfoHash, _blockInfo.hash);
+ ETH_CHECK_EQUAL(expectedBlockInfoMixHash, _blockInfo.mixHash);
+ ETH_CHECK_EQUAL(expectedBlockInfoNonce, _blockInfo.nonce);
+ ETH_CHECK_EQUAL(expectedBlockInfoNumber, _blockInfo.number);
+ ETH_CHECK_EQUAL(expectedBlockInfoParentHash, _blockInfo.parentHash);
+ ETH_CHECK_EQUAL(expectedBlockInfoReceiptsRoot, _blockInfo.receiptsRoot);
+ ETH_CHECK_EQUAL(expectedBlockInfoTimestamp, _blockInfo.timestamp);
+ ETH_CHECK_EQUAL(expectedBlockInfoTransactionsRoot, _blockInfo.transactionsRoot);
+ ETH_CHECK_EQUAL(expectedBlockInfoUncldeHash, _blockInfo.sha3Uncles);
+ };
+
+ BlockInfo blockInfo = _client.blockInfo(blockHash);
+ compareBlockInfos(blockHeader, blockInfo);
+
+ // blockDetails
+ unsigned expectedBlockDetailsNumber = jsToInt(blockHeader["number"].asString());
+ totalDifficulty += u256(blockHeader["difficulty"].asString());
+ BlockDetails blockDetails = _client.blockDetails(blockHash);
+ ETH_CHECK_EQUAL(expectedBlockDetailsNumber, blockDetails.number);
+ ETH_CHECK_EQUAL(totalDifficulty, blockDetails.totalDifficulty);
+
+ auto compareTransactions = [](Json::Value const& _t, Transaction _transaction) -> void
+ {
+ bytes expectedTransactionData = fromHex(_t["data"].asString());
+ u256 expectedTransactionGasLimit = u256(_t["gasLimit"].asString());
+ u256 expectedTransactionGasPrice = u256(_t["gasPrice"].asString());
+ u256 expectedTransactionNonce = u256(_t["nonce"].asString());
+ u256 expectedTransactionSignatureR = h256(fromHex(_t["r"].asString()));
+ u256 expectedTransactionSignatureS = h256(fromHex(_t["s"].asString()));
+// unsigned expectedTransactionSignatureV = jsToInt(t["v"].asString());
+
+ ETH_CHECK_EQUAL_COLLECTIONS(expectedTransactionData.begin(), expectedTransactionData.end(),
+ _transaction.data().begin(), _transaction.data().end());
+ ETH_CHECK_EQUAL(expectedTransactionGasLimit, _transaction.gas());
+ ETH_CHECK_EQUAL(expectedTransactionGasPrice, _transaction.gasPrice());
+ ETH_CHECK_EQUAL(expectedTransactionNonce, _transaction.nonce());
+ ETH_CHECK_EQUAL(expectedTransactionSignatureR, _transaction.signature().r);
+ ETH_CHECK_EQUAL(expectedTransactionSignatureS, _transaction.signature().s);
+// ETH_CHECK_EQUAL(expectedTransactionSignatureV, _transaction.signature().v); // 27 === 0x0, 28 === 0x1, not sure why
+ };
+
+ Transactions ts = _client.transactions(blockHash);
+ TransactionHashes tHashes = _client.transactionHashes(blockHash);
+ unsigned tsCount = _client.transactionCount(blockHash);
+
+ ETH_REQUIRE(transactions.size() == ts.size());
+ ETH_REQUIRE(transactions.size() == tHashes.size());
+
+ // transactionCount
+ ETH_CHECK_EQUAL(transactions.size(), tsCount);
+
+ for (unsigned i = 0; i < tsCount; i++)
+ {
+ Json::Value t = transactions[i];
+
+ // transaction (by block hash and transaction index)
+ Transaction transaction = _client.transaction(blockHash, i);
+ compareTransactions(t, transaction);
+
+ // transaction (by hash)
+ Transaction transactionByHash = _client.transaction(transaction.sha3());
+ compareTransactions(t, transactionByHash);
+
+ // transactions
+ compareTransactions(t, ts[i]);
+
+ // transactionHashes
+ ETH_CHECK_EQUAL(transaction.sha3(), tHashes[i]);
+ }
+
+ // uncleCount
+ unsigned usCount = _client.uncleCount(blockHash);
+ ETH_CHECK_EQUAL(uncles.size(), usCount);
+
+ for (unsigned i = 0; i < usCount; i++)
+ {
+ Json::Value u = uncles[i];
+
+ // uncle (by hash)
+ BlockInfo uncle = _client.uncle(blockHash, i);
+ compareBlockInfos(u, uncle);
+ }
+ }
+ });
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp
index b29c5dc3a..7dc001498 100644
--- a/test/TestHelper.cpp
+++ b/test/TestHelper.cpp
@@ -374,22 +374,6 @@ void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _e
}
}
-std::string getTestPath()
-{
- string testPath;
- const char* ptestPath = getenv("ETHEREUM_TEST_PATH");
-
- if (ptestPath == NULL)
- {
- cnote << " could not find environment variable ETHEREUM_TEST_PATH \n";
- testPath = "../../../tests";
- }
- else
- testPath = ptestPath;
-
- return testPath;
-}
-
void userDefinedTest(string testTypeFlag, std::function doTests)
{
for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)
diff --git a/test/TestHelper.h b/test/TestHelper.h
index ade20f5e4..022c715f8 100644
--- a/test/TestHelper.h
+++ b/test/TestHelper.h
@@ -28,6 +28,7 @@
#include "JsonSpiritHeaders.h"
#include
#include
+#include
namespace dev
{
@@ -138,7 +139,6 @@ void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs);
void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _expectedCallCreates);
void executeTests(const std::string& _name, const std::string& _testPathAppendix, std::function doTests);
-std::string getTestPath();
void userDefinedTest(std::string testTypeFlag, std::function doTests);
RLPStream createRLPStreamFromTransactionFields(json_spirit::mObject& _tObj);
eth::LastHashes lastHashes(u256 _currentBlockNumber);
diff --git a/test/TestUtils.cpp b/test/TestUtils.cpp
new file mode 100644
index 000000000..6222955d5
--- /dev/null
+++ b/test/TestUtils.cpp
@@ -0,0 +1,117 @@
+/*
+ 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 .
+ */
+/** @file TestUtils.cpp
+ * @author Marek Kotewicz
+ * @date 2015
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include "TestUtils.h"
+
+using namespace std;
+using namespace dev;
+using namespace dev::eth;
+using namespace dev::test;
+
+namespace dev
+{
+namespace test
+{
+
+bool getCommandLineOption(std::string const& _name);
+std::string getCommandLineArgument(std::string const& _name, bool _require = false);
+
+}
+}
+
+bool dev::test::getCommandLineOption(string const& _name)
+{
+ auto argc = boost::unit_test::framework::master_test_suite().argc;
+ auto argv = boost::unit_test::framework::master_test_suite().argv;
+ bool result = false;
+ for (auto i = 0; !result && i < argc; ++i)
+ result = _name == argv[i];
+ return result;
+}
+
+std::string dev::test::getCommandLineArgument(string const& _name, bool _require)
+{
+ auto argc = boost::unit_test::framework::master_test_suite().argc;
+ auto argv = boost::unit_test::framework::master_test_suite().argv;
+ for (auto i = 1; i < argc; ++i)
+ {
+ string str = argv[i];
+ if (_name == str.substr(0, _name.size()))
+ return str.substr(str.find("=") + 1);
+ }
+ if (_require)
+ BOOST_ERROR("Failed getting command line argument: " << _name << " from: " << argv);
+ return "";
+}
+
+LoadTestFileFixture::LoadTestFileFixture()
+{
+ m_json = loadJsonFromFile(toTestFilePath(getCommandLineArgument("--eth_testfile")));
+}
+
+void ParallelFixture::enumerateThreads(std::function callback) const
+{
+ size_t threadsCount = std::stoul(getCommandLineArgument("--eth_threads"), nullptr, 10);
+
+ vector workers;
+ for (size_t i = 0; i < threadsCount; i++)
+ workers.emplace_back(callback);
+
+ for_each(workers.begin(), workers.end(), [](thread &t)
+ {
+ t.join();
+ });
+}
+
+void BlockChainFixture::enumerateBlockchains(std::function callback) const
+{
+ for (string const& name: m_json.getMemberNames())
+ {
+ BlockChainLoader bcl(m_json[name]);
+ callback(m_json[name], bcl.bc(), bcl.state());
+ }
+}
+
+void ClientBaseFixture::enumerateClients(std::function callback) const
+{
+ enumerateBlockchains([&callback](Json::Value const& _json, BlockChain const& _bc, State _state) -> void
+ {
+ FixedClient client(_bc, _state);
+ callback(_json, client);
+ });
+}
+
+void ParallelClientBaseFixture::enumerateClients(std::function callback) const
+{
+ ClientBaseFixture::enumerateClients([this, &callback](Json::Value const& _json, dev::eth::ClientBase& _client) -> void
+ {
+ // json is being copied here
+ enumerateThreads([callback, _json, &_client]() -> void
+ {
+ callback(_json, _client);
+ });
+ });
+}
diff --git a/test/TestUtils.h b/test/TestUtils.h
new file mode 100644
index 000000000..f9817c21d
--- /dev/null
+++ b/test/TestUtils.h
@@ -0,0 +1,82 @@
+/*
+ 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 .
+ */
+/** @file TestUtils.h
+ * @author Marek Kotewicz
+ * @date 2015
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+namespace dev
+{
+namespace test
+{
+
+// should be used for multithread tests
+static SharedMutex x_boostTest;
+#define ETH_CHECK_EQUAL(x, y) { dev::WriteGuard(x_boostTest); BOOST_CHECK_EQUAL(x, y); }
+#define ETH_CHECK_EQUAL_COLLECTIONS(xb, xe, yb, ye) { dev::WriteGuard(x_boostTest); BOOST_CHECK_EQUAL_COLLECTIONS(xb, xe, yb, ye); }
+#define ETH_REQUIRE(x) { dev::WriteGuard(x_boostTest); BOOST_REQUIRE(x); }
+
+struct LoadTestFileFixture
+{
+ LoadTestFileFixture();
+
+protected:
+ Json::Value m_json;
+};
+
+struct ParallelFixture
+{
+ void enumerateThreads(std::function callback) const;
+};
+
+struct BlockChainFixture: public LoadTestFileFixture
+{
+ void enumerateBlockchains(std::function callback) const;
+};
+
+struct ClientBaseFixture: public BlockChainFixture
+{
+ void enumerateClients(std::function callback) const;
+};
+
+// important BOOST TEST do have problems with thread safety!!!
+// BOOST_CHECK is not thread safe
+// BOOST_MESSAGE is not thread safe
+// http://boost.2283326.n4.nabble.com/Is-boost-test-thread-safe-td3471644.html
+// http://lists.boost.org/boost-users/2010/03/57691.php
+// worth reading
+// https://codecrafter.wordpress.com/2012/11/01/c-unit-test-framework-adapter-part-3/
+struct ParallelClientBaseFixture: public ClientBaseFixture, public ParallelFixture
+{
+ void enumerateClients(std::function callback) const;
+};
+
+struct JsonRpcFixture: public ClientBaseFixture
+{
+
+};
+
+}
+}