Skip to content

Commit 01044b5

Browse files
tstennercboulay
authored andcommitted
Unit tests: add more network related tests
1 parent 1a01ad5 commit 01044b5

File tree

1 file changed

+84
-12
lines changed

1 file changed

+84
-12
lines changed

testing/test_int_network.cpp

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "../src/cancellable_streambuf.h"
22
#include "catch.hpp"
33
#include <boost/asio/io_context.hpp>
4+
#include <boost/asio/ip/multicast.hpp>
45
#include <boost/asio/ip/tcp.hpp>
56
#include <boost/asio/ip/udp.hpp>
67
#include <boost/asio/ip/v6_only.hpp>
@@ -12,14 +13,18 @@
1213
#include <thread>
1314

1415
namespace asio = lslboost::asio;
15-
namespace ip = lslboost::asio::ip;
16-
using lslboost::system::error_code;
16+
using namespace asio;
17+
using err_t = const lslboost::system::error_code &;
1718
typedef lsl::cancellable_streambuf cancellable_streambuf;
1819

19-
const uint16_t port = 28812;
20+
static uint16_t port = 28812;
21+
static const char hello[] = "Hello World";
22+
static const std::string hellostr(hello);
2023

2124
static std::mutex output_mutex;
2225

26+
asio::const_buffer hellobuf() { return asio::const_buffer(hello, sizeof(hello)); }
27+
2328
#define MINFO(str) \
2429
{ \
2530
std::unique_lock<std::mutex> out_lock(output_mutex); \
@@ -50,7 +55,7 @@ template <typename T> void test_cancel_thread(T &&task, cancellable_streambuf &s
5055

5156
if (future.wait_for(std::chrono::milliseconds(200)) == std::future_status::ready)
5257
MINFO("Thread 1 finished too soon, couldn't test cancellation")
53-
MINFO("Thread 0: Closing socket…");
58+
MINFO("Thread 0: Closing socket…")
5459
sb.cancel();
5560
// Double cancel, shouldn't do anything dramatic
5661
sb.cancel();
@@ -68,7 +73,7 @@ TEST_CASE("streambufs can connect", "[streambuf][basic][network]") {
6873
asio::io_context io_ctx;
6974
cancellable_streambuf sb_connect;
7075
INFO("Thread 0: Binding remote socket and keeping it busy…")
71-
ip::tcp::endpoint ep(ip::address_v4::loopback(), port + 1);
76+
ip::tcp::endpoint ep(ip::address_v4::loopback(), port++);
7277
ip::tcp::acceptor remote(io_ctx);
7378
remote.open(ip::tcp::v4());
7479
remote.bind(ep);
@@ -98,7 +103,7 @@ TEST_CASE("streambufs can connect", "[streambuf][basic][network]") {
98103
TEST_CASE("streambufs can transfer data", "[streambuf][network]") {
99104
asio::io_context io_ctx;
100105
cancellable_streambuf sb_read;
101-
ip::tcp::endpoint ep(ip::address_v4::loopback(), port + 1);
106+
ip::tcp::endpoint ep(ip::address_v4::loopback(), port++);
102107
ip::tcp::acceptor remote(io_ctx, ep, true);
103108
remote.listen(1);
104109
INFO("Thread 0: Connecting…")
@@ -116,24 +121,91 @@ TEST_CASE("streambufs can transfer data", "[streambuf][network]") {
116121
}
117122

118123
TEST_CASE("receive v4 packets on v6 socket", "[ipv6][network]") {
119-
const uint16_t test_port = port + 2;
124+
const uint16_t test_port = port++;
120125
asio::io_context io_ctx;
121126
ip::udp::socket sock(io_ctx, ip::udp::v6());
122127
sock.set_option(ip::v6_only(false));
123128
sock.bind(ip::udp::endpoint(ip::address_v6::any(), test_port));
124129

125130
ip::udp::socket sender_v4(io_ctx, ip::udp::v4()), sender_v6(io_ctx, ip::udp::v6());
126-
const std::string buf = "Hello World";
127-
asio::const_buffer sbuf(buf.data(), buf.length());
131+
asio::const_buffer sbuf(hellobuf());
128132
char recvbuf[64] = {0};
129133
sender_v4.send_to(sbuf, ip::udp::endpoint(ip::address_v4::loopback(), test_port));
130134
auto recv_len = sock.receive(asio::buffer(recvbuf, sizeof(recvbuf) - 1));
131-
CHECK(recv_len == buf.length());
132-
CHECK(buf == recvbuf);
135+
CHECK(recv_len == sizeof(hello));
136+
CHECK(hellostr == recvbuf);
133137
std::fill_n(recvbuf, recv_len, 0);
134138

135139
sender_v6.send_to(sbuf, ip::udp::endpoint(ip::address_v6::loopback(), test_port));
136140
recv_len = sock.receive(asio::buffer(recvbuf, sizeof(recvbuf) - 1));
137-
CHECK(buf == recvbuf);
141+
CHECK(hellostr == recvbuf);
138142
std::fill_n(recvbuf, recv_len, 0);
139143
}
144+
145+
TEST_CASE("ipaddresses", "[ipv6][network][basic]") {
146+
ip::address_v4 v4addr(ip::make_address_v4("192.168.172.1")),
147+
mcastv4(ip::make_address_v4("239.0.0.183"));
148+
ip::address_v6 v6addr = ip::make_address_v6(ip::v4_mapped_t(), v4addr);
149+
ip::address addr(v4addr), addr_mapped(v6addr);
150+
CHECK(!v4addr.is_multicast());
151+
CHECK(mcastv4.is_multicast());
152+
// mapped IPv4 multicast addresses aren't considered IPv6 multicast addresses
153+
CHECK(!ip::make_address_v6(ip::v4_mapped, mcastv4).is_multicast());
154+
CHECK(v6addr.is_v4_mapped());
155+
CHECK(addr != addr_mapped);
156+
CHECK(addr == ip::address(ip::make_address_v4(ip::v4_mapped, v6addr)));
157+
158+
auto scoped = ip::make_address_v6("::1%3");
159+
CHECK(scoped.scope_id() == 3);
160+
}
161+
162+
/// Can multiple sockets bind to the same port and receive all broad-/multicast packets?
163+
TEST_CASE("reuseport", "[network][basic][!mayfail]") {
164+
const uint16_t test_port = port++;
165+
asio::io_context io_ctx(1);
166+
lslboost::system::error_code ec;
167+
// Linux: sudo ip link set lo multicast on; sudo ip mroute show table all
168+
for (auto addrstr : {"224.0.0.1", "255.255.255.255", "ff02::1"}) SECTION(addrstr) {
169+
std::vector<ip::udp::socket> socks;
170+
auto addr = ip::make_address(addrstr);
171+
if (!addr.is_multicast())
172+
REQUIRE((addr.is_v4() && addr.to_v4() == ip::address_v4::broadcast()));
173+
auto proto = addr.is_v4() ? ip::udp::v4() : ip::udp::v6();
174+
for (int i = 0; i < 2; ++i) {
175+
socks.emplace_back(io_ctx, proto);
176+
auto &sock = socks.back();
177+
sock.set_option(ip::udp::socket::reuse_address(true));
178+
if (addr.is_multicast()) sock.set_option(ip::multicast::join_group(addr), ec);
179+
if (ec == error::no_such_device || ec == std::errc::address_not_available)
180+
FAIL("No IPv6 route configured, skipping test!");
181+
sock.bind(ip::udp::endpoint(proto, test_port));
182+
}
183+
{
184+
ip::udp::socket outsock(io_ctx, proto);
185+
if (addr.is_multicast())
186+
outsock.set_option(ip::multicast::join_group(addr));
187+
else
188+
outsock.set_option(ip::udp::socket::broadcast(true));
189+
// outsock.set_option(ip::multicast::enable_loopback(true));
190+
auto sent = outsock.send_to(hellobuf(), ip::udp::endpoint(addr, test_port));
191+
REQUIRE(sent == sizeof(hello));
192+
outsock.close();
193+
}
194+
char inbuf[sizeof(hello)] = {0};
195+
std::size_t received = 0;
196+
197+
asio::steady_timer timeout(io_ctx, std::chrono::seconds(2));
198+
timeout.async_wait([](err_t err) {
199+
if (!err) throw std::runtime_error("Test didn't finish in time");
200+
});
201+
for (auto &insock : socks)
202+
insock.async_receive(
203+
asio::buffer(inbuf, sizeof(inbuf)), [&](err_t, std::size_t len) {
204+
CHECK(len == sizeof(hello));
205+
CHECK(hellostr == inbuf);
206+
received++;
207+
});
208+
while (received < socks.size()) io_ctx.run_one();
209+
timeout.cancel();
210+
}
211+
}

0 commit comments

Comments
 (0)