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 +{ + +}; + +} +}