/** * @file tests/unit/platform/test_common.cpp * @brief Test src/platform/common.*. */ // test includes #include "../../tests_common.h" // standard includes #include #include #include #include #include // lib includes #include #include #include #include // local includes #include TEST(HostnameTests, TestAsioEquality) { // These should be equivalent on all platforms for ASCII hostnames ASSERT_EQ(platf::get_host_name(), boost::asio::ip::host_name()); } TEST(SocketAddressTest, FormatsConstIpv4AndIpv6AddressesAndPreservesNetworkOrderPorts) { for (const auto *text : {"192.0.2.17", "2001:db8::17"}) { const boost::asio::ip::udp::endpoint endpoint {boost::asio::ip::make_address(text), 4321}; EXPECT_EQ(platf::from_sockaddr(endpoint.data()), text); const auto [port, address] = platf::from_sockaddr_ex(endpoint.data()); EXPECT_EQ(port, htons(4321)); EXPECT_EQ(address, text); } } TEST(SocketAddressTest, UnsupportedAddressFamiliesProduceEmptyAddressesAndZeroPorts) { const sockaddr address {}; EXPECT_TRUE(platf::from_sockaddr(&address).empty()); EXPECT_EQ(platf::from_sockaddr_ex(&address), (std::pair {0, {}})); } /** * @brief Const batch descriptors locate payload boundaries and reject offsets past all buffers. */ TEST(BatchedSendInfoTest, ConstLookupPreservesBuffersAndFindsEveryBoundary) { const std::string_view first = "abc"; const std::string_view second = "defg"; std::vector buffers {{nullptr, 0}, {first.data(), first.size()}, {second.data(), second.size()}}; auto address = boost::asio::ip::address_v4::loopback(); boost::asio::ip::address endpoint = address; const platf::batched_send_info_t batch {nullptr, 0, buffers, 1, 0, 7, 0, endpoint, 0, endpoint}; EXPECT_EQ(batch.buffer_for_payload_offset(0).buffer, first.data()); EXPECT_EQ(batch.buffer_for_payload_offset(0).size, first.size()); EXPECT_EQ(batch.buffer_for_payload_offset(2).buffer, first.data() + 2); EXPECT_EQ(batch.buffer_for_payload_offset(2).size, 1); EXPECT_EQ(batch.buffer_for_payload_offset(3).buffer, second.data()); EXPECT_EQ(batch.buffer_for_payload_offset(3).size, second.size()); EXPECT_EQ(batch.buffer_for_payload_offset(6).buffer, second.data() + 3); EXPECT_EQ(batch.buffer_for_payload_offset(6).size, 1); for (const auto offset : {7, 8}) { EXPECT_EQ(batch.buffer_for_payload_offset(offset).buffer, nullptr); EXPECT_EQ(batch.buffer_for_payload_offset(offset).size, 0); } EXPECT_EQ(buffers.size(), 3); EXPECT_EQ(buffers[1].buffer, first.data()); EXPECT_EQ(buffers[1].size, first.size()); } #if defined(_WIN32) || defined(__linux__) || defined(__FreeBSD__) /** * @brief Exercise source-address control messages on bound UDP sockets with optional headers. */ class UdpSourceAddressTest: public testing::TestWithParam> {}; TEST_P(UdpSourceAddressTest, SendsTheExpectedDatagramFromTheRequestedAddress) { const auto [ipv6, batched, with_header] = GetParam(); auto address = boost::asio::ip::make_address(ipv6 ? "::1" : "127.0.0.1"); boost::asio::io_context context; boost::asio::ip::udp::socket receiver(context, {address, 0}); // FreeBSD requires a bound port before accepting IP_SENDSRCADDR. boost::asio::ip::udp::socket sender(context, {receiver.local_endpoint().protocol(), 0}); const auto port = receiver.local_endpoint().port(); const auto native_socket = static_cast(sender.native_handle()); const std::string_view header = "header"; const std::string_view payload = "payload"; if (batched) { const std::string_view headers = "headerheader"; const std::string_view payloads = "payloadpayload"; std::vector buffers {{payloads.data(), payloads.size()}}; const platf::batched_send_info_t batch_info { with_header ? headers.data() : nullptr, with_header ? header.size() : 0, buffers, payload.size(), 0, 2, native_socket, address, port, address }; ASSERT_TRUE(platf::send_batch(batch_info)); } else { platf::send_info_t packet_info { with_header ? header.data() : nullptr, with_header ? header.size() : 0, payload.data(), payload.size(), native_socket, address, port, address }; ASSERT_TRUE(platf::send(packet_info)); } for (auto packet = 0; packet < (batched ? 2 : 1); ++packet) { std::array bytes; boost::asio::ip::udp::endpoint peer; boost::system::error_code receive_error; std::size_t received = 0; bool completed = false; receiver.async_receive_from(boost::asio::buffer(bytes), peer, [&receive_error, &received, &completed](const auto &result, std::size_t size) { receive_error = result; received = size; completed = true; }); context.restart(); context.run_for(std::chrono::seconds {2}); ASSERT_TRUE(completed); ASSERT_FALSE(receive_error) << receive_error.message(); EXPECT_EQ(peer.address(), address); EXPECT_EQ(std::string_view(bytes.data(), received), with_header ? "headerpayload" : "payload"); } } INSTANTIATE_TEST_SUITE_P( Loopback, UdpSourceAddressTest, testing::Combine(testing::Bool(), testing::Bool(), testing::Bool()) ); #endif