diff options
author | Tor Egge <Tor.Egge@online.no> | 2024-06-22 14:15:11 +0200 |
---|---|---|
committer | Tor Egge <Tor.Egge@online.no> | 2024-06-22 14:15:11 +0200 |
commit | 9f6d388374c99052f65dade552d592efa7477e25 (patch) | |
tree | fb5a63b9e18ec7d0c9ab00016693d84815f35b64 /vespalib/src | |
parent | d7c4bf19130d68df0aba20cf1659dc36ae4e33d0 (diff) |
Rewrite socket unit test and send fd unit test to gtest.
Diffstat (limited to 'vespalib/src')
-rw-r--r-- | vespalib/src/tests/net/send_fd/CMakeLists.txt | 1 | ||||
-rw-r--r-- | vespalib/src/tests/net/send_fd/send_fd_test.cpp | 92 | ||||
-rw-r--r-- | vespalib/src/tests/net/socket/CMakeLists.txt | 1 | ||||
-rw-r--r-- | vespalib/src/tests/net/socket/socket_test.cpp | 382 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/test/socket_options_verifier.h | 32 |
5 files changed, 344 insertions, 164 deletions
diff --git a/vespalib/src/tests/net/send_fd/CMakeLists.txt b/vespalib/src/tests/net/send_fd/CMakeLists.txt index 4c46a773f5c..b4baaeb5855 100644 --- a/vespalib/src/tests/net/send_fd/CMakeLists.txt +++ b/vespalib/src/tests/net/send_fd/CMakeLists.txt @@ -4,5 +4,6 @@ vespa_add_executable(vespalib_send_fd_test_app TEST send_fd_test.cpp DEPENDS vespalib + GTest::gtest ) vespa_add_test(NAME vespalib_send_fd_test_app COMMAND vespalib_send_fd_test_app) diff --git a/vespalib/src/tests/net/send_fd/send_fd_test.cpp b/vespalib/src/tests/net/send_fd/send_fd_test.cpp index 59b9aacea07..8dc1235ff76 100644 --- a/vespalib/src/tests/net/send_fd/send_fd_test.cpp +++ b/vespalib/src/tests/net/send_fd/send_fd_test.cpp @@ -1,5 +1,5 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/testkit/test_kit.h> +#include <vespa/vespalib/gtest/gtest.h> #include <vespa/vespalib/testkit/time_bomb.h> #include <vespa/vespalib/net/selector.h> @@ -7,15 +7,19 @@ #include <vespa/vespalib/net/server_socket.h> #include <vespa/vespalib/net/socket_options.h> #include <vespa/vespalib/net/socket.h> +#include <vespa/vespalib/test/nexus.h> #include <vespa/vespalib/util/stringfmt.h> #include <vespa/vespalib/test/socket_options_verifier.h> -#include <thread> -#include <functional> #include <chrono> +#include <functional> +#include <latch> +#include <optional> +#include <thread> #include <unistd.h> #include <sys/stat.h> using namespace vespalib; +using vespalib::test::Nexus; vespalib::string read_bytes(SocketHandle &socket, size_t wanted_bytes) { char tmp[64]; @@ -23,7 +27,9 @@ vespalib::string read_bytes(SocketHandle &socket, size_t wanted_bytes) { while (result.size() < wanted_bytes) { size_t read_size = std::min(sizeof(tmp), wanted_bytes - result.size()); ssize_t read_result = socket.read(tmp, read_size); - ASSERT_GREATER(read_result, 0); + if (read_result <= 0) { + return result; + } result.append(tmp, read_result); } return result; @@ -34,14 +40,14 @@ void verify_socket_io(bool is_server, SocketHandle &socket) { vespalib::string client_message = "please pick up, I need to talk to you"; if(is_server) { ssize_t written = socket.write(server_message.data(), server_message.size()); - EXPECT_EQUAL(written, ssize_t(server_message.size())); + EXPECT_EQ(written, ssize_t(server_message.size())); vespalib::string read = read_bytes(socket, client_message.size()); - EXPECT_EQUAL(client_message, read); + EXPECT_EQ(client_message, read); } else { ssize_t written = socket.write(client_message.data(), client_message.size()); - EXPECT_EQUAL(written, ssize_t(client_message.size())); + EXPECT_EQ(written, ssize_t(client_message.size())); vespalib::string read = read_bytes(socket, server_message.size()); - EXPECT_EQUAL(server_message, read); + EXPECT_EQ(server_message, read); } } @@ -79,10 +85,10 @@ void send_fd(SocketHandle &socket, SocketHandle fd) { int *fd_dst = (int *) (void *) CMSG_DATA(hdr); fd_dst[0] = fd.get(); ssize_t res = sendmsg(socket.get(), &msg, 0); - ASSERT_EQUAL(res, 1); + ASSERT_EQ(res, 1); } -SocketHandle recv_fd(SocketHandle &socket) { +void recv_fd(SocketHandle &socket, std::optional<SocketHandle>& result) { struct msghdr msg = {}; char tag = '*'; struct iovec data; @@ -94,36 +100,62 @@ SocketHandle recv_fd(SocketHandle &socket) { msg.msg_control = buf; msg.msg_controllen = sizeof(buf); ssize_t res = recvmsg(socket.get(), &msg, 0); - ASSERT_EQUAL(res, 1); + ASSERT_EQ(res, 1); struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); bool type_ok = ((hdr->cmsg_level == SOL_SOCKET) && (hdr->cmsg_type == SCM_RIGHTS)); ASSERT_TRUE(type_ok); int *fd_src = (int *) (void *) CMSG_DATA(hdr); fprintf(stderr, "got fd: %d\n", fd_src[0]); - return SocketHandle(fd_src[0]); + result = SocketHandle(fd_src[0]); } //----------------------------------------------------------------------------- -TEST_MT_FFF("require that an open socket (handle) can be passed over a unix domain socket", 3, - ServerSocket("tcp/0"), ServerSocket("ipc/file:my_socket"), TimeBomb(60)) -{ - if (thread_id == 0) { // server - SocketHandle socket = accept(f1); - TEST_DO(verify_socket_io(true, socket)); // server side - TEST_BARRIER(); - } else if (thread_id == 1) { // proxy - SocketHandle server_socket = connect(f1); - SocketHandle client_socket = accept(f2); - send_fd(client_socket, std::move(server_socket)); - TEST_BARRIER(); - } else { // client - SocketHandle proxy_socket = connect(f2); - SocketHandle socket = recv_fd(proxy_socket); - TEST_DO(verify_socket_io(false, socket)); // client side - TEST_BARRIER(); +namespace { + +class WaitLatch { + std::latch& _latch; +public: + explicit WaitLatch(std::latch& latch) noexcept + : _latch(latch) + { } + ~WaitLatch() { _latch.arrive_and_wait(); } +}; + +} + +TEST(SendFdTest, require_that_an_open_socket_handle_can_be_passed_over_a_unix_domain_socket) +{ + constexpr size_t num_threads = 3; + ServerSocket f1("tcp/0"); + ServerSocket f2("ipc/file:my_socket"); + std::latch latch(num_threads); + TimeBomb f3(60); + auto task = [&f1,&f2,&latch](Nexus& ctx) { + auto thread_id = ctx.thread_id(); + if (thread_id == 0) { // server + SocketHandle socket = accept(f1); + WaitLatch wait(latch); + SCOPED_TRACE("verify socket io server side"); + verify_socket_io(true, socket); // server side + } else if (thread_id == 1) { // proxy + SocketHandle server_socket = connect(f1); + SocketHandle client_socket = accept(f2); + WaitLatch wait(latch); + ASSERT_NO_FATAL_FAILURE(send_fd(client_socket, std::move(server_socket))); + } else { // client + SocketHandle proxy_socket = connect(f2); + std::optional<SocketHandle> socket; + WaitLatch wait(latch); + ASSERT_NO_FATAL_FAILURE(recv_fd(proxy_socket, socket)); + ASSERT_TRUE(socket.has_value()); + SCOPED_TRACE("verify socket io client side"); + verify_socket_io(false, socket.value()); // client side + } + }; + Nexus::run(num_threads, task); } -TEST_MAIN() { TEST_RUN_ALL(); } +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/vespalib/src/tests/net/socket/CMakeLists.txt b/vespalib/src/tests/net/socket/CMakeLists.txt index d20720971d2..49c4ca20ba3 100644 --- a/vespalib/src/tests/net/socket/CMakeLists.txt +++ b/vespalib/src/tests/net/socket/CMakeLists.txt @@ -4,6 +4,7 @@ vespa_add_executable(vespalib_socket_test_app TEST socket_test.cpp DEPENDS vespalib + GTest::gtest ) vespa_add_test(NAME vespalib_socket_test_app COMMAND vespalib_socket_test_app) vespa_add_executable(vespalib_socket_server_app diff --git a/vespalib/src/tests/net/socket/socket_test.cpp b/vespalib/src/tests/net/socket/socket_test.cpp index b0708388cd6..64f337c03f6 100644 --- a/vespalib/src/tests/net/socket/socket_test.cpp +++ b/vespalib/src/tests/net/socket/socket_test.cpp @@ -1,11 +1,12 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/testkit/test_kit.h> +#include <vespa/vespalib/gtest/gtest.h> #include <vespa/vespalib/testkit/time_bomb.h> #include <vespa/vespalib/net/selector.h> #include <vespa/vespalib/net/socket_spec.h> #include <vespa/vespalib/net/server_socket.h> #include <vespa/vespalib/net/socket_options.h> #include <vespa/vespalib/net/socket.h> +#include <vespa/vespalib/test/nexus.h> #include <vespa/vespalib/util/stringfmt.h> #include <vespa/vespalib/test/socket_options_verifier.h> #include <thread> @@ -14,19 +15,55 @@ #include <sys/stat.h> using namespace vespalib; +using vespalib::test::Nexus; -bool ipv4_enabled = false; -bool ipv6_enabled = false; +class SocketTest : public ::testing::Test { +protected: + static bool ipv4_enabled; + static bool ipv6_enabled; -int my_inet() { + SocketTest(); + ~SocketTest() override; + static void SetUpTestSuite(); + static void TearDownTestSuite(); + static int my_inet(); +}; + +bool SocketTest::ipv4_enabled = false; +bool SocketTest::ipv6_enabled = false; + +SocketTest::SocketTest() + : testing::Test() +{ +} + +SocketTest::~SocketTest() = default; + +void +SocketTest::SetUpTestSuite() +{ + auto list = SocketAddress::resolve(4080); + for (const auto &addr : list) { + (void) addr; + ipv4_enabled |= addr.is_ipv4(); + ipv6_enabled |= addr.is_ipv6(); + } + ASSERT_TRUE(ipv4_enabled || ipv6_enabled) << "tcp/ip support not detected"; +} + +void +SocketTest::TearDownTestSuite() +{ +} + +int +SocketTest::my_inet() +{ if (ipv6_enabled) { return AF_INET6; - } - if (ipv4_enabled) { + } else { return AF_INET; } - TEST_ERROR("tcp/ip support not detected"); - return AF_UNIX; } bool is_socket(const vespalib::string &path) { @@ -52,8 +89,8 @@ void remove_file(const vespalib::string &path) { void replace_file(const vespalib::string &path, const vespalib::string &data) { remove_file(path); int fd = creat(path.c_str(), 0600); - ASSERT_NOT_EQUAL(fd, -1); - ASSERT_EQUAL(write(fd, data.data(), data.size()), ssize_t(data.size())); + ASSERT_NE(fd, -1); + ASSERT_EQ(write(fd, data.data(), data.size()), ssize_t(data.size())); close(fd); } @@ -96,14 +133,14 @@ void verify_socket_io(bool is_server, SocketHandle &socket) { vespalib::string client_message = "please pick up, I need to talk to you"; if(is_server) { ssize_t written = socket.write(server_message.data(), server_message.size()); - EXPECT_EQUAL(written, ssize_t(server_message.size())); + EXPECT_EQ(written, ssize_t(server_message.size())); vespalib::string read = read_bytes(socket, client_message.size()); - EXPECT_EQUAL(client_message, read); + EXPECT_EQ(client_message, read); } else { ssize_t written = socket.write(client_message.data(), client_message.size()); - EXPECT_EQUAL(written, ssize_t(client_message.size())); + EXPECT_EQ(written, ssize_t(client_message.size())); vespalib::string read = read_bytes(socket, server_message.size()); - EXPECT_EQUAL(server_message, read); + EXPECT_EQ(server_message, read); } } @@ -122,22 +159,22 @@ SocketHandle connect_sockets(bool is_server, ServerSocket &server_socket) { //----------------------------------------------------------------------------- -TEST("my local address") { +TEST_F(SocketTest, my_local_address) +{ auto list = SocketAddress::resolve(4080); fprintf(stderr, "resolve(4080):\n"); for (const auto &addr: list) { EXPECT_TRUE(addr.is_wildcard()); EXPECT_TRUE(addr.is_ipv4() || addr.is_ipv6()); - ipv4_enabled |= addr.is_ipv4(); - ipv6_enabled |= addr.is_ipv6(); EXPECT_TRUE(!addr.is_ipc()); EXPECT_TRUE(!addr.is_abstract()); - EXPECT_EQUAL(addr.port(), 4080); + EXPECT_EQ(addr.port(), 4080); fprintf(stderr, " %s (%s)\n", addr.spec().c_str(), get_meta(addr).c_str()); } } -TEST("yahoo.com address") { +TEST_F(SocketTest, yahoo_com_address) +{ auto list = SocketAddress::resolve(80, "yahoo.com"); fprintf(stderr, "resolve(80, 'yahoo.com'):\n"); for (const auto &addr: list) { @@ -145,80 +182,102 @@ TEST("yahoo.com address") { EXPECT_TRUE(addr.is_ipv4() || addr.is_ipv6()); EXPECT_TRUE(!addr.is_ipc()); EXPECT_TRUE(!addr.is_abstract()); - EXPECT_EQUAL(addr.port(), 80); + EXPECT_EQ(addr.port(), 80); fprintf(stderr, " %s (%s)\n", addr.spec().c_str(), get_meta(addr).c_str()); } } -TEST("ipc address (path)") { +TEST_F(SocketTest, ipc_address_with_path) +{ auto addr = SocketAddress::from_path("my_socket"); EXPECT_TRUE(!addr.is_ipv4()); EXPECT_TRUE(!addr.is_ipv6()); EXPECT_TRUE(addr.is_ipc()); EXPECT_TRUE(!addr.is_abstract()); EXPECT_TRUE(!addr.is_wildcard()); - EXPECT_EQUAL(addr.port(), -1); - EXPECT_EQUAL(vespalib::string("my_socket"), addr.path()); + EXPECT_EQ(addr.port(), -1); + EXPECT_EQ(vespalib::string("my_socket"), addr.path()); EXPECT_TRUE(addr.name().empty()); fprintf(stderr, "from_path(my_socket)\n"); fprintf(stderr, " %s (%s)\n", addr.spec().c_str(), get_meta(addr).c_str()); } -TEST("ipc address (name)") { +TEST_F(SocketTest, ipc_address_with_name) +{ auto addr = SocketAddress::from_name("my_socket"); EXPECT_TRUE(!addr.is_ipv4()); EXPECT_TRUE(!addr.is_ipv6()); EXPECT_TRUE(addr.is_ipc()); EXPECT_TRUE(addr.is_abstract()); EXPECT_TRUE(!addr.is_wildcard()); - EXPECT_EQUAL(addr.port(), -1); + EXPECT_EQ(addr.port(), -1); EXPECT_TRUE(addr.path().empty()); - EXPECT_EQUAL(vespalib::string("my_socket"), addr.name()); + EXPECT_EQ(vespalib::string("my_socket"), addr.name()); fprintf(stderr, "from_path(my_socket)\n"); fprintf(stderr, " %s (%s)\n", addr.spec().c_str(), get_meta(addr).c_str()); } -TEST("local client/server addresses") { +TEST_F(SocketTest, local_client_and_server_addresses) { auto spec = SocketSpec("tcp/123"); auto client = spec.client_address(); auto server = spec.server_address(); EXPECT_TRUE(!client.is_wildcard()); - EXPECT_EQUAL(client.port(), 123); + EXPECT_EQ(client.port(), 123); EXPECT_TRUE(server.is_wildcard()); - EXPECT_EQUAL(server.port(), 123); + EXPECT_EQ(server.port(), 123); fprintf(stderr, "client(tcp/123): %s (%s)\n", client.spec().c_str(), get_meta(client).c_str()); fprintf(stderr, "server(tcp/123): %s (%s)\n", server.spec().c_str(), get_meta(server).c_str()); } -TEST_MT_FF("require that basic socket io works", 2, ServerSocket("tcp/0"), TimeBomb(60)) { - bool is_server = (thread_id == 0); - SocketHandle socket = connect_sockets(is_server, f1); - TEST_DO(verify_socket_io(is_server, socket)); -} - -TEST_MT_FF("require that basic unix domain socket io works (path)", 2, - ServerSocket("ipc/file:my_socket"), TimeBomb(60)) +TEST_F(SocketTest, require_that_basic_socket_io_works) +{ + constexpr size_t num_threads = 2; + ServerSocket f1("tcp/0"); + TimeBomb f2(60); + auto task = [&f1](Nexus& ctx) { + bool is_server = (ctx.thread_id() == 0); + SocketHandle socket = connect_sockets(is_server, f1); + verify_socket_io(is_server, socket); + }; + Nexus::run(num_threads, task); +} + +TEST_F(SocketTest, require_that_basic_unix_domain_socket_io_works_with_path) +{ + constexpr size_t num_threads = 2; + ServerSocket f1("ipc/file:my_socket"); + TimeBomb f2(60); + auto task = [&f1](Nexus& ctx) { + bool is_server = (ctx.thread_id() == 0); + SocketHandle socket = connect_sockets(is_server, f1); + verify_socket_io(is_server, socket); + }; + Nexus::run(num_threads, task); +} + +TEST_F(SocketTest, require_that_server_accept_can_be_interrupted) +{ + constexpr size_t num_threads = 2; + ServerSocket f1("tcp/0"); + TimeBomb f2(60); + auto task = [&f1](Nexus& ctx) { + bool is_server = (ctx.thread_id() == 0); + if (is_server) { + fprintf(stderr, "--> calling accept\n"); + SocketHandle socket = f1.accept(); + fprintf(stderr, "<-- accept returned\n"); + EXPECT_TRUE(!socket.valid()); + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + fprintf(stderr, "--- closing server socket\n"); + f1.shutdown(); + } + }; + Nexus::run(num_threads, task); +} + +TEST_F(SocketTest, require_that_socket_file_is_removed_by_server_socket_when_destructed) { - bool is_server = (thread_id == 0); - SocketHandle socket = connect_sockets(is_server, f1); - TEST_DO(verify_socket_io(is_server, socket)); -} - -TEST_MT_FF("require that server accept can be interrupted", 2, ServerSocket("tcp/0"), TimeBomb(60)) { - bool is_server = (thread_id == 0); - if (is_server) { - fprintf(stderr, "--> calling accept\n"); - SocketHandle socket = f1.accept(); - fprintf(stderr, "<-- accept returned\n"); - EXPECT_TRUE(!socket.valid()); - } else { - std::this_thread::sleep_for(std::chrono::milliseconds(20)); - fprintf(stderr, "--- closing server socket\n"); - f1.shutdown(); - } -} - -TEST("require that socket file is removed by server socket when destructed") { remove_file("my_socket"); ServerSocket server("ipc/file:my_socket"); EXPECT_TRUE(server.valid()); @@ -227,7 +286,8 @@ TEST("require that socket file is removed by server socket when destructed") { EXPECT_TRUE(!is_socket("my_socket")); } -TEST("require that socket file is only removed on destruction if it is a socket") { +TEST_F(SocketTest, require_that_socket_file_is_only_removed_on_destruction_if_it_is_a_socket) +{ remove_file("my_socket"); ServerSocket server("ipc/file:my_socket"); EXPECT_TRUE(server.valid()); @@ -238,7 +298,8 @@ TEST("require that socket file is only removed on destruction if it is a socket" remove_file("my_socket"); } -TEST("require that a server socket will fail to listen to a path that is already a regular file") { +TEST_F(SocketTest, require_that_a_server_socket_will_fail_to_listen_to_a_path_that_is_already_a_regular_file) +{ replace_file("my_socket", "hello\n"); ServerSocket server("ipc/file:my_socket"); EXPECT_TRUE(!server.valid()); @@ -247,7 +308,8 @@ TEST("require that a server socket will fail to listen to a path that is already remove_file("my_socket"); } -TEST("require that a server socket will fail to listen to a path that is already taken by another server") { +TEST_F(SocketTest, require_that_a_server_socket_will_fail_to_listen_to_a_path_that_is_already_taken_by_another_server) +{ remove_file("my_socket"); ServerSocket server1("ipc/file:my_socket"); ServerSocket server2("ipc/file:my_socket"); @@ -258,7 +320,8 @@ TEST("require that a server socket will fail to listen to a path that is already EXPECT_TRUE(!is_socket("my_socket")); } -TEST("require that a server socket will remove an old socket file if it cannot be connected to") { +TEST_F(SocketTest, require_that_a_server_socket_will_remove_an_old_socket_file_if_it_cannot_be_connected_to) +{ remove_file("my_socket"); { SocketHandle server_handle = SocketAddress::from_path("my_socket").listen(); @@ -272,15 +335,21 @@ TEST("require that a server socket will remove an old socket file if it cannot b } #ifdef __linux__ -TEST_MT_FF("require that basic unix domain socket io works (name)", 2, - ServerSocket(make_string("ipc/name:my_socket-%d", int(getpid()))), TimeBomb(60)) +TEST_F(SocketTest, require_that_basic_unix_domain_socket_io_works_with_name) +{ + constexpr size_t num_threads = 2; + ServerSocket f1(make_string("ipc/name:my_socket-%d", int(getpid()))); + TimeBomb f2(60); + auto task = [&f1](Nexus& ctx) { + bool is_server = (ctx.thread_id() == 0); + SocketHandle socket = connect_sockets(is_server, f1); + verify_socket_io(is_server, socket); + }; + Nexus::run(num_threads, task); +} + +TEST_F(SocketTest, require_that_two_server_sockets_cannot_have_the_same_abstract_unix_domain_socket_name) { - bool is_server = (thread_id == 0); - SocketHandle socket = connect_sockets(is_server, f1); - TEST_DO(verify_socket_io(is_server, socket)); -} - -TEST("require that two server sockets cannot have the same abstract unix domain socket name") { vespalib::string spec = make_string("ipc/name:my_socket-%d", int(getpid())); ServerSocket server1(spec); ServerSocket server2(spec); @@ -288,7 +357,8 @@ TEST("require that two server sockets cannot have the same abstract unix domain EXPECT_TRUE(!server2.valid()); } -TEST("require that abstract socket names are freed when the server socket is destructed") { +TEST_F(SocketTest, require_that_abstract_socket_names_are_freed_when_the_server_socket_is_destructed) +{ vespalib::string spec = make_string("ipc/name:my_socket-%d", int(getpid())); ServerSocket server1(spec); EXPECT_TRUE(server1.valid()); @@ -297,100 +367,162 @@ TEST("require that abstract socket names are freed when the server socket is des EXPECT_TRUE(server2.valid()); } -TEST("require that abstract sockets do not have socket files") { +TEST_F(SocketTest, require_that_abstract_sockets_do_not_have_socket_files) +{ vespalib::string name = make_string("my_socket-%d", int(getpid())); ServerSocket server(SocketSpec::from_name(name)); EXPECT_TRUE(server.valid()); EXPECT_TRUE(!is_socket(name)); - EXPECT_TRUE(!is_file(name)); + EXPECT_TRUE(!is_file(name)); } -TEST_MT_FFF("require that abstract and file-based unix domain sockets are not in conflict", 4, - ServerSocket(make_string("ipc/file:my_socket-%d", int(getpid()))), - ServerSocket(make_string("ipc/name:my_socket-%d", int(getpid()))), TimeBomb(60)) +TEST_F(SocketTest, require_that_abstract_and_file_based_unix_domain_sockets_are_not_in_conflict) { - bool is_server = ((thread_id % 2) == 0); - ServerSocket &server_socket = ((thread_id / 2) == 0) ? f1 : f2; - SocketHandle socket = connect_sockets(is_server, server_socket); - TEST_DO(verify_socket_io(is_server, socket)); + constexpr size_t num_threads = 4; + ServerSocket f1(make_string("ipc/file:my_socket-%d", int(getpid()))); + ServerSocket f2(make_string("ipc/name:my_socket-%d", int(getpid()))); + TimeBomb f3(60); + auto task = [&f1,&f2](Nexus& ctx) { + auto thread_id = ctx.thread_id(); + bool is_server = ((thread_id % 2) == 0); + ServerSocket &server_socket = ((thread_id / 2) == 0) ? f1 : f2; + SocketHandle socket = connect_sockets(is_server, server_socket); + verify_socket_io(is_server, socket); + }; + Nexus::run(num_threads, task); } #endif -TEST("require that sockets can be set blocking and non-blocking") { +TEST_F(SocketTest, require_that_sockets_can_be_set_blocking_and_non_blocking) +{ SocketHandle handle(socket(my_inet(), SOCK_STREAM, 0)); test::SocketOptionsVerifier verifier(handle.get()); EXPECT_TRUE(!SocketOptions::set_blocking(-1, true)); EXPECT_TRUE(handle.set_blocking(true)); - TEST_DO(verifier.verify_blocking(true)); + { + SCOPED_TRACE("verify blocking true"); + verifier.verify_blocking(true); + } EXPECT_TRUE(handle.set_blocking(false)); - TEST_DO(verifier.verify_blocking(false)); + { + SCOPED_TRACE("verify blocking false"); + verifier.verify_blocking(false); + } } -TEST("require that server sockets use non-blocking underlying socket") { +TEST_F(SocketTest, require_that_server_sockets_use_non_blocking_underlying_socket) +{ ServerSocket tcp_server("tcp/0"); ServerSocket ipc_server("ipc/file:my_socket"); test::SocketOptionsVerifier tcp_verifier(tcp_server.get_fd()); test::SocketOptionsVerifier ipc_verifier(ipc_server.get_fd()); - TEST_DO(tcp_verifier.verify_blocking(false)); - TEST_DO(ipc_verifier.verify_blocking(false)); + { + SCOPED_TRACE("verify tcp nonblocking"); + tcp_verifier.verify_blocking(false); + } + { + SCOPED_TRACE("verify ipc nonblocking"); + ipc_verifier.verify_blocking(false); + } } -TEST("require that tcp nodelay can be enabled and disabled") { +TEST_F(SocketTest, require_that_tcp_nodelay_can_be_enabled_and_disabled) +{ SocketHandle handle(socket(my_inet(), SOCK_STREAM, 0)); test::SocketOptionsVerifier verifier(handle.get()); EXPECT_TRUE(!SocketOptions::set_nodelay(-1, true)); EXPECT_TRUE(handle.set_nodelay(true)); - TEST_DO(verifier.verify_nodelay(true)); + { + SCOPED_TRACE("verify nodelay true"); + verifier.verify_nodelay(true); + } EXPECT_TRUE(handle.set_nodelay(false)); - TEST_DO(verifier.verify_nodelay(false)); + { + SCOPED_TRACE("verify nodelay false"); + verifier.verify_nodelay(false); + } } -TEST("require that reuse addr can be set and cleared") { +TEST_F(SocketTest, require_that_reuse_addr_can_be_set_and_cleared) +{ SocketHandle handle(socket(my_inet(), SOCK_STREAM, 0)); test::SocketOptionsVerifier verifier(handle.get()); EXPECT_TRUE(!SocketOptions::set_reuse_addr(-1, true)); EXPECT_TRUE(handle.set_reuse_addr(true)); - TEST_DO(verifier.verify_reuse_addr(true)); + { + SCOPED_TRACE("verify reuse addr true"); + verifier.verify_reuse_addr(true); + } EXPECT_TRUE(handle.set_reuse_addr(false)); - TEST_DO(verifier.verify_reuse_addr(false)); + { + SCOPED_TRACE("verify reuse addr false"); + verifier.verify_reuse_addr(false); + } } -TEST("require that ipv6_only can be set and cleared") { +TEST_F(SocketTest, require_that_ipv6_only_can_be_set_and_cleared) +{ if (ipv6_enabled) { SocketHandle handle(socket(my_inet(), SOCK_STREAM, 0)); test::SocketOptionsVerifier verifier(handle.get()); EXPECT_TRUE(!SocketOptions::set_ipv6_only(-1, true)); EXPECT_TRUE(handle.set_ipv6_only(true)); - TEST_DO(verifier.verify_ipv6_only(true)); + { + SCOPED_TRACE("verify ipv6 only true"); + verifier.verify_ipv6_only(true); + } EXPECT_TRUE(handle.set_ipv6_only(false)); - TEST_DO(verifier.verify_ipv6_only(false)); + { + SCOPED_TRACE("verify ipv6 only false"); + verifier.verify_ipv6_only(false); + } } else { fprintf(stderr, "WARNING: skipping ipv6_only test since ipv6 is disabled"); } } -TEST("require that tcp keepalive can be set and cleared") { +TEST_F(SocketTest, require_that_tcp_keepalive_can_be_set_and_cleared) +{ SocketHandle handle(socket(my_inet(), SOCK_STREAM, 0)); test::SocketOptionsVerifier verifier(handle.get()); EXPECT_TRUE(!SocketOptions::set_keepalive(-1, true)); EXPECT_TRUE(handle.set_keepalive(true)); - TEST_DO(verifier.verify_keepalive(true)); + { + SCOPED_TRACE("verify keepalive true"); + verifier.verify_keepalive(true); + } EXPECT_TRUE(handle.set_keepalive(false)); - TEST_DO(verifier.verify_keepalive(false)); + { + SCOPED_TRACE("verify keepalive false"); + verifier.verify_keepalive(false); + } } -TEST("require that tcp lingering can be adjusted") { +TEST_F(SocketTest, require_that_tcp_lingering_can_be_adjusted) +{ SocketHandle handle(socket(my_inet(), SOCK_STREAM, 0)); test::SocketOptionsVerifier verifier(handle.get()); EXPECT_TRUE(!SocketOptions::set_linger(-1, true, 0)); EXPECT_TRUE(handle.set_linger(true, 0)); - TEST_DO(verifier.verify_linger(true, 0)); + { + SCOPED_TRACE("verify linger true 0"); + verifier.verify_linger(true, 0); + } EXPECT_TRUE(handle.set_linger(true, 10)); - TEST_DO(verifier.verify_linger(true, 10)); + { + SCOPED_TRACE("verify linger true 10"); + verifier.verify_linger(true, 10); + } EXPECT_TRUE(handle.set_linger(false, 0)); - TEST_DO(verifier.verify_linger(false, 0)); + { + SCOPED_TRACE("verify linger false 0"); + verifier.verify_linger(false, 0); + } EXPECT_TRUE(handle.set_linger(false, 10)); - TEST_DO(verifier.verify_linger(false, 0)); + { + SCOPED_TRACE("verify linger false 0 (overridden)"); + verifier.verify_linger(false, 0); + } } SocketHandle connect_async(const SocketAddress &addr) { @@ -411,7 +543,10 @@ SocketHandle connect_async(const SocketAddress &addr) { ctx.handle = addr.connect_async(); EXPECT_TRUE(ctx.handle.valid()); test::SocketOptionsVerifier verifier(ctx.handle.get()); - TEST_DO(verifier.verify_blocking(false)); + { + SCOPED_TRACE("verify blocking false"); + verifier.verify_blocking(false); + } if (ctx.handle.valid()) { selector.add(ctx.handle.get(), ctx, true, true); while (!ctx.connect_done) { @@ -420,23 +555,30 @@ SocketHandle connect_async(const SocketAddress &addr) { } selector.remove(ctx.handle.get()); } - EXPECT_EQUAL(ctx.error, 0); + EXPECT_EQ(ctx.error, 0); return std::move(ctx.handle); } -TEST_MT_FF("require that async connect pattern works", 2, ServerSocket("tcp/0"), TimeBomb(60)) { - if (thread_id == 0) { - SocketHandle socket = f1.accept(); - EXPECT_TRUE(socket.valid()); - TEST_DO(verify_socket_io(true, socket)); - } else { - SocketAddress addr = SocketSpec::from_port(f1.address().port()).client_address(); - SocketHandle socket = connect_async(addr); - socket.set_blocking(true); - TEST_DO(verify_socket_io(false, socket)); - // TEST_DO(connect_async(SocketAddress::select_remote(80, "www.yahoo.com"))); - // TEST_DO(connect_async(SocketAddress::select_remote(85, "myinternalhost"))); - } -} - -TEST_MAIN() { TEST_RUN_ALL(); } +TEST_F(SocketTest, require_that_async_connect_pattern_works) +{ + constexpr size_t num_threads = 2; + ServerSocket f1("tcp/0"); + TimeBomb f2(60); + auto task = [&f1](Nexus& ctx) { + if (ctx.thread_id() == 0) { + SocketHandle socket = f1.accept(); + EXPECT_TRUE(socket.valid()); + SCOPED_TRACE("verify socket io true"); + verify_socket_io(true, socket); + } else { + SocketAddress addr = SocketSpec::from_port(f1.address().port()).client_address(); + SocketHandle socket = connect_async(addr); + socket.set_blocking(true); + SCOPED_TRACE("verify socket io false"); + verify_socket_io(false, socket); + } + }; + Nexus::run(num_threads, task); +} + +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/vespalib/src/vespa/vespalib/test/socket_options_verifier.h b/vespalib/src/vespa/vespalib/test/socket_options_verifier.h index 04b4dea414e..3831c03d629 100644 --- a/vespalib/src/vespa/vespalib/test/socket_options_verifier.h +++ b/vespalib/src/vespa/vespalib/test/socket_options_verifier.h @@ -2,7 +2,7 @@ #pragma once -#include <vespa/vespalib/testkit/test_kit.h> +#include <vespa/vespalib/gtest/gtest.h> #include <fcntl.h> #include <unistd.h> #include <netinet/tcp.h> @@ -16,9 +16,9 @@ namespace { void verify_bool_opt(int fd, int level, int name, bool expect) { int data = 0; socklen_t size = sizeof(data); - EXPECT_EQUAL(getsockopt(fd, level, name, &data, &size), 0); - EXPECT_EQUAL(size, sizeof(data)); - EXPECT_EQUAL(data != 0, expect); + EXPECT_EQ(getsockopt(fd, level, name, &data, &size), 0); + EXPECT_EQ(size, sizeof(data)); + EXPECT_EQ(data != 0, expect); } } // namespace vespalib::test::<unnamed> @@ -31,31 +31,35 @@ struct SocketOptionsVerifier { SocketOptionsVerifier(int fd_in) : fd(fd_in) {} void verify_blocking(bool value) { int flags = fcntl(fd, F_GETFL, NULL); - EXPECT_NOT_EQUAL(flags, -1); - EXPECT_EQUAL(((flags & O_NONBLOCK) == 0), value); + EXPECT_NE(flags, -1); + EXPECT_EQ(((flags & O_NONBLOCK) == 0), value); } void verify_nodelay(bool value) { - TEST_DO(verify_bool_opt(fd, IPPROTO_TCP, TCP_NODELAY, value)); + SCOPED_TRACE("verify nodelay"); + verify_bool_opt(fd, IPPROTO_TCP, TCP_NODELAY, value); } void verify_reuse_addr(bool value) { - TEST_DO(verify_bool_opt(fd, SOL_SOCKET, SO_REUSEADDR, value)); + SCOPED_TRACE("verify reuse addr"); + verify_bool_opt(fd, SOL_SOCKET, SO_REUSEADDR, value); } void verify_ipv6_only(bool value) { - TEST_DO(verify_bool_opt(fd, IPPROTO_IPV6, IPV6_V6ONLY, value)); + SCOPED_TRACE("verify ipv6 only"); + verify_bool_opt(fd, IPPROTO_IPV6, IPV6_V6ONLY, value); } void verify_keepalive(bool value) { - TEST_DO(verify_bool_opt(fd, SOL_SOCKET, SO_KEEPALIVE, value)); + SCOPED_TRACE("verify keepalive"); + verify_bool_opt(fd, SOL_SOCKET, SO_KEEPALIVE, value); } void verify_linger(bool enable, int value) { struct linger data; socklen_t size = sizeof(data); memset(&data, 0, sizeof(data)); - EXPECT_EQUAL(getsockopt(fd, SOL_SOCKET, SO_LINGER, &data, &size), 0); - EXPECT_EQUAL(size, sizeof(data)); - EXPECT_EQUAL(enable, data.l_onoff); + EXPECT_EQ(getsockopt(fd, SOL_SOCKET, SO_LINGER, &data, &size), 0); + EXPECT_EQ(size, sizeof(data)); + EXPECT_EQ(enable, data.l_onoff); if (enable) { - EXPECT_EQUAL(value, data.l_linger); + EXPECT_EQ(value, data.l_linger); } } }; |