Skip to content

Commit 52c3381

Browse files
committed
Merge bitcoin/bitcoin#33506: test: sock: Enable all socket tests on Windows
9316d96 test: 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: ACK 9316d96 sedited: ACK 9316d96 w0xlt: reACK 9316d96 Tree-SHA512: 23ad7070555bb934b0eb18d114e918bd2d50657d20d2221d8c98cd0e558e27e32e5ef89e8074c108a90d7309e539318d23cea43d2ad8eb10e635b114f5f1a87d
2 parents 2460938 + 9316d96 commit 52c3381

1 file changed

Lines changed: 64 additions & 55 deletions

File tree

src/test/sock_tests.cpp

Lines changed: 64 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -82,61 +82,75 @@ BOOST_AUTO_TEST_CASE(move_assignment)
8282
BOOST_CHECK(SocketIsClosed(s2));
8383
}
8484

85-
#ifndef WIN32 // Windows does not have socketpair(2).
86-
87-
static void CreateSocketPair(int s[2])
88-
{
89-
BOOST_REQUIRE_EQUAL(socketpair(AF_UNIX, SOCK_STREAM, 0, s), 0);
90-
}
91-
92-
static void SendAndRecvMessage(const Sock& sender, const Sock& receiver)
93-
{
94-
const char* msg = "abcd";
95-
constexpr ssize_t msg_len = 4;
96-
char recv_buf[10];
97-
98-
BOOST_CHECK_EQUAL(sender.Send(msg, msg_len, 0), msg_len);
99-
BOOST_CHECK_EQUAL(receiver.Recv(recv_buf, sizeof(recv_buf), 0), msg_len);
100-
BOOST_CHECK_EQUAL(strncmp(msg, recv_buf, msg_len), 0);
101-
}
85+
struct TcpSocketPair {
86+
Sock sender;
87+
Sock receiver;
88+
89+
TcpSocketPair()
90+
: sender{Sock{CreateSocket()}},
91+
receiver{Sock{CreateSocket()}}
92+
{
93+
connect_pair();
94+
}
95+
96+
TcpSocketPair(const TcpSocketPair&) = delete;
97+
TcpSocketPair& operator= (const TcpSocketPair&) = delete;
98+
TcpSocketPair(TcpSocketPair&&) = default;
99+
TcpSocketPair& operator= (TcpSocketPair&&) = default;
100+
101+
void connect_pair()
102+
{
103+
sockaddr_in addr{};
104+
addr.sin_family = AF_INET;
105+
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
106+
addr.sin_port = 0;
107+
108+
BOOST_REQUIRE_EQUAL(receiver.Bind(reinterpret_cast<sockaddr*>(&addr), sizeof(addr)), 0);
109+
BOOST_REQUIRE_EQUAL(receiver.Listen(1), 0);
110+
111+
// Get the address of the listener.
112+
sockaddr_in bound{};
113+
socklen_t blen = sizeof(bound);
114+
BOOST_REQUIRE_EQUAL(receiver.GetSockName(reinterpret_cast<sockaddr*>(&bound), &blen), 0);
115+
BOOST_REQUIRE_EQUAL(blen, sizeof(bound));
116+
117+
BOOST_REQUIRE_EQUAL(sender.Connect(reinterpret_cast<sockaddr*>(&bound), sizeof(bound)), 0);
118+
119+
std::unique_ptr<Sock> accepted = receiver.Accept(nullptr, nullptr);
120+
BOOST_REQUIRE(accepted != nullptr);
121+
122+
receiver = std::move(*accepted);
123+
}
124+
125+
void send_and_receive()
126+
{
127+
const char* msg = "abcd";
128+
constexpr ssize_t msg_len = 4;
129+
char recv_buf[10];
130+
131+
BOOST_CHECK_EQUAL(sender.Send(msg, msg_len, 0), msg_len);
132+
BOOST_CHECK_EQUAL(receiver.Recv(recv_buf, sizeof(recv_buf), 0), msg_len);
133+
BOOST_CHECK_EQUAL(strncmp(msg, recv_buf, msg_len), 0);
134+
}
135+
};
102136

