danicoin/tests/IntegrationTestLib/TestNetwork.cpp
2016-01-18 15:33:29 +00:00

259 lines
6.3 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 "TestNetwork.h"
#include <fstream>
#include <boost/filesystem.hpp>
#include "CryptoNoteConfig.h"
#include "InProcTestNode.h"
#include "RPCTestNode.h"
#ifdef _WIN32
const std::string daemonExec = std::string(CryptoNote::CRYPTONOTE_NAME) + "d.exe";
#else
const std::string daemonExec = std::string(CryptoNote::CRYPTONOTE_NAME) + "d";
#endif
namespace {
using namespace Tests;
void writeConfiguration(const std::string& confFile, const TestNodeConfiguration& cfg) {
std::ofstream config(confFile, std::ios_base::trunc | std::ios_base::out);
config
<< "rpc-bind-port=" << cfg.rpcPort << std::endl
<< "p2p-bind-port=" << cfg.p2pPort << std::endl
<< "log-level=4" << std::endl
<< "log-file=" << cfg.logFile << std::endl;
for (const auto& ex : cfg.exclusiveNodes) {
config << "add-exclusive-node=" << ex << std::endl;
}
}
bool waitDaemonReady(TestNode& node) {
for (size_t i = 0;; ++i) {
if (node.getLocalHeight() > 0) {
break;
} else if (i < 2 * 60) {
std::this_thread::sleep_for(std::chrono::seconds(1));
} else {
return false;
}
}
return true;
}
void copyBlockchainFiles(bool testnet, const std::string& from, const std::string& to) {
boost::filesystem::path fromPath(from);
boost::filesystem::path toPath(to);
auto files = {
std::make_pair("blockindexes.dat", true),
std::make_pair("blocks.dat", true),
std::make_pair("blockscache.dat", false),
std::make_pair("blockchainindices.dat", false)
};
for (const auto& item : files) {
try {
boost::filesystem::path filePath = std::string(testnet ? "testnet_" : "") + item.first;
boost::filesystem::copy(fromPath / filePath, toPath / filePath);
} catch (...) {
if (item.second) {
// if file is required, the rethrow error
throw;
}
}
}
}
}
namespace Tests {
TestNetworkBuilder::TestNetworkBuilder(size_t nodeCount, Topology topology, uint16_t rpcBasePort, uint16_t p2pBasePort) :
nodeCount(nodeCount),
topology(topology),
rpcBasePort(rpcBasePort),
p2pBasePort(p2pBasePort),
baseDataDir("."),
testnet(true)
{}
std::vector<TestNodeConfiguration> TestNetworkBuilder::build() {
std::vector<TestNodeConfiguration> cfg;
for (size_t i = 0; i < nodeCount; ++i) {
cfg.push_back(buildNodeConfiguration(i));
}
return cfg;
}
TestNetworkBuilder& TestNetworkBuilder::setDataDirectory(const std::string& dataDir) {
this->baseDataDir = dataDir;
return *this;
}
TestNetworkBuilder& TestNetworkBuilder::setBlockchain(const std::string& blockchainDir) {
this->blockchainLocation = blockchainDir;
return *this;
}
TestNetworkBuilder& TestNetworkBuilder::setTestnet(bool isTestnet) {
this->testnet = isTestnet;
return *this;
}
TestNodeConfiguration TestNetworkBuilder::buildNodeConfiguration(size_t index) {
TestNodeConfiguration cfg;
if (!baseDataDir.empty()) {
cfg.dataDir = baseDataDir + "/node" + std::to_string(index);
}
if (!blockchainLocation.empty()) {
cfg.blockchainLocation = blockchainLocation;
}
cfg.daemonPath = daemonExec; // default
cfg.testnet = testnet;
cfg.logFile = std::string("test_") + CryptoNote::CRYPTONOTE_NAME + "d" + std::to_string(index) + ".log";
uint16_t rpcPort = static_cast<uint16_t>(rpcBasePort + index);
uint16_t p2pPort = static_cast<uint16_t>(p2pBasePort + index);
cfg.p2pPort = p2pPort;
cfg.rpcPort = rpcPort;
switch (topology) {
case Topology::Line:
if (index != 0) {
cfg.exclusiveNodes.push_back("127.0.0.1:" + std::to_string(p2pPort - 1));
}
break;
case Topology::Ring: {
uint16_t p2pExternalPort = static_cast<uint16_t>(p2pBasePort + (index + 1) % nodeCount);
cfg.exclusiveNodes.push_back("127.0.0.1:" + std::to_string(p2pExternalPort));
break;
}
case Topology::Star:
if (index == 0) {
for (size_t node = 1; node < nodeCount; ++node) {
cfg.exclusiveNodes.push_back("127.0.0.1:" + std::to_string(p2pBasePort + node));
}
}
break;
}
return cfg;
}
TestNetwork::TestNetwork(System::Dispatcher& dispatcher, const CryptoNote::Currency& currency) :
m_dispatcher(dispatcher),
m_currency(currency) {
}
void TestNetwork::addNodes(const std::vector<TestNodeConfiguration>& nodes) {
for (const auto& n : nodes) {
addNode(n);
}
}
void TestNetwork::addNode(const TestNodeConfiguration& cfg) {
std::unique_ptr<TestNode> node;
boost::system::error_code ec;
boost::filesystem::remove_all(cfg.dataDir, ec);
boost::filesystem::create_directory(cfg.dataDir);
if (!cfg.blockchainLocation.empty()) {
copyBlockchainFiles(cfg.testnet, cfg.blockchainLocation, cfg.dataDir);
}
switch (cfg.nodeType) {
case NodeType::InProcess:
node.reset(new InProcTestNode(cfg, m_currency));
break;
case NodeType::RPC:
node = startDaemon(cfg);
break;
}
nodes.push_back(std::make_pair(std::move(node), cfg));
}
void TestNetwork::waitNodesReady() {
for (auto& node : nodes) {
if (!waitDaemonReady(*node.first)) {
throw std::runtime_error("Daemon startup failure (timeout)");
}
}
}
TestNode& TestNetwork::getNode(size_t index) {
if (index >= nodes.size()) {
throw std::runtime_error("Invalid node index");
}
return *nodes[index].first;
}
void TestNetwork::shutdown() {
for (auto& node : nodes) {
node.first->stopDaemon();
}
for (auto& daemon : m_daemons) {
daemon.wait();
}
std::this_thread::sleep_for(std::chrono::seconds(1));
for (auto& node : nodes) {
if (node.second.cleanupDataDir) {
boost::filesystem::remove_all(node.second.dataDir);
}
}
}
std::unique_ptr<TestNode> TestNetwork::startDaemon(const TestNodeConfiguration& cfg) {
if (!boost::filesystem::exists(cfg.daemonPath)) {
throw std::runtime_error("daemon binary wasn't found");
}
writeConfiguration(cfg.dataDir + "/daemon.conf", cfg);
Process process;
std::vector<std::string> daemonArgs = { "--data-dir=" + cfg.dataDir, "--config-file=daemon.conf" };
if (cfg.testnet) {
daemonArgs.emplace_back("--testnet");
}
process.startChild(cfg.daemonPath, daemonArgs);
std::unique_ptr<TestNode> node(new RPCTestNode(cfg.rpcPort, m_dispatcher));
m_daemons.push_back(process);
return node;
}
}