blob: 3af47c3f18a7df2887a05bbfc5a9a086a832a570 [file] [edit]
//===-- Unittests for getsockopt and setsockopt ---------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "hdr/sys_socket_macros.h"
#include "hdr/time_macros.h"
#include "hdr/types/struct_linger.h"
#include "hdr/types/struct_timespec.h"
#include "hdr/types/struct_timeval.h"
#include "src/sys/socket/getsockopt.h"
#include "src/sys/socket/recv.h"
#include "src/sys/socket/setsockopt.h"
#include "src/sys/socket/socket.h"
#include "src/sys/socket/socketpair.h"
#include "src/time/clock_gettime.h"
#include "src/unistd/close.h"
#include "src/unistd/pipe.h"
#include "src/__support/CPP/scope.h"
#include "test/UnitTest/ErrnoCheckingTest.h"
#include "test/UnitTest/ErrnoSetterMatcher.h"
#include "test/UnitTest/Test.h"
#include <sys/socket.h>
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
using LlvmLibcSocketOptTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
using LIBC_NAMESPACE::cpp::scope_exit;
TEST_F(LlvmLibcSocketOptTest, BasicSocketOpt) {
int sock = LIBC_NAMESPACE::socket(AF_UNIX, SOCK_STREAM, 0);
ASSERT_GE(sock, 0);
scope_exit close_sock(
[&] { ASSERT_THAT(LIBC_NAMESPACE::close(sock), Succeeds(0)); });
int optval = 0;
socklen_t optlen = sizeof(optval);
// Test a boolean-like option
optval = 1;
ASSERT_THAT(LIBC_NAMESPACE::setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
&optval, optlen),
Succeeds(0));
optval = 0;
ASSERT_THAT(LIBC_NAMESPACE::getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
&optval, &optlen),
Succeeds(0));
ASSERT_EQ(optval, 1);
ASSERT_EQ(optlen, static_cast<socklen_t>(sizeof(optval)));
// Test SO_TYPE (read-only)
ASSERT_THAT(
LIBC_NAMESPACE::getsockopt(sock, SOL_SOCKET, SO_TYPE, &optval, &optlen),
Succeeds(0));
ASSERT_EQ(optval, static_cast<int>(SOCK_STREAM));
ASSERT_EQ(optlen, static_cast<socklen_t>(sizeof(optval)));
optval = SOCK_DGRAM;
ASSERT_THAT(
LIBC_NAMESPACE::setsockopt(sock, SOL_SOCKET, SO_TYPE, &optval, optlen),
Fails(ENOPROTOOPT));
// Test SO_LINGER (uses a struct)
struct linger lin;
lin.l_onoff = 1;
lin.l_linger = 5;
optlen = sizeof(lin);
ASSERT_THAT(
LIBC_NAMESPACE::setsockopt(sock, SOL_SOCKET, SO_LINGER, &lin, optlen),
Succeeds(0));
lin = {};
optlen = sizeof(lin);
ASSERT_THAT(
LIBC_NAMESPACE::getsockopt(sock, SOL_SOCKET, SO_LINGER, &lin, &optlen),
Succeeds(0));
ASSERT_EQ(lin.l_onoff, 1);
ASSERT_EQ(lin.l_linger, 5);
ASSERT_EQ(optlen, static_cast<socklen_t>(sizeof(lin)));
}
TEST_F(LlvmLibcSocketOptTest, NotASocket) {
int fds[2];
ASSERT_THAT(LIBC_NAMESPACE::pipe(fds), Succeeds(0));
scope_exit close_fd0(
[&] { ASSERT_THAT(LIBC_NAMESPACE::close(fds[0]), Succeeds(0)); });
ASSERT_THAT(LIBC_NAMESPACE::close(fds[1]), Succeeds(0));
int optval = 1;
socklen_t optlen = sizeof(optval);
ASSERT_THAT(LIBC_NAMESPACE::setsockopt(fds[0], SOL_SOCKET, SO_KEEPALIVE,
&optval, optlen),
Fails(ENOTSOCK));
ASSERT_THAT(LIBC_NAMESPACE::getsockopt(fds[0], SOL_SOCKET, SO_KEEPALIVE,
&optval, &optlen),
Fails(ENOTSOCK));
}
TEST_F(LlvmLibcSocketOptTest, InvalidSocket) {
int optval = 1;
socklen_t optlen = sizeof(optval);
ASSERT_THAT(
LIBC_NAMESPACE::setsockopt(-1, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen),
Fails(EBADF));
ASSERT_THAT(LIBC_NAMESPACE::getsockopt(-1, SOL_SOCKET, SO_KEEPALIVE, &optval,
&optlen),
Fails(EBADF));
}
TEST_F(LlvmLibcSocketOptTest, ReceiveTimeout) {
int sv[2] = {0, 0};
ASSERT_THAT(LIBC_NAMESPACE::socketpair(AF_UNIX, SOCK_STREAM, 0, sv),
Succeeds(0));
scope_exit close_sv([&] {
ASSERT_THAT(LIBC_NAMESPACE::close(sv[0]), Succeeds(0));
ASSERT_THAT(LIBC_NAMESPACE::close(sv[1]), Succeeds(0));
});
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
socklen_t optlen = sizeof(tv);
ASSERT_THAT(
LIBC_NAMESPACE::setsockopt(sv[0], SOL_SOCKET, SO_RCVTIMEO, &tv, optlen),
Succeeds(0));
// Retrieve option to verify it was set correctly.
struct timeval retrieved_tv;
retrieved_tv.tv_sec = 0;
retrieved_tv.tv_usec = 0;
socklen_t retrieved_optlen = sizeof(retrieved_tv);
ASSERT_THAT(LIBC_NAMESPACE::getsockopt(sv[0], SOL_SOCKET, SO_RCVTIMEO,
&retrieved_tv, &retrieved_optlen),
Succeeds(0));
ASSERT_EQ(retrieved_optlen, optlen);
ASSERT_EQ(retrieved_tv.tv_sec, tv.tv_sec);
char buffer[10];
struct timespec start, end;
ASSERT_EQ(LIBC_NAMESPACE::clock_gettime(CLOCK_MONOTONIC, &start), 0);
// Read/recv on empty socket should block for ~1s and fail with EAGAIN.
ASSERT_THAT(LIBC_NAMESPACE::recv(sv[0], buffer, sizeof(buffer), 0),
Fails<ssize_t>(EAGAIN));
ASSERT_EQ(LIBC_NAMESPACE::clock_gettime(CLOCK_MONOTONIC, &end), 0);
int64_t elapsed_seconds = end.tv_sec - start.tv_sec;
int64_t elapsed_nseconds = end.tv_nsec - start.tv_nsec;
int64_t elapsed_ms = elapsed_seconds * 1000 + elapsed_nseconds / 1000000;
ASSERT_GE(elapsed_ms, static_cast<int64_t>(1000));
ASSERT_LT(elapsed_ms, static_cast<int64_t>(10000));
}