mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-07 14:18:18 +02:00
Merge bitcoin/bitcoin#33506: test: sock: Enable all socket tests on Windows
9316d96240test: sock: Enable socket pair tests on Windows (David Gumberg) Pull request description: Some `class Sock` tests were previously disabled because Windows lacks [`socketpair(2)`](https://man7.org/linux/man-pages/man2/socketpair.2.html) which is used as a helper function in the socket tests, but is not strictly necessary or related to the `Socket` class under testing. This PR adds a `CreateSocketPair()` helper which creates a sender socket and receiver socket with a TCP connection, enabling these test cases for Windows. This also enables future tests that require more granular control over sockets than what `socketpair()` allows for, like using `setsockopt()` before connecting a socket. This change is generally an improvement, but is also broken out of a [branch](github.com/davidgumberg/bitcoin/tree/2025-09-02-0xB10C-prefill-rebase) that does compact block prefilling up to the available bytes in the connection's current TCP window (see [delving post](https://delvingbitcoin.org/t/stats-on-compact-block-reconstructions/1052/34)). Creating connected socket pairs is useful for added tests in that branch that validate querying the current TCP window state, and without this change those tests don't run on Windows. ACKs for top commit: achow101: ACK9316d96240sedited: ACK9316d96240w0xlt: reACK9316d96240Tree-SHA512: 23ad7070555bb934b0eb18d114e918bd2d50657d20d2221d8c98cd0e558e27e32e5ef89e8074c108a90d7309e539318d23cea43d2ad8eb10e635b114f5f1a87d
This commit is contained in:
@@ -82,61 +82,75 @@ BOOST_AUTO_TEST_CASE(move_assignment)
|
||||
BOOST_CHECK(SocketIsClosed(s2));
|
||||
}
|
||||
|
||||
#ifndef WIN32 // Windows does not have socketpair(2).
|
||||
struct TcpSocketPair {
|
||||
Sock sender;
|
||||
Sock receiver;
|
||||
|
||||
static void CreateSocketPair(int s[2])
|
||||
{
|
||||
BOOST_REQUIRE_EQUAL(socketpair(AF_UNIX, SOCK_STREAM, 0, s), 0);
|
||||
}
|
||||
TcpSocketPair()
|
||||
: sender{Sock{CreateSocket()}},
|
||||
receiver{Sock{CreateSocket()}}
|
||||
{
|
||||
connect_pair();
|
||||
}
|
||||
|
||||
static void SendAndRecvMessage(const Sock& sender, const Sock& receiver)
|
||||
{
|
||||
const char* msg = "abcd";
|
||||
constexpr ssize_t msg_len = 4;
|
||||
char recv_buf[10];
|
||||
TcpSocketPair(const TcpSocketPair&) = delete;
|
||||
TcpSocketPair& operator= (const TcpSocketPair&) = delete;
|
||||
TcpSocketPair(TcpSocketPair&&) = default;
|
||||
TcpSocketPair& operator= (TcpSocketPair&&) = default;
|
||||
|
||||
BOOST_CHECK_EQUAL(sender.Send(msg, msg_len, 0), msg_len);
|
||||
BOOST_CHECK_EQUAL(receiver.Recv(recv_buf, sizeof(recv_buf), 0), msg_len);
|
||||
BOOST_CHECK_EQUAL(strncmp(msg, recv_buf, msg_len), 0);
|
||||
}
|
||||
void connect_pair()
|
||||
{
|
||||
sockaddr_in addr{};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
addr.sin_port = 0;
|
||||
|
||||
BOOST_REQUIRE_EQUAL(receiver.Bind(reinterpret_cast<sockaddr*>(&addr), sizeof(addr)), 0);
|
||||
BOOST_REQUIRE_EQUAL(receiver.Listen(1), 0);
|
||||
|
||||
// Get the address of the listener.
|
||||
sockaddr_in bound{};
|
||||
socklen_t blen = sizeof(bound);
|
||||
BOOST_REQUIRE_EQUAL(receiver.GetSockName(reinterpret_cast<sockaddr*>(&bound), &blen), 0);
|
||||
BOOST_REQUIRE_EQUAL(blen, sizeof(bound));
|
||||
|
||||
BOOST_REQUIRE_EQUAL(sender.Connect(reinterpret_cast<sockaddr*>(&bound), sizeof(bound)), 0);
|
||||
|
||||
std::unique_ptr<Sock> accepted = receiver.Accept(nullptr, nullptr);
|
||||
BOOST_REQUIRE(accepted != nullptr);
|
||||
|
||||
receiver = std::move(*accepted);
|
||||
}
|
||||
|
||||
void send_and_receive()
|
||||
{
|
||||
const char* msg = "abcd";
|
||||
constexpr ssize_t msg_len = 4;
|
||||
char recv_buf[10];
|
||||
|
||||
BOOST_CHECK_EQUAL(sender.Send(msg, msg_len, 0), msg_len);
|
||||
BOOST_CHECK_EQUAL(receiver.Recv(recv_buf, sizeof(recv_buf), 0), msg_len);
|
||||
BOOST_CHECK_EQUAL(strncmp(msg, recv_buf, msg_len), 0);
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE(send_and_receive)
|
||||
{
|
||||
int s[2];
|
||||
CreateSocketPair(s);
|
||||
TcpSocketPair socks{};
|
||||
socks.send_and_receive();
|
||||
|
||||
Sock* sock0 = new Sock(s[0]);
|
||||
Sock* sock1 = new Sock(s[1]);
|
||||
|
||||
SendAndRecvMessage(*sock0, *sock1);
|
||||
|
||||
Sock* sock0moved = new Sock(std::move(*sock0));
|
||||
Sock* sock1moved = new Sock(INVALID_SOCKET);
|
||||
*sock1moved = std::move(*sock1);
|
||||
|
||||
delete sock0;
|
||||
delete sock1;
|
||||
|
||||
SendAndRecvMessage(*sock1moved, *sock0moved);
|
||||
|
||||
delete sock0moved;
|
||||
delete sock1moved;
|
||||
|
||||
BOOST_CHECK(SocketIsClosed(s[0]));
|
||||
BOOST_CHECK(SocketIsClosed(s[1]));
|
||||
// Sockets are still connected after being moved.
|
||||
TcpSocketPair socks_moved = std::move(socks);
|
||||
socks_moved.send_and_receive();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(wait)
|
||||
{
|
||||
int s[2];
|
||||
CreateSocketPair(s);
|
||||
TcpSocketPair socks = TcpSocketPair{};
|
||||
|
||||
Sock sock0(s[0]);
|
||||
Sock sock1(s[1]);
|
||||
std::thread waiter([&socks]() { (void)socks.receiver.Wait(24h, Sock::RECV); });
|
||||
|
||||
std::thread waiter([&sock0]() { (void)sock0.Wait(24h, Sock::RECV); });
|
||||
|
||||
BOOST_REQUIRE_EQUAL(sock1.Send("a", 1, 0), 1);
|
||||
BOOST_REQUIRE_EQUAL(socks.sender.Send("a", 1, 0), 1);
|
||||
|
||||
waiter.join();
|
||||
}
|
||||
@@ -145,31 +159,26 @@ BOOST_AUTO_TEST_CASE(recv_until_terminator_limit)
|
||||
{
|
||||
constexpr auto timeout = 1min; // High enough so that it is never hit.
|
||||
CThreadInterrupt interrupt;
|
||||
int s[2];
|
||||
CreateSocketPair(s);
|
||||
|
||||
Sock sock_send(s[0]);
|
||||
Sock sock_recv(s[1]);
|
||||
TcpSocketPair socks = TcpSocketPair{};
|
||||
|
||||
std::thread receiver([&sock_recv, &timeout, &interrupt]() {
|
||||
std::thread receiver([&socks, &timeout, &interrupt]() {
|
||||
constexpr size_t max_data{10};
|
||||
bool threw_as_expected{false};
|
||||
// BOOST_CHECK_EXCEPTION() writes to some variables shared with the main thread which
|
||||
// creates a data race. So mimic it manually.
|
||||
try {
|
||||
(void)sock_recv.RecvUntilTerminator('\n', timeout, interrupt, max_data);
|
||||
(void)socks.receiver.RecvUntilTerminator('\n', timeout, interrupt, max_data);
|
||||
} catch (const std::runtime_error& e) {
|
||||
threw_as_expected = HasReason("too many bytes without a terminator")(e);
|
||||
}
|
||||
assert(threw_as_expected);
|
||||
});
|
||||
|
||||
BOOST_REQUIRE_NO_THROW(sock_send.SendComplete("1234567", timeout, interrupt));
|
||||
BOOST_REQUIRE_NO_THROW(sock_send.SendComplete("89a\n", timeout, interrupt));
|
||||
BOOST_REQUIRE_NO_THROW(socks.sender.SendComplete("1234567", timeout, interrupt));
|
||||
BOOST_REQUIRE_NO_THROW(socks.sender.SendComplete("89a\n", timeout, interrupt));
|
||||
|
||||
receiver.join();
|
||||
}
|
||||
|
||||
#endif /* WIN32 */
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
Reference in New Issue
Block a user