// 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 #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() override; void TearDown() override; 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(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(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(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(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::unique_ptr>(new MessageQueue(dispatcher)); quards[i] = 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(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::unique_ptr>(new MessageQueue(dispatcher)); quards[i] = 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(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)); }