#include <ableton/discovery/UdpMessenger.hpp>
#include <ableton/discovery/test/Interface.hpp>
#include <ableton/discovery/test/PayloadEntries.hpp>
#include <ableton/test/CatchWrapper.hpp>
#include <ableton/test/serial_io/Fixture.hpp>
#include <array>
namespace ableton
{
namespace discovery
{
namespace
{
struct TestNodeState
{
using IdType = uint8_t;
IdType ident() const { return nodeId; }
friend auto toPayload(const TestNodeState& state) -> decltype(makePayload(test::Foo{}))
{
return makePayload(test::Foo{state.fooVal});
}
template <typename It>
static TestNodeState fromPayload(const uint8_t id, It begin, It end)
{
TestNodeState state = {id, 0};
parsePayload<test::Foo>(
begin, end, [&state](const test::Foo& foo) { state.fooVal = foo.fooVal; });
return state;
}
IdType nodeId;
int32_t fooVal;
};
struct TestHandler
{
void operator()(PeerState<TestNodeState> state) { peerStates.push_back(state); }
void operator()(ByeBye<TestNodeState::IdType> byeBye) { byeByes.push_back(byeBye); }
std::vector<PeerState<TestNodeState>> peerStates;
std::vector<ByeBye<TestNodeState::IdType>> byeByes;
};
template <typename Messenger>
struct MessengerWrapper
{
MessengerWrapper(Messenger messenger)
: mMessenger(std::move(messenger))
{
}
Messenger mMessenger;
};
template <typename Messenger>
MessengerWrapper<Messenger> wrapMessenger(Messenger messenger)
{
return {std::move(messenger)};
}
}
TEST_CASE("UdpMessenger")
{
const TestNodeState state1 = {5, 15};
const auto state2 = TestNodeState{3, 10};
const auto peerEndpoint = UdpEndpoint{makeAddress("123.123.234.234"), 1900};
::ableton::test::serial_io::Fixture io;
auto iface = test::Interface(UdpEndpoint{makeAddress("123.123.234.42"), 1234});
SECTION("BroadcastsStateOnConstruction")
{
auto messenger = makeUdpMessenger(
util::injectRef(iface), state2, util::injectVal(io.makeIoContext()), 1, 1);
REQUIRE(1 == iface.sentMessages.size());
const auto messageBuffer = iface.sentMessages[0].first;
const auto sentTo = iface.sentMessages[0].second;
const auto result = v1::parseMessageHeader<TestNodeState::IdType>(
begin(messageBuffer), end(messageBuffer));
CHECK(v1::kAlive == result.first.messageType);
CHECK(state2.nodeId == result.first.ident);
CHECK(1 == result.first.ttl);
CHECK(multicastEndpointV4() == sentTo);
const auto actualState =
TestNodeState::fromPayload(state2.nodeId, result.second, end(messageBuffer));
CHECK(state2.fooVal == actualState.fooVal);
}
SECTION("Heartbeat")
{
auto messenger = makeUdpMessenger(
util::injectRef(iface), state2, util::injectVal(io.makeIoContext()), 4, 2);
REQUIRE(1 == iface.sentMessages.size());
io.advanceTime(std::chrono::seconds(3));
CHECK(2 == iface.sentMessages.size());
}
SECTION("Response")
{
auto messenger = makeUdpMessenger(
util::injectRef(iface), state2, util::injectVal(io.makeIoContext()), 1, 1);
v1::MessageBuffer buffer;
const auto messageEnd =
v1::aliveMessage(state1.ident(), 0, makePayload(), begin(buffer));
iface.incomingMessage(peerEndpoint, begin(buffer), messageEnd);
REQUIRE(2 == iface.sentMessages.size());
const auto messageBuffer = iface.sentMessages[1].first;
const auto sentTo = iface.sentMessages[1].second;
const auto result = v1::parseMessageHeader<TestNodeState::IdType>(
begin(messageBuffer), end(messageBuffer));
CHECK(v1::kResponse == result.first.messageType);
CHECK(state2.nodeId == result.first.ident);
CHECK(1 == result.first.ttl);
CHECK(peerEndpoint == sentTo);
}
SECTION("Receive")
{
auto tmpMessenger = makeUdpMessenger(
util::injectRef(iface), TestNodeState{}, util::injectVal(io.makeIoContext()), 1, 1);
auto messenger = std::move(tmpMessenger);
auto handler = TestHandler{};
messenger.receive(std::ref(handler));
v1::MessageBuffer buffer;
auto end = v1::aliveMessage(state1.nodeId, 3, toPayload(state1), begin(buffer));
iface.incomingMessage(peerEndpoint, begin(buffer), end);
end = v1::byeByeMessage(state1.nodeId, begin(buffer));
messenger.receive(std::ref(handler));
iface.incomingMessage(peerEndpoint, begin(buffer), end);
REQUIRE(1 == handler.peerStates.size());
CHECK(3 == handler.peerStates[0].ttl);
CHECK(state1.nodeId == handler.peerStates[0].peerState.nodeId);
CHECK(state1.fooVal == handler.peerStates[0].peerState.fooVal);
REQUIRE(1 == handler.byeByes.size());
CHECK(state1.nodeId == handler.byeByes[0].peerId);
}
SECTION("SendByeByeOnDestruction")
{
{
auto messenger = makeUdpMessenger(util::injectRef(iface),
TestNodeState{5, 10},
util::injectVal(io.makeIoContext()),
1,
1);
}
REQUIRE(2 == iface.sentMessages.size());
const auto messageBuffer = iface.sentMessages[1].first;
const auto sentTo = iface.sentMessages[1].second;
const auto result = v1::parseMessageHeader<TestNodeState::IdType>(
begin(messageBuffer), end(messageBuffer));
CHECK(v1::kByeBye == result.first.messageType);
CHECK(multicastEndpointV4() == sentTo);
}
SECTION("MovingMessengerDoesntSendByeBye")
{
{
auto messenger = makeUdpMessenger(util::injectRef(iface),
TestNodeState{5, 10},
util::injectVal(io.makeIoContext()),
1,
1);
auto wrapper = wrapMessenger(std::move(messenger));
}
CHECK(2 == iface.sentMessages.size());
}
}
} }