Skip to content

Commit

Permalink
Improved code of message bus, and added testcases for it
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexAUT committed Dec 21, 2018
1 parent d2ef510 commit 351d1b7
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 25 deletions.
70 changes: 45 additions & 25 deletions include/aw/utils/messageBus/messageBus.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,53 +92,73 @@ class MessageBus
public:
using TypeIDs = TypeCounter<struct Anonym>;

public:
MessageBus() = default;
MessageBus(const MessageBus&) = delete;
MessageBus operator=(const MessageBus&) = delete;

template <typename EventType>
typename Channel<EventType>::SubscriptionType subscribeToChannel(typename Channel<EventType>::Callback callback)
{
auto channelId = TypeIDs::getId<EventType>();
if (mChannels.size() <= channelId)
mChannels.resize(channelId + 1);

using EventChannel = Channel<EventType>;
if (!mChannels[channelId])
mChannels[channelId] = std::make_unique<EventChannel>();

return static_cast<EventChannel&>(*mChannels[channelId]).subscribeSafely(std::move(callback));
auto& channel = getAndCreateChannel<EventType>();
return channel.subscribeSafely(std::move(callback));
}

template <typename EventType>
int subscribeToChannelUnsafe(typename Channel<EventType>::Callback callback)
{
auto channelId = TypeIDs::getId<EventType>();
if (mChannels.size() <= channelId)
mChannels.resize(channelId + 1);

using EventChannel = Channel<EventType>;
if (!mChannels[channelId])
mChannels[channelId] = std::make_unique<EventChannel>();

return static_cast<EventChannel&>(*mChannels[channelId]).subscribe(std::move(callback));
auto& channel = getAndCreateChannel<EventType>();
return channel.subscribe(std::move(callback));
}

template <typename EventType>
void unsubscribeFromChannel(int id)
{
using EventChannel = Channel<EventType>;
auto channelId = TypeIDs::getId<EventType>();
if (mChannels.size() > channelId || mChannels[channelId])
static_cast<EventChannel&>(*mChannels[channelId]).unsubscribe(id);
auto* channel = getChannel<EventType>();
if (channel)
channel->unsubscribe(id);
}

template <typename EventType>
void broadcast(const EventType& eventType) const
{
auto* channel = getChannel<EventType>();
if (channel)
channel->broadcast(eventType);
}

private:
template <typename EventType>
Channel<EventType>& getAndCreateChannel()
{
using EventChannel = Channel<EventType>;
auto* channel = getChannel<EventType>();

if (!channel)
{
auto channelId = TypeIDs::getId<EventType>();
while (mChannels.size() <= channelId)
mChannels.emplace_back(nullptr);
if (!mChannels[channelId])
mChannels[channelId] = std::make_unique<EventChannel>();

channel = static_cast<EventChannel*>(mChannels[channelId].get());
}

assert(channel);
return *channel;
}

template <typename EventType>
Channel<EventType>* getChannel() const
{
auto channelId = TypeIDs::getId<EventType>();
if (mChannels.size() > channelId && mChannels[channelId])
static_cast<const EventChannel&>(*mChannels[channelId]).broadcast(eventType);
if (channelId >= mChannels.size())
return nullptr;
using EventChannel = Channel<EventType>;
return mChannels[channelId] ? static_cast<EventChannel*>(mChannels[channelId].get()) : nullptr;
}

private:
private:
std::vector<std::unique_ptr<ChannelWrapper>> mChannels;
};
Expand Down
1 change: 1 addition & 0 deletions tests/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
set(HEADER_FILES_TESTS ${HEADER_FILES_TESTS} PARENT_SCOPE)

set(SOURCE_FILES_TESTS ${SOURCE_FILES_TESTS}
${CMAKE_CURRENT_SOURCE_DIR}/testMessageBus.cpp
${CMAKE_CURRENT_SOURCE_DIR}/testStringFormat.cpp
PARENT_SCOPE)

85 changes: 85 additions & 0 deletions tests/utils/testMessageBus.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include <catch2/catch.hpp>

#include <aw/utils/messageBus/messageBus.hpp>

struct MessageA
{
std::string msg;
};

struct MessageB
{
std::string msg;
};

TEST_CASE("MessageBus")
{
aw::MessageBus bus;
bus.broadcast(MessageA{"Hallo"});

SECTION("One receiver")
{
INFO("Check if subscription safely works");
MessageA message{"Message1"};
MessageA received;
{
auto sub = bus.subscribeToChannel<MessageA>([&](const auto& msg) { received = msg; });
bus.broadcast(message);
CHECK(received.msg == message.msg);
}
received.msg = "";
REQUIRE(received.msg != message.msg);
bus.broadcast(message);
INFO("Check if unsubscribe works on destructor");
CHECK(received.msg != message.msg);
}

SECTION("Multiple message types")
{
MessageA messageA{"A"};
MessageB messageB{"B"};
MessageA recA;
MessageB recB;
{
auto subA = bus.subscribeToChannel<MessageA>([&](const auto& msg) { recA = msg; });
{
auto subB = bus.subscribeToChannel<MessageB>([&](const auto& msg) { recB = msg; });
bus.broadcast(messageA);
bus.broadcast(messageB);
CHECK(recA.msg == messageA.msg);
CHECK(recB.msg == messageB.msg);
}
recA.msg = "";
recB.msg = "";
bus.broadcast(messageA);
bus.broadcast(messageB);
CHECK(recA.msg == messageA.msg);
CHECK(recB.msg != messageB.msg);
}
recA.msg = "";
recB.msg = "";
bus.broadcast(messageA);
bus.broadcast(messageB);
CHECK(recA.msg != messageA.msg);
CHECK(recB.msg != messageB.msg);
}

SECTION("Test out of order messages")
{
MessageA messageA{"A"};
MessageB messageB{"B"};
MessageA recA;
MessageB recB;
aw::MessageBus bus2;
auto subB = bus2.subscribeToChannel<MessageB>([&](const auto& msg) { recB = msg; });
bus2.broadcast(messageB);
CHECK(recB.msg == messageB.msg);

bus2.broadcast(messageA);
auto subA = bus2.subscribeToChannel<MessageA>([&](const auto& msg) { recA = msg; });
bus2.broadcast(messageA);

CHECK(recA.msg == messageA.msg);
}
}

0 comments on commit 351d1b7

Please sign in to comment.