aboutsummaryrefslogtreecommitdiff
path: root/src/test/sock_tests.cpp
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@protonmail.com>2021-02-11 13:21:31 +0100
committerWladimir J. van der Laan <laanwj@protonmail.com>2021-02-11 14:07:33 +0100
commita1be08405d9bbf771a965dc170ce85ea1ad034c7 (patch)
tree3e5cf94f5d6a393f972ae66cffaeaed066154fe8 /src/test/sock_tests.cpp
parent685c16fcb2c718154bf3e6114e35fcac351503f2 (diff)
parent615ba0eb96cf131364c1ceca9d3dedf006fa1e1c (diff)
Merge #20788: net: add RAII socket and use it instead of bare SOCKET
615ba0eb96cf131364c1ceca9d3dedf006fa1e1c test: add Sock unit tests (Vasil Dimov) 7bd21ce1efc363b3e8ea1d51dd1410ccd66820cb style: rename hSocket to sock (Vasil Dimov) 04ae8469049e1f14585aabfb618ae522150240a7 net: use Sock in InterruptibleRecv() and Socks5() (Vasil Dimov) ba9d73268f9585d4b9254adcf54708f88222798b net: add RAII socket and use it instead of bare SOCKET (Vasil Dimov) dec9b5e850c6aad989e814aea5b630b36f55d580 net: move CloseSocket() from netbase to util/sock (Vasil Dimov) aa17a44551c03b00a47854438afe9f2f89b6ea74 net: move MillisToTimeval() from netbase to util/time (Vasil Dimov) Pull request description: Introduce a class to manage the lifetime of a socket - when the object that contains the socket goes out of scope, the underlying socket will be closed. In addition, the new `Sock` class has a `Send()`, `Recv()` and `Wait()` methods that can be overridden by unit tests to mock the socket operations. The `Wait()` method also hides the `#ifdef USE_POLL poll() #else select() #endif` technique from higher level code. ACKs for top commit: laanwj: Re-ACK 615ba0eb96cf131364c1ceca9d3dedf006fa1e1c jonatack: re-ACK 615ba0eb96cf131364c1ceca9d3dedf006fa1e1c Tree-SHA512: 3003e6bc0259295ca0265ccdeb1522ee25b4abe66d32e6ceaa51b55e0a999df7ddee765f86ce558a788c1953ee2009bfa149b09d494593f7d799c0d7d930bee8
Diffstat (limited to 'src/test/sock_tests.cpp')
-rw-r--r--src/test/sock_tests.cpp149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/test/sock_tests.cpp b/src/test/sock_tests.cpp
new file mode 100644
index 0000000000..cc0e6e7057
--- /dev/null
+++ b/src/test/sock_tests.cpp
@@ -0,0 +1,149 @@
+// Copyright (c) 2021-2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <compat.h>
+#include <test/util/setup_common.h>
+#include <util/sock.h>
+#include <util/system.h>
+
+#include <boost/test/unit_test.hpp>
+
+#include <thread>
+
+using namespace std::chrono_literals;
+
+BOOST_FIXTURE_TEST_SUITE(sock_tests, BasicTestingSetup)
+
+static bool SocketIsClosed(const SOCKET& s)
+{
+ // Notice that if another thread is running and creates its own socket after `s` has been
+ // closed, it may be assigned the same file descriptor number. In this case, our test will
+ // wrongly pretend that the socket is not closed.
+ int type;
+ socklen_t len = sizeof(type);
+ return getsockopt(s, SOL_SOCKET, SO_TYPE, (sockopt_arg_type)&type, &len) == SOCKET_ERROR;
+}
+
+static SOCKET CreateSocket()
+{
+ const SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ BOOST_REQUIRE(s != static_cast<SOCKET>(SOCKET_ERROR));
+ return s;
+}
+
+BOOST_AUTO_TEST_CASE(constructor_and_destructor)
+{
+ const SOCKET s = CreateSocket();
+ Sock* sock = new Sock(s);
+ BOOST_CHECK_EQUAL(sock->Get(), s);
+ BOOST_CHECK(!SocketIsClosed(s));
+ delete sock;
+ BOOST_CHECK(SocketIsClosed(s));
+}
+
+BOOST_AUTO_TEST_CASE(move_constructor)
+{
+ const SOCKET s = CreateSocket();
+ Sock* sock1 = new Sock(s);
+ Sock* sock2 = new Sock(std::move(*sock1));
+ delete sock1;
+ BOOST_CHECK(!SocketIsClosed(s));
+ BOOST_CHECK_EQUAL(sock2->Get(), s);
+ delete sock2;
+ BOOST_CHECK(SocketIsClosed(s));
+}
+
+BOOST_AUTO_TEST_CASE(move_assignment)
+{
+ const SOCKET s = CreateSocket();
+ Sock* sock1 = new Sock(s);
+ Sock* sock2 = new Sock();
+ *sock2 = std::move(*sock1);
+ delete sock1;
+ BOOST_CHECK(!SocketIsClosed(s));
+ BOOST_CHECK_EQUAL(sock2->Get(), s);
+ delete sock2;
+ BOOST_CHECK(SocketIsClosed(s));
+}
+
+BOOST_AUTO_TEST_CASE(release)
+{
+ SOCKET s = CreateSocket();
+ Sock* sock = new Sock(s);
+ BOOST_CHECK_EQUAL(sock->Release(), s);
+ delete sock;
+ BOOST_CHECK(!SocketIsClosed(s));
+ BOOST_REQUIRE(CloseSocket(s));
+}
+
+BOOST_AUTO_TEST_CASE(reset)
+{
+ const SOCKET s = CreateSocket();
+ Sock sock(s);
+ sock.Reset();
+ BOOST_CHECK(SocketIsClosed(s));
+}
+
+#ifndef WIN32 // Windows does not have socketpair(2).
+
+static void CreateSocketPair(int s[2])
+{
+ BOOST_REQUIRE_EQUAL(socketpair(AF_UNIX, SOCK_STREAM, 0, s), 0);
+}
+
+static void SendAndRecvMessage(const Sock& sender, const Sock& receiver)
+{
+ const char* msg = "abcd";
+ constexpr size_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);
+
+ 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();
+ *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]));
+}
+
+BOOST_AUTO_TEST_CASE(wait)
+{
+ int s[2];
+ CreateSocketPair(s);
+
+ Sock sock0(s[0]);
+ Sock sock1(s[1]);
+
+ std::thread waiter([&sock0]() { sock0.Wait(24h, Sock::RECV); });
+
+ BOOST_REQUIRE_EQUAL(sock1.Send("a", 1, 0), 1);
+
+ waiter.join();
+}
+
+#endif /* WIN32 */
+
+BOOST_AUTO_TEST_SUITE_END()