danicoin/tests/IntegrationTests/main.cpp
2016-01-18 15:33:29 +00:00

853 lines
42 KiB
C++

// Copyright (c) 2011-2016 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <gtest/gtest.h>
#include <thread>
#include <chrono>
#include <mutex>
#include <functional>
#include <future>
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>
#include "CryptoNoteCore/CryptoNoteFormatUtils.h"
#include "../IntegrationTestLib/BaseFunctionalTests.h"
#include "../IntegrationTestLib/Logger.h"
#include "Logging/ConsoleLogger.h"
#ifndef CHECK_AND_ASSERT_MES
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message) do{if(!(expr)) {LOG_ERROR(message); return fail_ret_val;};}while(0)
#endif
#ifndef CHECK_AND_ASSERT_MES_NON_FATAL
#define CHECK_AND_ASSERT_MES_NON_FATAL(expr, fail_ret_val, message) do{if(!(expr)) {LOG_WARNING(message); };}while(0)
#endif
Tests::Common::BaseFunctionalTestsConfig baseCfg;
// System::Dispatcher globalDispatcher;
namespace po = boost::program_options;
namespace {
class ConfigurationError : public std::runtime_error {
public:
ConfigurationError(const char* desc) : std::runtime_error(desc) {}
};
struct Configuration : public Tests::Common::BaseFunctionalTestsConfig {
Configuration() : desc("Allowed options") {
init();
}
bool handleCommandLine(int argc, char **argv) {
po::variables_map vm;
po::store(po::command_line_parser(argc, argv).options(desc).allow_unregistered().run(), vm);
po::notify(vm);
BaseFunctionalTestsConfig::handleCommandLine(vm);
if (vm.count("help")) {
std::cout << desc << std::endl;
return false;
}
if (vm.count("test-type")) {
auto testType = vm["test-type"].as<uint16_t>();
if (testType < 1 || testType >= TESTLAST)
throw ConfigurationError("Incorrect test type.");
_testType = (TestType)testType;
} else
throw ConfigurationError("Missing test type.");
return true;
}
enum TestType {
WALLET2WALLET = 1,
BLOCKTHRUDAEMONS = 3,
RELAYBLOCKTHRUDAEMONS = 4,
TESTPOOLANDINPROCNODE = 5,
TESTPOOLDELETION = 6,
TESTMULTIVERSION = 7,
TESTLAST
} _testType;
po::options_description desc;
protected:
void init() {
desc.add_options()
("help,h", "produce this help message and exit")
("test-type,t", po::value<uint16_t>()->default_value(1),
"test type:\r\n"
"1 - wallet to wallet test,\r\n"
"3 - block thru daemons test\r\n"
"4 - relay block thru daemons\r\n"
"5 - test tx pool and inproc node\r\n"
"6 - deleting tx from pool due to timeout\r\n"
"7 - multiple daemons interoperability test (use -a option to specify daemons)\r\n");
BaseFunctionalTestsConfig::init(desc);
}
};
}
class SimpleTest : public Tests::Common::BaseFunctionalTests {
public:
SimpleTest(const CryptoNote::Currency& currency, System::Dispatcher& system, const Tests::Common::BaseFunctionalTestsConfig& config) :
BaseFunctionalTests(currency, system, config) {}
class WaitForActualGrowObserver : public CryptoNote::IWalletLegacyObserver {
Tests::Common::Semaphore& m_GotActual;
uint64_t m_lastFunds;
public:
WaitForActualGrowObserver(Tests::Common::Semaphore& GotActual, uint64_t lastFunds) : m_GotActual(GotActual), m_lastFunds(lastFunds) { }
virtual void actualBalanceUpdated(uint64_t actualBalance) override {
if (m_lastFunds < actualBalance) {
m_GotActual.notify();
}
m_lastFunds = actualBalance;
}
};
class WaitForActualDwindleObserver : public CryptoNote::IWalletLegacyObserver {
Tests::Common::Semaphore& m_GotActual;
uint64_t m_lastFunds;
public:
WaitForActualDwindleObserver(Tests::Common::Semaphore& GotActual, uint64_t lastFunds) : m_GotActual(GotActual), m_lastFunds(lastFunds) { }
virtual void actualBalanceUpdated(uint64_t actualBalance) override {
if (m_lastFunds > actualBalance) {
m_GotActual.notify();
}
m_lastFunds = actualBalance;
}
};
class WaitForPendingGrowObserver : public CryptoNote::IWalletLegacyObserver {
Tests::Common::Semaphore& m_GotActual;
uint64_t m_lastFunds;
public:
WaitForPendingGrowObserver(Tests::Common::Semaphore& GotActual, uint64_t lastFunds) : m_GotActual(GotActual), m_lastFunds(lastFunds) { }
virtual void pendingBalanceUpdated(uint64_t pendingBalance) override {
if (m_lastFunds < pendingBalance) {
m_GotActual.notify();
}
m_lastFunds = pendingBalance;
}
};
class WaitForConfirmationObserver : public CryptoNote::IWalletLegacyObserver {
Tests::Common::Semaphore& m_confirmed;
std::function<bool(uint64_t)> m_pred;
public:
WaitForConfirmationObserver(Tests::Common::Semaphore& confirmed, std::function<bool(uint64_t)> pred) : m_confirmed(confirmed), m_pred(pred) { }
virtual void pendingBalanceUpdated(uint64_t pendingBalance) override {
if (m_pred(pendingBalance)) m_confirmed.notify();
}
};
class WaitForSendCompletedObserver : public CryptoNote::IWalletLegacyObserver {
Tests::Common::Semaphore& m_Sent;
std::error_code& m_error;
CryptoNote::TransactionId& m_transactionId;
public:
WaitForSendCompletedObserver(Tests::Common::Semaphore& Sent, CryptoNote::TransactionId& transactionId, std::error_code& error) : m_Sent(Sent), m_transactionId(transactionId), m_error(error) { }
virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) override {
m_error = result;
m_transactionId = transactionId;
m_Sent.notify();
}
};
class WaitForExternalTransactionObserver : public CryptoNote::IWalletLegacyObserver {
public:
WaitForExternalTransactionObserver() { }
std::promise<CryptoNote::TransactionId> promise;
virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId) override {
promise.set_value(transactionId);
}
};
class WaitForTransactionUpdated : public CryptoNote::IWalletLegacyObserver {
public:
WaitForTransactionUpdated() {}
std::promise<void> promise;
virtual void transactionUpdated(CryptoNote::TransactionId transactionId) override {
if (expectindTxId == transactionId) {
promise.set_value();
}
}
CryptoNote::TransactionId expectindTxId;
};
bool perform1() {
using namespace Tests::Common;
using namespace CryptoNote;
const uint64_t FEE = 1000000;
launchTestnet(2);
LOG_TRACE("STEP 1 PASSED");
std::unique_ptr<CryptoNote::INode> node1;
std::unique_ptr<CryptoNote::INode> node2;
nodeDaemons.front()->makeINode(node1);
nodeDaemons.front()->makeINode(node2);
std::unique_ptr<CryptoNote::IWalletLegacy> wallet1;
std::unique_ptr<CryptoNote::IWalletLegacy> wallet2;
makeWallet(wallet1, node1);
makeWallet(wallet2, node2);
CHECK_AND_ASSERT_MES(mineBlock(), false, "can't mine block");
CHECK_AND_ASSERT_MES(mineBlock(), false, "can't mine block");
LOG_TRACE("STEP 2 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
CHECK_AND_ASSERT_MES(mineBlock(wallet1), false, "can't mine block on wallet 1");
CHECK_AND_ASSERT_MES(mineBlock(), false, "can't mine block");
LOG_TRACE("STEP 3 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
Semaphore wallet1GotActual;
WaitForConfirmationObserver wallet1ActualGrown(wallet1GotActual, [](uint64_t pending)->bool {return pending == 0; });
wallet1->addObserver(&wallet1ActualGrown);
CHECK_AND_ASSERT_MES(startMining(1) , false, "startMining(1) failed");
wallet1GotActual.wait();
LOG_TRACE("STEP 4 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
CHECK_AND_ASSERT_MES(stopMining() , false, "stopMining() failed");
auto wallet1ActualBeforeTransaction = wallet1->actualBalance();
auto wallet2ActualBeforeTransaction = wallet2->actualBalance();
auto wallet2PendingBeforeTransaction = wallet2->pendingBalance();
CryptoNote::WalletLegacyTransfer tr;
tr.address = wallet2->getAddress();
tr.amount = wallet1ActualBeforeTransaction / 2;
TransactionId sendTransaction;
std::error_code result;
Semaphore moneySent;
WaitForSendCompletedObserver sco1(moneySent, sendTransaction, result);
Semaphore w2GotPending;
WaitForPendingGrowObserver pgo1(w2GotPending, wallet2PendingBeforeTransaction);
wallet2->addObserver(&pgo1);
wallet1->addObserver(&sco1);
wallet1->sendTransaction(tr, FEE);
CHECK_AND_ASSERT_MES(startMining(1), false, "startMining(1) failed");
moneySent.wait();
w2GotPending.wait();
CHECK_AND_ASSERT_MES(stopMining(), false, "stopMining() failed");
auto wallet2PendingAfterTransaction = wallet2->pendingBalance();
auto wallet1PendingAfterTransaction = wallet1->pendingBalance();
auto w2PendingDiff = wallet2PendingAfterTransaction - wallet2PendingBeforeTransaction;
auto wallet1ActualAfterTransaction = wallet1->actualBalance();
LOG_TRACE("STEP 5 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
CHECK_AND_ASSERT_MES((tr.amount == w2PendingDiff), false, "STEP 6 ASSERTION 1 FAILED\r\n Transfered amount " + m_currency.formatAmount(tr.amount) + " doesn't match recieved amount " + m_currency.formatAmount(w2PendingDiff));
CHECK_AND_ASSERT_MES((wallet1ActualBeforeTransaction - wallet1PendingAfterTransaction - wallet1ActualAfterTransaction - tr.amount - FEE == 0), false,
"STEP 6 ASSERTION 2 FAILED\r\n wallet1 Actual Before Transaction doesn't match wallet1 total After Transaction + Transfered amount + Fee "
+ m_currency.formatAmount(wallet1ActualBeforeTransaction) + " <> " + m_currency.formatAmount(wallet1PendingAfterTransaction) + " + " + m_currency.formatAmount(wallet1ActualAfterTransaction) + " + " + m_currency.formatAmount(tr.amount) + " + " + m_currency.formatAmount(FEE));
LOG_TRACE("STEP 6 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
CHECK_AND_ASSERT_MES(startMining(1), false, "startMining(1) failed");
Semaphore confirmed2;
Semaphore confirmed1;
WaitForConfirmationObserver confirmationObserver2(confirmed2, [](uint64_t pending)->bool {return pending == 0; });
WaitForConfirmationObserver confirmationObserver1(confirmed1, [](uint64_t pending)->bool {return pending == 0; });
wallet2->addObserver(&confirmationObserver2);
wallet1->addObserver(&confirmationObserver1);
if (wallet2->pendingBalance() != 0) confirmed2.wait();
if (wallet1->pendingBalance() != 0) confirmed1.wait();
CHECK_AND_ASSERT_MES(stopMining(), false, "stopMining() failed");
auto wallet1ActualAfterTransactionAndConfirmation = wallet1->actualBalance();
auto wallet2ActualAfterTransactionAndConfirmation = wallet2->actualBalance();
auto w2ActualDiff = wallet2ActualAfterTransactionAndConfirmation - wallet2ActualBeforeTransaction;
auto w1ActualDiff = wallet1ActualBeforeTransaction - wallet1ActualAfterTransactionAndConfirmation;
CHECK_AND_ASSERT_MES((tr.amount == w2ActualDiff), false, "STEP 7 FAILED\r\n Transfered amount " + m_currency.formatAmount(tr.amount) + " doesn't match confirmed recieved amount " + m_currency.formatAmount(w2ActualDiff));
CHECK_AND_ASSERT_MES((w1ActualDiff - tr.amount - FEE == 0), false,
"STEP 7 FAILED\r\n wallet1 Actual Before Transaction doesn't match wallet1 Actual After Transaction + Transfered amount + Fee "
+ m_currency.formatAmount(wallet1ActualBeforeTransaction) + " <> " + m_currency.formatAmount(wallet1ActualAfterTransactionAndConfirmation) + "+" + m_currency.formatAmount(tr.amount) + "+" + m_currency.formatAmount(FEE));
LOG_TRACE("STEP 7 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
wallet1->removeObserver(&wallet1ActualGrown);
wallet2->removeObserver(&pgo1);
wallet1->removeObserver(&sco1);
wallet2->removeObserver(&confirmationObserver2);
wallet1->removeObserver(&confirmationObserver1);
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
return true;
}
class WaitForBlockchainHeightChangeObserver : public CryptoNote::INodeObserver {
Tests::Common::Semaphore& m_changed;
public:
WaitForBlockchainHeightChangeObserver(Tests::Common::Semaphore& changed) : m_changed(changed) { }
virtual void lastKnownBlockHeightUpdated(uint32_t height) override {
m_changed.notify();
}
};
class CallbackHeightChangeObserver : public CryptoNote::INodeObserver {
std::function<void(uint32_t)> m_callback;
public:
CallbackHeightChangeObserver(std::function<void(uint32_t)> callback) : m_callback(callback) {}
virtual void lastKnownBlockHeightUpdated(uint32_t height) override {
m_callback(height);
}
};
bool perform2(size_t blocksCount = 10)
{
using namespace Tests::Common;
launchTestnet(3, Line);
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
LOG_TRACE("STEP 1 PASSED");
mineBlock();
mineBlock();
LOG_TRACE("STEP 2 PASSED");
std::unique_ptr<CryptoNote::INode> localNode;
std::unique_ptr<CryptoNote::INode> remoteNode;
nodeDaemons.front()->makeINode(localNode);
nodeDaemons.back()->makeINode(remoteNode);
std::unique_ptr<CryptoNote::IWalletLegacy> wallet;
makeWallet(wallet, localNode);
LOG_TRACE("STEP 3 PASSED");
Semaphore blockMined;
Semaphore blockArrivedToRemote;
WaitForBlockchainHeightChangeObserver localHCO(blockMined);
WaitForBlockchainHeightChangeObserver remoteHCO(blockArrivedToRemote);
localNode->addObserver(&localHCO);
remoteNode->addObserver(&remoteHCO);
for (size_t blockNumber = 0; blockNumber < blocksCount; ++blockNumber) {
nodeDaemons.front()->startMining(1, wallet->getAddress());
blockMined.wait();
CHECK_AND_ASSERT_MES(blockArrivedToRemote.wait_for(std::chrono::milliseconds(5000)), false, "block propagation too slow >5000ms.");
nodeDaemons.front()->stopMining();
LOG_TRACE("STEP 4 STAGE " + TO_STRING(blockNumber+1) + " of " + TO_STRING(blocksCount)+" PASSED");
}
return true;
}
bool perform4() {
using namespace CryptoNote;
using namespace Tests::Common;
launchTestnet(3, Star);
LOG_TRACE("STEP 1 PASSED");
std::unique_ptr<CryptoNote::INode> hopNode;
std::unique_ptr<CryptoNote::INode> localNode;
std::unique_ptr<CryptoNote::INode> remoteNode;
nodeDaemons[0]->makeINode(hopNode);
nodeDaemons[1]->makeINode(localNode);
nodeDaemons[2]->makeINode(remoteNode);
LOG_TRACE("STEP 2 PASSED");
std::string test_block1_hex =
"0101b392d79f05a742885cb01d11b7b36fb8bf14616d42cd3d8c1429a224df41afa81b86b8a3a84e"
"d8c33f010b01ff0108c0a62d02cc353782cbe4c6067bd30510f11d1f2993f2c7fed37239f299ffe3"
"f96f135675c096b102023e8d4b2c22d73f91d0d9f8e0e12c8df24e5917f00d0b2dd99786c5bb0e5b"
"300580bbb021022764ae61c084db07e7cd83c55e9c833f42b1d422e1008220fdb4acc726b94ea980"
"88debe01023330c2b7dc4840f478066370ae48b148ce8dd010c59f6ecc08598682d32f07d080a0d9"
"e61d02bcf35dc40ead54a614174774e60d8f5d0e46272c70bc7e70f205f7ccef25c34980b09dc2df"
"01026ddcf1aed901f018453fd9352a01d5a44067d271ca403b4cd799d9832076daa280f092cbdd08"
"021f613eab32b76ed03f6a796de7a5c92009ea9f9b9e3299ec91df7657cd694e5580c089a9a2f50f"
"02f545046885a297ba63a2c7b305a74fdb741129cc367330661c1363e0bb0f0d0b2101acf052dcbe"
"407bc34df1b7fffc17f0bfb0ffc23002e2b6de48a210df6f78bf1400";
std::string test_block2_hex =
"0101b492d79f05456231a956ed3a8c1ac0bfe8efc1bb5d522d8474e566b051919ddea0ceab478a74"
"e35210010c01ff0207c0e41202b2d7e697c6e2e894f9e98262c278235720b39f3a149774cb58cb52"
"e5dde21601c09fab0302abffefad3afab42ca1ce2f7dccfa6942256f31387b307becd43571cfe22a"
"10688084af5f02e2ab32d9b8fb8ced4bf4a81de0f48c23dc575076e8d233a3532d28f36e79035380"
"a0d9e61d020c10664fe1ca35418733fa32ae2deadd4bf7ed982bb5d11ba98a7940a73e161580b09d"
"c2df010266c3bfa27436b480a217a2fe06df714f4d2094ec1a0ced3bac2d96881972e28a80f092cb"
"dd0802f31e9ac25fb8afd1d9d964331242a94f023c3188db5e532b5a9c800a843a3ebc80c089a9a2"
"f50f0206244fcc73941c3da62ea6d62d679bedb311fc530d149099bdfd04c59cd507a121019d4b74"
"f09454ccfdd6ca44b8c5f73c6805ea08dbe6a71769b058e158b2d4df5100";
std::string test_block3_hex =
"0101b492d79f051f6fe6d9f7c14c0d5e16ba82d9ea68e4e6d6f30726854d45330aeb2fae5c1cd3fb"
"7f4352010d01ff0308ffae350220f4c1c7631ecf4247688c376665df2b9dd935af6e4c027c9cddcb"
"400fefec7380a4e803029e05ef9b3295e178d0f3199fca420f909f04fdab09b97c14290c8a913e42"
"19c68087a70e02693641fefb1a6da81c2308370f349ef5e4adab792ae06b5da989ae3f1b7a13ca80"
"d293ad0302c14d721ed8da5c98f108ef17c326737765857ddfa0b705fd4483cfa7ffeaad51808cee"
"891a02f5a8e2ac24d6a9f789e5514de520c3ac28387788e130e22c4250b7d1be47460380b09dc2df"
"0102cc3b2f894b416f3e09afae0395fc01cc2ec9763dff72839944e60055049ea37d80f092cbdd08"
"02772df06a2cd92c174815ae1572799430ea01e903796f6a763648c7b350151ce580c089a9a2f50f"
"0211c7bea98edba4fad6d3f19b330a676b8fb0391f7a99f45542e7cf52d39d6c632101e0370c5c79"
"e99d772b41e0569bc41e1ebde2e563cdb7f5bdd23984899fad103200";
std::string test_block4_hex =
"0101b492d79f0537da79424e1cc69d16aadf174dcf443947f8027695a5d1e30b2be4f5aa71904194"
"47fe54010e01ff0407fffc1a0278eb82c9ea2f1e998906cec55caf26e347224c3391fb0aa2213bc1"
"5eec4dacc580bbb021024ce32a63614269f43f698644c98fd9b7a11694dc69fd5126f6f6735ba6c5"
"98dd808c8d9e0202d539ead46faf6d786964dd5106004612eb8d64778ad4fe8befa8c63e4d666f92"
"808cee891a021a6c6669298dcc1c86af887804f128123d95a6d96b5884db97cfc96fa9ad018e80b0"
"9dc2df0102dd3b9bbfef1eddeef8c406de9c0c4fc469c8069c910541252491df5a482fd5e380f092"
"cbdd0802176a4cb411309761b7f50b0f495e99cc55cbaae70011d3c901e409a8a938f1b680c089a9"
"a2f50f02bb232a77911350a1315de0b3de447142390f97e5ef25ecc1bf5837a8972b4b5e2101ef54"
"5c318e38cfdd92362340fab6ec6630e4134b93cfd01db4d9a42fa945fdef00";
Semaphore blockArrivedToRemote;
WaitForBlockchainHeightChangeObserver remoteHCO(blockArrivedToRemote);
std::chrono::steady_clock::time_point localAdded;
std::chrono::steady_clock::time_point hoplAdded;
std::chrono::steady_clock::time_point remoteAdded;
std::chrono::steady_clock::time_point submitInvokingStart;
std::chrono::steady_clock::time_point submitInvoked;
//auto height = localNode->getLastKnownBlockHeight();
CallbackHeightChangeObserver CHCOLocal([&localAdded](uint64_t new_height){localAdded = std::chrono::steady_clock::now(); });
CallbackHeightChangeObserver CHCOHop([&hoplAdded](uint64_t new_height){hoplAdded = std::chrono::steady_clock::now(); });
CallbackHeightChangeObserver CHCORemote([&remoteAdded](uint64_t new_height){remoteAdded = std::chrono::steady_clock::now(); });
localNode->addObserver(&CHCOLocal);
hopNode->addObserver(&CHCOHop);
remoteNode->addObserver(&CHCORemote);
remoteNode->addObserver(&remoteHCO);
LOG_TRACE("test_block1");
submitInvokingStart = std::chrono::steady_clock::now();
if (!nodeDaemons[1]->submitBlock(test_block1_hex)) return false;
submitInvoked = std::chrono::steady_clock::now();
CHECK_AND_ASSERT_MES(blockArrivedToRemote.wait_for(std::chrono::milliseconds(10000)), false, "block 1 propagation too slow >10000ms.");
LOG_TRACE("submitBlock() invocation takes: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(submitInvoked - submitInvokingStart).count()) + " ms");
LOG_TRACE("HeightChangedCallback() since submit : " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(localAdded - submitInvoked).count()) + " ms");
LOG_TRACE("Local -> HopNode: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(hoplAdded - localAdded).count()) + " ms");
LOG_TRACE("HopNode -> Remote: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(remoteAdded - hoplAdded).count()) + " ms");
LOG_TRACE("Local -> Remote: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(remoteAdded - localAdded).count()) + " ms");
LOG_TRACE("test_block2");
submitInvokingStart = std::chrono::steady_clock::now();
if (!nodeDaemons[1]->submitBlock(test_block2_hex)) return false;
submitInvoked = std::chrono::steady_clock::now();
CHECK_AND_ASSERT_MES(blockArrivedToRemote.wait_for(std::chrono::milliseconds(10000)), false, "block 2 propagation too slow >10000ms.");
LOG_TRACE("submitBlock() invocation takes: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(submitInvoked - submitInvokingStart).count()) + " ms");
LOG_TRACE("HeightChangedCallback() since submit : " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(localAdded - submitInvoked).count()) + " ms");
LOG_TRACE("Local -> HopNode: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(hoplAdded - localAdded).count()) + " ms");
LOG_TRACE("HopNode -> Remote: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(remoteAdded - hoplAdded).count()) + " ms");
LOG_TRACE("Local -> Remote: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(remoteAdded - localAdded).count()) + " ms");
LOG_TRACE("test_block3");
submitInvokingStart = std::chrono::steady_clock::now();
if (!nodeDaemons[1]->submitBlock(test_block3_hex)) return false;
submitInvoked = std::chrono::steady_clock::now();
CHECK_AND_ASSERT_MES(blockArrivedToRemote.wait_for(std::chrono::milliseconds(10000)), false, "block 3 propagation too slow >10000ms.");
LOG_TRACE("submitBlock() invocation takes: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(submitInvoked - submitInvokingStart).count()) + " ms");
LOG_TRACE("HeightChangedCallback() since submit : " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(localAdded - submitInvoked).count()) + " ms");
LOG_TRACE("Local -> HopNode: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(hoplAdded - localAdded).count()) + " ms");
LOG_TRACE("HopNode -> Remote: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(remoteAdded - hoplAdded).count()) + " ms");
LOG_TRACE("Local -> Remote: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(remoteAdded - localAdded).count()) + " ms");
LOG_TRACE("test_block4");
submitInvokingStart = std::chrono::steady_clock::now();
if (!nodeDaemons[1]->submitBlock(test_block4_hex)) return false;
submitInvoked = std::chrono::steady_clock::now();
CHECK_AND_ASSERT_MES(blockArrivedToRemote.wait_for(std::chrono::milliseconds(10000)), false, "block 4 propagation too slow >10000ms.");
LOG_TRACE("submitBlock() invocation takes: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(submitInvoked - submitInvokingStart).count()) + " ms");
LOG_TRACE("HeightChangedCallback() since submit : " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(localAdded - submitInvoked).count()) + " ms");
LOG_TRACE("Local -> HopNode: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(hoplAdded - localAdded).count()) + " ms");
LOG_TRACE("HopNode -> Remote: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(remoteAdded - hoplAdded).count()) + " ms");
LOG_TRACE("Local -> Remote: " + TO_STRING(std::chrono::duration_cast<std::chrono::milliseconds>(remoteAdded - localAdded).count()) + " ms");
localNode.release();
remoteNode.release();
hopNode.release();
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
return true;
}
bool perform5() {
using namespace Tests::Common;
using namespace CryptoNote;
const uint64_t FEE = 1000000;
launchTestnetWithInprocNode(2);
std::unique_ptr<CryptoNote::INode> node1;
std::unique_ptr<CryptoNote::INode> inprocNode;
nodeDaemons.front()->makeINode(node1);
nodeDaemons.back()->makeINode(inprocNode);
while (node1->getLastLocalBlockHeight() != inprocNode->getLastLocalBlockHeight()) {
LOG_TRACE("Syncing...");
std::this_thread::sleep_for(std::chrono::seconds(1));
}
LOG_TRACE("STEP 1 PASSED");
std::unique_ptr<CryptoNote::IWalletLegacy> wallet1;
std::unique_ptr<CryptoNote::IWalletLegacy> wallet2;
makeWallet(wallet1, node1);
makeWallet(wallet2, inprocNode);
CHECK_AND_ASSERT_MES(mineBlock(), false, "can't mine block");
CHECK_AND_ASSERT_MES(mineBlock(), false, "can't mine block");
LOG_TRACE("STEP 2 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
CHECK_AND_ASSERT_MES(mineBlock(wallet1), false, "can't mine block on wallet 1");
LOG_TRACE("STEP 3 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
Semaphore wallet1GotActual;
WaitForConfirmationObserver wallet1ActualGrown(wallet1GotActual, [&wallet1](uint64_t actual)->bool {return wallet1->pendingBalance() == actual; });
wallet1->addObserver(&wallet1ActualGrown);
CHECK_AND_ASSERT_MES(startMining(1), false, "startMining(1) failed");
wallet1GotActual.wait();
LOG_TRACE("STEP 4 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
CHECK_AND_ASSERT_MES(stopMining(), false, "stopMining() failed");
auto wallet1ActualBeforeTransaction = wallet1->actualBalance();
auto wallet1PendingBeforeTransaction = wallet1->pendingBalance();
auto wallet2ActualBeforeTransaction = wallet2->actualBalance();
auto wallet2PendingBeforeTransaction = wallet2->pendingBalance();
CryptoNote::WalletLegacyTransfer tr;
tr.address = wallet2->getAddress();
tr.amount = wallet1ActualBeforeTransaction / 2;
std::error_code result;
Semaphore w2GotPending;
WaitForPendingGrowObserver pgo1(w2GotPending, wallet2PendingBeforeTransaction);
wallet2->addObserver(&pgo1);
WaitForExternalTransactionObserver poolTxWaiter;
auto future = poolTxWaiter.promise.get_future();
wallet2->addObserver(&poolTxWaiter);
wallet1->sendTransaction(tr, FEE);
auto txId = future.get();
w2GotPending.wait();
wallet2->removeObserver(&poolTxWaiter);
CryptoNote::WalletLegacyTransaction txInfo;
wallet2->getTransaction(txId, txInfo);
auto wallet2PendingAfterTransaction = wallet2->pendingBalance();
auto wallet1PendingAfterTransaction = wallet1->pendingBalance();
auto w2PendingDiff = wallet2PendingAfterTransaction - wallet2PendingBeforeTransaction;
auto w1PendingDiff = wallet1PendingBeforeTransaction - wallet1PendingAfterTransaction;
CHECK_AND_ASSERT_MES((txInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT), false, "STEP 5 ASSERTION 1 FAILED\r\n Transaction blockHeight differs unconfirmed_tx_height");
CHECK_AND_ASSERT_MES((tr.amount == txInfo.totalAmount), false, "STEP 5 ASSERTION 2 FAILED\r\n Transfered amount " + m_currency.formatAmount(tr.amount) + " doesn't match recieved amount from pool transaction " + m_currency.formatAmount(txInfo.totalAmount));
CHECK_AND_ASSERT_MES((tr.amount == w2PendingDiff), false, "STEP 5 ASSERTION 3 FAILED\r\n Transfered amount " + m_currency.formatAmount(tr.amount) + " doesn't match recieved amount " + m_currency.formatAmount(w2PendingDiff));
CHECK_AND_ASSERT_MES((w1PendingDiff - tr.amount - FEE == 0), false,
"STEP 5 ASSERTION 4 FAILED\r\n wallet1 Pending Before Transaction doesn't match wallet1 Pending After Transaction + Transfered amount + Fee "
+ m_currency.formatAmount(wallet1PendingBeforeTransaction) + " <> " + m_currency.formatAmount(wallet1PendingAfterTransaction) + "+" + m_currency.formatAmount(tr.amount) + "+" + m_currency.formatAmount(FEE));
LOG_TRACE("STEP 5 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
WaitForTransactionUpdated trasactionConfirmationObserver;
trasactionConfirmationObserver.expectindTxId = txId;
wallet2->addObserver(&trasactionConfirmationObserver);
auto txUpdated = trasactionConfirmationObserver.promise.get_future();
CHECK_AND_ASSERT_MES(mineBlock(), false, "mineBlock() failed");
CHECK_AND_ASSERT_MES(mineBlock(), false, "mineBlock() failed");
txUpdated.get();
wallet2->getTransaction(txId, txInfo);
wallet2->removeObserver(&trasactionConfirmationObserver);
CHECK_AND_ASSERT_MES(txInfo.blockHeight <= inprocNode->getLastLocalBlockHeight(), false, "STEP 6 ASSERTION FAILED tx height confirmation failed");
LOG_TRACE("STEP 6 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
CHECK_AND_ASSERT_MES(startMining(1), false, "startMining(1) failed");
Semaphore confirmed2;
Semaphore confirmed1;
WaitForConfirmationObserver confirmationObserver2(confirmed2, [&wallet2](uint64_t actual)->bool {return wallet2->pendingBalance() == actual; });
WaitForConfirmationObserver confirmationObserver1(confirmed1, [&wallet1](uint64_t actual)->bool {return wallet1->pendingBalance() == actual; });
wallet2->addObserver(&confirmationObserver2);
wallet1->addObserver(&confirmationObserver1);
if (wallet2->pendingBalance() != wallet2->actualBalance()) confirmed2.wait();
if (wallet1->pendingBalance() != wallet1->actualBalance()) confirmed1.wait();
CHECK_AND_ASSERT_MES(stopMining(), false, "stopMining() failed");
auto wallet1ActualAfterTransactionAndConfirmation = wallet1->actualBalance();
auto wallet2ActualAfterTransactionAndConfirmation = wallet2->actualBalance();
auto w2ActualDiff = wallet2ActualAfterTransactionAndConfirmation - wallet2ActualBeforeTransaction;
auto w1ActualDiff = wallet1ActualBeforeTransaction - wallet1ActualAfterTransactionAndConfirmation;
CHECK_AND_ASSERT_MES((tr.amount == w2ActualDiff), false, "STEP 7 FAILED\r\n Transfered amount " + m_currency.formatAmount(tr.amount) + " doesn't match confirmed recieved amount " + m_currency.formatAmount(w2ActualDiff));
CHECK_AND_ASSERT_MES((w1ActualDiff - tr.amount - FEE == 0), false,
"STEP 7 FAILED\r\n wallet1 Actual Before Transaction doesn't match wallet1 Actual After Transaction + Transfered amount + Fee "
+ m_currency.formatAmount(wallet1ActualBeforeTransaction) + " <> " + m_currency.formatAmount(wallet1ActualAfterTransactionAndConfirmation) + "+" + m_currency.formatAmount(tr.amount) + "+" + m_currency.formatAmount(FEE));
LOG_TRACE("STEP 7 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
wallet1->removeObserver(&wallet1ActualGrown);
wallet2->removeObserver(&pgo1);
wallet2->removeObserver(&confirmationObserver2);
wallet1->removeObserver(&confirmationObserver1);
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
return true;
}
bool perform6() {
using namespace Tests::Common;
using namespace CryptoNote;
const uint64_t FEE = 1000000;
launchTestnetWithInprocNode(2);
std::unique_ptr<CryptoNote::INode> node1;
std::unique_ptr<CryptoNote::INode> inprocNode;
nodeDaemons.front()->makeINode(node1);
nodeDaemons.back()->makeINode(inprocNode);
while (node1->getLastLocalBlockHeight() != inprocNode->getLastLocalBlockHeight()) {
LOG_TRACE("Syncing...");
std::this_thread::sleep_for(std::chrono::seconds(1));
}
LOG_TRACE("STEP 1 PASSED");
std::unique_ptr<CryptoNote::IWalletLegacy> wallet1;
std::unique_ptr<CryptoNote::IWalletLegacy> wallet2;
makeWallet(wallet1, node1);
makeWallet(wallet2, inprocNode);
CHECK_AND_ASSERT_MES(mineBlock(), false, "can't mine block");
CHECK_AND_ASSERT_MES(mineBlock(), false, "can't mine block");
LOG_TRACE("STEP 2 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
CHECK_AND_ASSERT_MES(mineBlock(wallet1), false, "can't mine block on wallet 1");
LOG_TRACE("STEP 3 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
Semaphore wallet1GotActual;
WaitForConfirmationObserver wallet1ActualGrown(wallet1GotActual, [&wallet1](uint64_t actual)->bool {return wallet1->pendingBalance() == actual; });
wallet1->addObserver(&wallet1ActualGrown);
CHECK_AND_ASSERT_MES(startMining(1), false, "startMining(1) failed");
wallet1GotActual.wait();
LOG_TRACE("STEP 4 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
CHECK_AND_ASSERT_MES(stopMining(), false, "stopMining() failed");
auto wallet1ActualBeforeTransaction = wallet1->actualBalance();
auto wallet1PendingBeforeTransaction = wallet1->pendingBalance();
auto wallet2PendingBeforeTransaction = wallet2->pendingBalance();
CryptoNote::WalletLegacyTransfer tr;
tr.address = wallet2->getAddress();
tr.amount = wallet1ActualBeforeTransaction / 2;
std::error_code result;
Semaphore w2GotPending;
WaitForPendingGrowObserver pgo1(w2GotPending, wallet2PendingBeforeTransaction);
wallet2->addObserver(&pgo1);
WaitForExternalTransactionObserver poolTxWaiter;
auto future = poolTxWaiter.promise.get_future();
wallet2->addObserver(&poolTxWaiter);
wallet1->sendTransaction(tr, FEE);
auto txId = future.get();
w2GotPending.wait();
wallet2->removeObserver(&poolTxWaiter);
CryptoNote::WalletLegacyTransaction txInfo;
wallet2->getTransaction(txId, txInfo);
auto wallet2PendingAfterTransaction = wallet2->pendingBalance();
auto wallet1PendingAfterTransaction = wallet1->pendingBalance();
auto w2PendingDiff = wallet2PendingAfterTransaction - wallet2PendingBeforeTransaction;
auto w1PendingDiff = wallet1PendingBeforeTransaction - wallet1PendingAfterTransaction;
CHECK_AND_ASSERT_MES((txInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT), false, "STEP 5 ASSERTION 1 FAILED\r\n Transaction blockHeight differs unconfirmed_tx_height");
CHECK_AND_ASSERT_MES((tr.amount == txInfo.totalAmount), false, "STEP 5 ASSERTION 2 FAILED\r\n Transfered amount " + m_currency.formatAmount(tr.amount) + " doesn't match recieved amount from pool transaction " + m_currency.formatAmount(txInfo.totalAmount));
CHECK_AND_ASSERT_MES((tr.amount == w2PendingDiff), false, "STEP 5 ASSERTION 3 FAILED\r\n Transfered amount " + m_currency.formatAmount(tr.amount) + " doesn't match recieved amount " + m_currency.formatAmount(w2PendingDiff));
CHECK_AND_ASSERT_MES((w1PendingDiff - tr.amount - FEE == 0), false,
"STEP 5 ASSERTION 4 FAILED\r\n wallet1 Pending Before Transaction doesn't match wallet1 Pending After Transaction + Transfered amount + Fee "
+ m_currency.formatAmount(wallet1PendingBeforeTransaction) + " <> " + m_currency.formatAmount(wallet1PendingAfterTransaction) + "+" + m_currency.formatAmount(tr.amount) + "+" + m_currency.formatAmount(FEE));
LOG_TRACE("STEP 5 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
WaitForTransactionUpdated trasactionDeletionObserver;
trasactionDeletionObserver.expectindTxId = txId;
wallet2->addObserver(&trasactionDeletionObserver);
auto txUpdated = trasactionDeletionObserver.promise.get_future();
txUpdated.get();
wallet2->getTransaction(txId, txInfo);
wallet2->removeObserver(&trasactionDeletionObserver);
CHECK_AND_ASSERT_MES(txInfo.state == WalletLegacyTransactionState::Deleted, false, "STEP 6 ASSERTION 1 FAILED tx not deleted");
CHECK_AND_ASSERT_MES(wallet2PendingBeforeTransaction == wallet2->pendingBalance(), false, "STEP 6 ASSERTION 2 FAILED current pending balance <> pending balance before transaction");
LOG_TRACE("STEP 6 PASSED");
LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance()));
LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance()));
LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance()));
LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance()));
wallet1->removeObserver(&wallet1ActualGrown);
wallet2->removeObserver(&pgo1);
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
return true;
}
};
void testMultiVersion(const CryptoNote::Currency& currency, System::Dispatcher& d, const Tests::Common::BaseFunctionalTestsConfig& config);
class SimpleTestCase : public ::testing::Test {
public:
SimpleTestCase() :
currency(CryptoNote::CurrencyBuilder(logger).testnet(true).currency()),
test(currency, dispatcher, baseCfg) {
}
System::Dispatcher dispatcher;
Logging::ConsoleLogger logger;
CryptoNote::Currency currency;
SimpleTest test;
};
TEST_F(SimpleTestCase, WALLET2WALLET) {
ASSERT_TRUE(test.perform1());
}
TEST_F(SimpleTestCase, BLOCKTHRUDAEMONS) {
ASSERT_TRUE(test.perform2());
}
TEST_F(SimpleTestCase, RELAYBLOCKTHRUDAEMONS) {
ASSERT_TRUE(test.perform4());
}
TEST_F(SimpleTestCase, TESTPOOLANDINPROCNODE) {
ASSERT_TRUE(test.perform5());
}
TEST_F(SimpleTestCase, TESTPOOLDELETION) {
currency = CryptoNote::CurrencyBuilder(logger).testnet(true).mempoolTxLiveTime(60).currency();
ASSERT_TRUE(test.perform6());
}
TEST_F(SimpleTestCase, MULTIVERSION) {
ASSERT_NO_THROW(testMultiVersion(currency, dispatcher, baseCfg));
}
int main(int argc, char** argv) {
CLogger::Instance().init(CLogger::DEBUG);
try {
::Configuration config;
if (!config.handleCommandLine(argc, argv)) {
return 0; //help message requested or so
}
baseCfg = config;
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
catch (::ConfigurationError& ex) {
std::cerr << "Configuration error: " << ex.what() << std::endl;
return 1;
}
catch (std::exception& ex) {
LOG_ERROR("Fatal error: " + std::string(ex.what()));
return 1;
}
return 0;
}