// 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. #pragma once #include #include #include #include #include #include #include #include #include #include #include "CryptoNoteCore/Currency.h" #include "IWalletLegacy.h" #include "INode.h" #include "TestNode.h" #include "NetworkConfiguration.h" namespace Tests { namespace Common { namespace po = boost::program_options; class Semaphore{ private: std::mutex mtx; std::condition_variable cv; bool available; public: Semaphore() : available(false) { } void notify() { std::unique_lock lck(mtx); available = true; cv.notify_one(); } void wait() { std::unique_lock lck(mtx); cv.wait(lck, [this](){ return available; }); available = false; } bool wait_for(const std::chrono::milliseconds& rel_time) { std::unique_lock lck(mtx); auto result = cv.wait_for(lck, rel_time, [this](){ return available; }); available = false; return result; } }; const uint16_t P2P_FIRST_PORT = 9000; const uint16_t RPC_FIRST_PORT = 9200; class BaseFunctionalTestsConfig { public: BaseFunctionalTestsConfig() {} void init(po::options_description& desc) { desc.add_options() ("daemon-dir,d", po::value()->default_value("."), "path to daemon") ("data-dir,n", po::value()->default_value("."), "path to daemon's data directory") ("add-daemons,a", po::value>()->multitoken(), "add daemon to topology"); } bool handleCommandLine(const po::variables_map& vm) { if (vm.count("daemon-dir")) { daemonDir = vm["daemon-dir"].as(); } if (vm.count("data-dir")) { dataDir = vm["data-dir"].as(); } if (vm.count("add-daemons")) { daemons = vm["add-daemons"].as>(); } return true; } std::string daemonDir; std::string dataDir; std::vector daemons; }; class BaseFunctionalTests : boost::noncopyable { public: BaseFunctionalTests(const CryptoNote::Currency& currency, System::Dispatcher& d, const BaseFunctionalTestsConfig& config) : m_dispatcher(d), m_currency(currency), m_nextTimestamp(time(nullptr) - 365 * 24 * 60 * 60), m_config(config), m_dataDir(config.dataDir), m_daemonDir(config.daemonDir), m_testnetSize(1) { if (m_dataDir.empty()) { m_dataDir = "."; } if (m_daemonDir.empty()) { m_daemonDir = "."; } }; ~BaseFunctionalTests(); enum Topology { Ring, Line, Star }; protected: TestNodeConfiguration createNodeConfiguration(size_t i); std::vector< std::unique_ptr > nodeDaemons; System::Dispatcher& m_dispatcher; const CryptoNote::Currency& m_currency; void launchTestnet(size_t count, Topology t = Line); void launchTestnetWithInprocNode(size_t count, Topology t = Line); void launchInprocTestnet(size_t count, Topology t = Line); void stopTestnet(); void startNode(size_t index); void stopNode(size_t index); bool makeWallet(std::unique_ptr & wallet, std::unique_ptr& node, const std::string& password = "pass"); bool mineBlocks(TestNode& node, const CryptoNote::AccountPublicAddress& address, size_t blockCount); bool mineBlock(std::unique_ptr& wallet); bool mineBlock(); bool startMining(size_t threads); bool stopMining(); bool getNodeTransactionPool(size_t nodeIndex, CryptoNote::INode& node, std::vector>& txPool); bool waitDaemonsReady(); bool waitDaemonReady(size_t nodeIndex); bool waitForPeerCount(CryptoNote::INode& node, size_t expectedPeerCount); bool waitForPoolSize(size_t nodeIndex, CryptoNote::INode& node, size_t expectedPoolSize, std::vector>& txPool); bool prepareAndSubmitBlock(TestNode& node, CryptoNote::Block&& blockTemplate); private: #ifdef __linux__ std::vector<__pid_t> pids; #endif Logging::ConsoleLogger logger; std::unique_ptr mainNode; std::unique_ptr workingWallet; uint64_t m_nextTimestamp; Topology m_topology; size_t m_testnetSize; BaseFunctionalTestsConfig m_config; std::string m_dataDir; std::string m_daemonDir; uint16_t m_mainDaemonRPCPort; }; } }