socket: Introduce GetSockAddr() and improve test quality Adds a new method to UnixSocket and UnixSocketRaw which wraps getsockname() and returns a host:port string for the current socket. This is needed by aosp/2627899 and removes our fragile random port allocation code for tests, which can be replaced by a Bind() on port 0. Test: sudo modprobe vsock-loopback && perfetto_unittests --gtest_filter=UnixSocketTest.GetSockAddr* Bug: 284258446 Change-Id: Ia8b2d22e13f1ff872055983bc10eab6abe794bd9
diff --git a/src/base/unix_socket_unittest.cc b/src/base/unix_socket_unittest.cc index 6ed65d5..ab62021 100644 --- a/src/base/unix_socket_unittest.cc +++ b/src/base/unix_socket_unittest.cc
@@ -39,6 +39,15 @@ #include "src/ipc/test/test_socket.h" #include "test/gtest_and_gmock.h" +#define SKIP_IF_VSOCK_LOOPBACK_NOT_SUPPORTED() \ + do { \ + if (!UnixSocketRaw::CreateMayFail(SockFamily::kVsock, SockType::kStream) \ + .Bind("vsock://1:10000")) { \ + GTEST_SKIP() << "vsock testing skipped: loopback address unsupported.\n" \ + << "Please run sudo modprobe vsock-loopback"; \ + } \ + } while (0) + namespace perfetto { namespace base { namespace { @@ -422,21 +431,100 @@ ASSERT_STREQ(buf, "test"); } -TEST_F(UnixSocketTest, TcpStream) { - char host_and_port[32]; - int attempt = 0; - std::unique_ptr<UnixSocket> srv; - - // Try listening on a random port. Some ports might be taken by other syste - // services. Do a bunch of attempts on different ports before giving up. - do { - base::SprintfTrunc(host_and_port, sizeof(host_and_port), "127.0.0.1:%d", - 10000 + (rand() % 10000)); - srv = UnixSocket::Listen(host_and_port, &event_listener_, &task_runner_, - SockFamily::kInet, SockType::kStream); - } while ((!srv || !srv->is_listening()) && attempt++ < 10); +// Tests that the return value of GetSockAddr() returns a well formatted address +// that can be passed to UnixSocket::Connect(). +TEST_F(UnixSocketTest, GetSockAddrTcp4) { + auto srv = UnixSocket::Listen("127.0.0.1:0", &event_listener_, &task_runner_, + SockFamily::kInet, SockType::kStream); ASSERT_TRUE(srv->is_listening()); + auto cli = + UnixSocket::Connect(srv->GetSockAddr(), &event_listener_, &task_runner_, + SockFamily::kInet, SockType::kStream); + EXPECT_CALL(event_listener_, OnConnect(cli.get(), true)) + .WillOnce(InvokeWithoutArgs(task_runner_.CreateCheckpoint("connected"))); + task_runner_.RunUntilCheckpoint("connected"); +} + +TEST_F(UnixSocketTest, GetSockAddrTcp6) { + auto srv = UnixSocket::Listen("[::1]:0", &event_listener_, &task_runner_, + SockFamily::kInet6, SockType::kStream); + ASSERT_TRUE(srv->is_listening()); + auto cli = + UnixSocket::Connect(srv->GetSockAddr(), &event_listener_, &task_runner_, + SockFamily::kInet6, SockType::kStream); + + EXPECT_CALL(event_listener_, OnConnect(cli.get(), true)) + .WillOnce(InvokeWithoutArgs(task_runner_.CreateCheckpoint("connected"))); + task_runner_.RunUntilCheckpoint("connected"); +} + +#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ + PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ + PERFETTO_BUILDFLAG(PERFETTO_OS_MAC) +TEST_F(UnixSocketTest, GetSockAddrUnixLinked) { + TempDir tmp_dir = TempDir::Create(); + std::string sock_path = tmp_dir.path() + "/test.sock"; + auto srv = UnixSocket::Listen(sock_path, &event_listener_, &task_runner_, + SockFamily::kUnix, SockType::kStream); + ASSERT_TRUE(srv->is_listening()); + EXPECT_EQ(sock_path, srv->GetSockAddr()); + auto cli = + UnixSocket::Connect(srv->GetSockAddr(), &event_listener_, &task_runner_, + SockFamily::kUnix, SockType::kStream); + EXPECT_CALL(event_listener_, OnConnect(cli.get(), true)) + .WillOnce(InvokeWithoutArgs(task_runner_.CreateCheckpoint("connected"))); + task_runner_.RunUntilCheckpoint("connected"); + cli.reset(); + srv.reset(); + remove(sock_path.c_str()); +} +#endif + +#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ + PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) +TEST_F(UnixSocketTest, GetSockAddrUnixAbstract) { + StackString<128> sock_name("@perfetto_sock_%d_%d", getpid(), rand() % 100000); + + auto srv = + UnixSocket::Listen(sock_name.ToStdString(), &event_listener_, + &task_runner_, SockFamily::kUnix, SockType::kStream); + ASSERT_TRUE(srv->is_listening()); + EXPECT_EQ(sock_name.ToStdString(), srv->GetSockAddr()); + auto cli = + UnixSocket::Connect(srv->GetSockAddr(), &event_listener_, &task_runner_, + SockFamily::kUnix, SockType::kStream); + EXPECT_CALL(event_listener_, OnConnect(cli.get(), true)) + .WillOnce(InvokeWithoutArgs(task_runner_.CreateCheckpoint("connected"))); + task_runner_.RunUntilCheckpoint("connected"); +} +#endif + +#if (PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ + PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)) +TEST_F(UnixSocketTest, GetSockAddrVsock) { + SKIP_IF_VSOCK_LOOPBACK_NOT_SUPPORTED(); + + auto srv = UnixSocket::Listen("vsock://1:-1", &event_listener_, &task_runner_, + SockFamily::kVsock, SockType::kStream); + ASSERT_TRUE(srv->is_listening()); + auto cli = + UnixSocket::Connect(srv->GetSockAddr(), &event_listener_, &task_runner_, + SockFamily::kVsock, SockType::kStream); + + EXPECT_CALL(event_listener_, OnConnect(cli.get(), true)) + .WillOnce(InvokeWithoutArgs(task_runner_.CreateCheckpoint("connected"))); + task_runner_.RunUntilCheckpoint("connected"); +} +#endif + +TEST_F(UnixSocketTest, TcpStream) { + // Listen on a random port. + std::unique_ptr<UnixSocket> srv = + UnixSocket::Listen("127.0.0.1:0", &event_listener_, &task_runner_, + SockFamily::kInet, SockType::kStream); + ASSERT_TRUE(srv->is_listening()); + std::string host_and_port = srv->GetSockAddr(); constexpr size_t kNumClients = 3; std::unique_ptr<UnixSocket> cli[kNumClients]; EXPECT_CALL(event_listener_, OnNewIncomingConnection(srv.get(), _)) @@ -1015,23 +1103,12 @@ addr_len)); } -bool VsockLoopBackAddrSupported() { - auto sock = - UnixSocketRaw::CreateMayFail(SockFamily::kVsock, SockType::kStream); - return sock.Bind("vsock://1:10000"); -} - TEST_F(UnixSocketTest, VSockStream) { - if (!VsockLoopBackAddrSupported()) - GTEST_SKIP() << "vsock testing skipped: loopback address unsupported"; + SKIP_IF_VSOCK_LOOPBACK_NOT_SUPPORTED(); - // Use the loopback CID (1) and a random unprileged port number. - StackString<128> sock_name("vsock://1:%d", 1024 + rand() % 10000); - - // Set up the server. - auto srv = - UnixSocket::Listen(sock_name.ToStdString(), &event_listener_, - &task_runner_, SockFamily::kVsock, SockType::kStream); + // Set up the server. Use the loopback CID (1) and a random port number. + auto srv = UnixSocket::Listen("vsock://1:-1", &event_listener_, &task_runner_, + SockFamily::kVsock, SockType::kStream); ASSERT_TRUE(srv && srv->is_listening()); std::unique_ptr<UnixSocket> prod; @@ -1048,15 +1125,15 @@ // Set up the client. prod = - UnixSocket::Connect(sock_name.ToStdString(), &event_listener_, - &task_runner_, SockFamily::kVsock, SockType::kStream); + UnixSocket::Connect(srv->GetSockAddr(), &event_listener_, &task_runner_, + SockFamily::kVsock, SockType::kStream); auto checkpoint = task_runner_.CreateCheckpoint("prod_connected"); EXPECT_CALL(event_listener_, OnDisconnect(prod.get())).Times(AtLeast(0)); EXPECT_CALL(event_listener_, OnConnect(prod.get(), true)); EXPECT_CALL(event_listener_, OnDataAvailable(prod.get())) .WillRepeatedly(Invoke([checkpoint](UnixSocket* s) { auto str = s->ReceiveString(); - if (str == "") + if (str.empty()) return; // Connection EOF. ASSERT_EQ("welcome", str); checkpoint();