103137
BOOST_AUTO_TEST_CASE(send_and_receive)
104138
{
105-
int s[2];
106-
CreateSocketPair(s);
107-
108-
Sock* sock0 = new Sock(s[0]);
109-
Sock* sock1 = new Sock(s[1]);
139+
TcpSocketPair socks{};
140+
socks.send_and_receive();
110141

111-
SendAndRecvMessage(*sock0, *sock1);
112-
113-
Sock* sock0moved = new Sock(std::move(*sock0));
114-
Sock* sock1moved = new Sock(INVALID_SOCKET);
115-
*sock1moved = std::move(*sock1);
116-
117-
delete sock0;
118-
delete sock1;
119-
120-
SendAndRecvMessage(*sock1moved, *sock0moved);
121-
122-
delete sock0moved;
123-
delete sock1moved;
124-
125-
BOOST_CHECK(SocketIsClosed(s[0]));
126-
BOOST_CHECK(SocketIsClosed(s[1]));
142+
// Sockets are still connected after being moved.
143+
TcpSocketPair socks_moved = std::move(socks);
144+
socks_moved.send_and_receive();
127145
}
128146

129147
BOOST_AUTO_TEST_CASE(wait)
130148
{
131-
int s[2];
132-
CreateSocketPair(s);
149+
TcpSocketPair socks = TcpSocketPair{};
133150

134-
Sock sock0(s[0]);
135-
Sock sock1(s[1]);
151+
std::thread waiter([&socks]() { (void)socks.receiver.Wait(24h, Sock::RECV); });
136152

137-
std::thread waiter([&sock0]() { (void)sock0.Wait(24h, Sock::RECV); });
138-
139-
BOOST_REQUIRE_EQUAL(sock1.Send("a", 1, 0), 1);
153+
BOOST_REQUIRE_EQUAL(socks.sender.Send("a", 1, 0), 1);
140154

141155
waiter.join();
142156
}
@@ -145,31 +159,26 @@ BOOST_AUTO_TEST_CASE(recv_until_terminator_limit)
145159
{
146160
constexpr auto timeout = 1min; // High enough so that it is never hit.
147161
CThreadInterrupt interrupt;
148-
int s[2];
149-
CreateSocketPair(s);
150162

151-
Sock sock_send(s[0]);
152-
Sock sock_recv(s[1]);
163+
TcpSocketPair socks = TcpSocketPair{};
153164

154-
std::thread receiver([&sock_recv, &timeout, &interrupt]() {
165+
std::thread receiver([&socks, &timeout, &interrupt]() {
155166
constexpr size_t max_data{10};
156167
bool threw_as_expected{false};
157168
// BOOST_CHECK_EXCEPTION() writes to some variables shared with the main thread which
158169
// creates a data race. So mimic it manually.
159170
try {
160-
(void)sock_recv.RecvUntilTerminator('\n', timeout, interrupt, max_data);
171+
(void)socks.receiver.RecvUntilTerminator('\n', timeout, interrupt, max_data);
161172
} catch (const std::runtime_error& e) {
162173
threw_as_expected = HasReason("too many bytes without a terminator")(e);
163174
}
164175
assert(threw_as_expected);
165176
});
166177

167-
BOOST_REQUIRE_NO_THROW(sock_send.SendComplete("1234567", timeout, interrupt));
168-
BOOST_REQUIRE_NO_THROW(sock_send.SendComplete("89a\n", timeout, interrupt));
178+
BOOST_REQUIRE_NO_THROW(socks.sender.SendComplete("1234567", timeout, interrupt));
179+
BOOST_REQUIRE_NO_THROW(socks.sender.SendComplete("89a\n", timeout, interrupt));
169180

170181
receiver.join();
171182
}
172183

173-
#endif /* WIN32 */
174-
175184
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)