// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see .
#include "gtest/gtest.h"
#include
#include
#include "System/ContextGroup.h"
#include "System/Dispatcher.h"
#include "System/Event.h"
#include "CryptoNoteCore/MessageQueue.h"
#include "CryptoNoteCore/BlockchainMessages.h"
#include "CryptoNoteCore/IntrusiveLinkedList.h"
#include "CryptoNoteCore/CryptoNoteTools.h"
using namespace CryptoNote;
class MessageQueueTest : public testing::Test {
public:
MessageQueueTest() : contextGroup(dispatcher) {}
bool addMessageQueue(MessageQueue& messageQueue);
bool removeMessageQueue(MessageQueue& messageQueue);
void sendBlockchainMessage(const BlockchainMessage& message);
void interruptBlockchainMessageWaiting();
void SetUp();
void TearDown();
protected:
System::Dispatcher dispatcher;
System::ContextGroup contextGroup;
IntrusiveLinkedList> blockchainMessageQueueList;
};
bool MessageQueueTest::addMessageQueue(MessageQueue& messageQueue) {
return blockchainMessageQueueList.insert(messageQueue);
}
bool MessageQueueTest::removeMessageQueue(MessageQueue& messageQueue) {
return blockchainMessageQueueList.remove(messageQueue);
}
void MessageQueueTest::sendBlockchainMessage(const BlockchainMessage& message) {
for (IntrusiveLinkedList>::iterator iter = blockchainMessageQueueList.begin(); iter != blockchainMessageQueueList.end(); ++iter) {
iter->push(message);
}
}
void MessageQueueTest::interruptBlockchainMessageWaiting() {
for (IntrusiveLinkedList>::iterator iter = blockchainMessageQueueList.begin(); iter != blockchainMessageQueueList.end(); ++iter) {
iter->stop();
}
}
void MessageQueueTest::SetUp() {
ASSERT_TRUE(blockchainMessageQueueList.empty());
}
void MessageQueueTest::TearDown() {
ASSERT_TRUE(blockchainMessageQueueList.empty());
}
TEST_F(MessageQueueTest, singleNewBlockMessage) {
MessageQueue queue(dispatcher);
MesageQueueGuard guard(*this, queue);
Crypto::Hash randomHash;
for (uint8_t& i : randomHash.data) {
i = rand();
}
contextGroup.spawn([&]() {
const BlockchainMessage& m = queue.front();
ASSERT_EQ(m.getType(), BlockchainMessage::MessageType::NEW_BLOCK_MESSAGE);
Crypto::Hash h;
ASSERT_TRUE(m.getNewBlockHash(h));
ASSERT_EQ(h, randomHash);
ASSERT_NO_THROW(queue.pop());
});
ASSERT_NO_THROW(sendBlockchainMessage(BlockchainMessage(std::move(NewBlockMessage(randomHash)))));
contextGroup.wait();
}
TEST_F(MessageQueueTest, singleNewAlternativeBlockMessage) {
MessageQueue queue(dispatcher);
MesageQueueGuard guard(*this, queue);
Crypto::Hash randomHash;
for (uint8_t& i : randomHash.data) {
i = rand();
}
contextGroup.spawn([&]() {
const BlockchainMessage& m = queue.front();
ASSERT_EQ(m.getType(), BlockchainMessage::MessageType::NEW_ALTERNATIVE_BLOCK_MESSAGE);
Crypto::Hash h;
ASSERT_TRUE(m.getNewAlternativeBlockHash(h));
ASSERT_EQ(h, randomHash);
ASSERT_NO_THROW(queue.pop());
});
ASSERT_NO_THROW(sendBlockchainMessage(BlockchainMessage(std::move(NewAlternativeBlockMessage(randomHash)))));
contextGroup.wait();
}
TEST_F(MessageQueueTest, singleChainSwitchMessage) {
MessageQueue queue(dispatcher);
MesageQueueGuard guard(*this, queue);
const size_t NUMBER_OF_BLOCKS = 10;
std::vector randomHashes;
for (size_t i = 0; i < NUMBER_OF_BLOCKS; ++i) {
Crypto::Hash randomHash;
for (uint8_t& j : randomHash.data) {
j = rand();
}
randomHashes.push_back(randomHash);
}
contextGroup.spawn([&]() {
const BlockchainMessage& m = queue.front();
ASSERT_EQ(m.getType(), BlockchainMessage::MessageType::CHAIN_SWITCH_MESSAGE);
std::vector res;
ASSERT_TRUE(m.getChainSwitch(res));
ASSERT_EQ(res, randomHashes);
ASSERT_NO_THROW(queue.pop());
});
std::vector copy = randomHashes;
ASSERT_NO_THROW(sendBlockchainMessage(BlockchainMessage(std::move(ChainSwitchMessage(std::move(copy))))));
contextGroup.wait();
}
TEST_F(MessageQueueTest, manyMessagesOneListener) {
MessageQueue queue(dispatcher);
MesageQueueGuard guard(*this, queue);
const size_t NUMBER_OF_BLOCKS = 10;
std::vector randomHashes;
for (size_t i = 0; i < NUMBER_OF_BLOCKS; ++i) {
Crypto::Hash randomHash;
for (uint8_t& j : randomHash.data) {
j = rand();
}
randomHashes.push_back(randomHash);
}
contextGroup.spawn([&]() {
for (size_t i = 0; i < NUMBER_OF_BLOCKS; ++i) {
const BlockchainMessage& m = queue.front();
ASSERT_EQ(m.getType(), BlockchainMessage::MessageType::NEW_BLOCK_MESSAGE);
Crypto::Hash h;
ASSERT_TRUE(m.getNewBlockHash(h));
ASSERT_EQ(h, randomHashes[i]);
ASSERT_NO_THROW(queue.pop());
}
});
for (auto h : randomHashes) {
ASSERT_NO_THROW(sendBlockchainMessage(BlockchainMessage(std::move(NewBlockMessage(h)))));
}
contextGroup.wait();
}
TEST_F(MessageQueueTest, manyMessagesManyListeners) {
const size_t NUMBER_OF_LISTENERS = 5;
std::array>, NUMBER_OF_LISTENERS> queues;
std::array>, NUMBER_OF_LISTENERS> quards;
for (size_t i = 0; i < NUMBER_OF_LISTENERS; ++i) {
queues[i] = std::move(std::unique_ptr>(new MessageQueue(dispatcher)));
quards[i] = std::move(std::unique_ptr>(new MesageQueueGuard(*this, *queues[i])));
}
const size_t NUMBER_OF_BLOCKS = 10;
std::vector randomHashes;
for (size_t i = 0; i < NUMBER_OF_BLOCKS; ++i) {
Crypto::Hash randomHash;
for (uint8_t& j : randomHash.data) {
j = rand();
}
randomHashes.push_back(randomHash);
}
contextGroup.spawn([&]() {
for (size_t i = 0; i < NUMBER_OF_LISTENERS; ++i) {
for (size_t j = 0; j < NUMBER_OF_BLOCKS; ++j) {
const BlockchainMessage& m = queues[i]->front();
ASSERT_EQ(m.getType(), BlockchainMessage::MessageType::NEW_BLOCK_MESSAGE);
Crypto::Hash h;
ASSERT_TRUE(m.getNewBlockHash(h));
ASSERT_EQ(h, randomHashes[j]);
ASSERT_NO_THROW(queues[i]->pop());
}
}
});
for (auto h : randomHashes) {
ASSERT_NO_THROW(sendBlockchainMessage(BlockchainMessage(std::move(NewBlockMessage(h)))));
}
contextGroup.wait();
}
TEST_F(MessageQueueTest, interruptWaiting) {
const size_t NUMBER_OF_LISTENERS = 5;
std::array>, NUMBER_OF_LISTENERS> queues;
std::array>, NUMBER_OF_LISTENERS> quards;
for (size_t i = 0; i < NUMBER_OF_LISTENERS; ++i) {
queues[i] = std::move(std::unique_ptr>(new MessageQueue(dispatcher)));
quards[i] = std::move(std::unique_ptr>(new MesageQueueGuard(*this, *queues[i])));
}
const size_t NUMBER_OF_BLOCKS = 10;
std::vector randomHashes;
for (size_t i = 0; i < NUMBER_OF_BLOCKS; ++i) {
Crypto::Hash randomHash;
for (uint8_t& j : randomHash.data) {
j = rand();
}
randomHashes.push_back(randomHash);
}
System::Event shutdownEvent(dispatcher);
contextGroup.spawn([&]() {
shutdownEvent.wait();
for (size_t i = 0; i < NUMBER_OF_LISTENERS; ++i) {
for (size_t j = 0; j < NUMBER_OF_BLOCKS; ++j) {
const BlockchainMessage& m = queues[i]->front();
ASSERT_EQ(m.getType(), BlockchainMessage::MessageType::NEW_BLOCK_MESSAGE);
Crypto::Hash h;
ASSERT_TRUE(m.getNewBlockHash(h));
ASSERT_EQ(h, randomHashes[j]);
ASSERT_NO_THROW(queues[i]->pop());
}
}
for (size_t i = 0; i < NUMBER_OF_LISTENERS; ++i) {
for (size_t j = 0; j < NUMBER_OF_BLOCKS; ++j) {
ASSERT_ANY_THROW(queues[i]->front());
ASSERT_ANY_THROW(queues[i]->pop());
}
}
});
for (auto h : randomHashes) {
ASSERT_NO_THROW(sendBlockchainMessage(BlockchainMessage(std::move(NewBlockMessage(h)))));
}
interruptBlockchainMessageWaiting();
shutdownEvent.set();
contextGroup.wait();
}
TEST_F(MessageQueueTest, doubleAddQueueToList) {
MessageQueue queue(dispatcher);
ASSERT_TRUE(blockchainMessageQueueList.insert(queue));
ASSERT_FALSE(blockchainMessageQueueList.insert(queue));
ASSERT_TRUE(blockchainMessageQueueList.remove(queue));
ASSERT_FALSE(blockchainMessageQueueList.remove(queue));
}