diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 64d4423ed..e23b663d1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,6 +10,14 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") find_program(VALGRIND_EXECUTABLE NAMES valgrind) endif() +# Some hosts (notably macOS, whose default soft limit is 256) start test +# processes with too few file descriptors for descriptor-heavy tests such as +# test_event. Raise the soft limit before exec'ing each test; a failure to +# raise it (hard limit too low) is ignored so the test still runs. +if(NOT WIN32) + set(SS_TEST_LAUNCHER sh -c "ulimit -n 1024 2>/dev/null\; exec \"$0\" \"$@\"") +endif() + # Helper function to add a unit test function(ss_add_test name sources libs) if(WIN32 AND "${sources}" MATCHES "utils.c") @@ -28,12 +36,12 @@ function(ss_add_test name sources libs) else() target_compile_options(${name} PRIVATE -UNDEBUG) endif() - add_test(NAME ${name} COMMAND ${name}) + add_test(NAME ${name} COMMAND ${SS_TEST_LAUNCHER} $) set_tests_properties(${name} PROPERTIES LABELS "unit") if(VALGRIND_EXECUTABLE) add_test(NAME memcheck_${name} - COMMAND ${VALGRIND_EXECUTABLE} + COMMAND ${SS_TEST_LAUNCHER} ${VALGRIND_EXECUTABLE} --tool=memcheck --leak-check=full --show-leak-kinds=definite,indirect diff --git a/tests/test_event.c b/tests/test_event.c index ab250dd71..311810846 100644 --- a/tests/test_event.c +++ b/tests/test_event.c @@ -35,6 +35,8 @@ static void must_not_run(struct ss_loop *loop, ss_io *io, int events) int main(void) { test_network_init(); + /* 256 receivers + sender + loop internals must fit under RLIMIT_NOFILE. */ + test_raise_fd_limit(512); struct ss_loop *loop = ss_loop_new(0); assert(loop); #ifdef _WIN32 @@ -48,8 +50,10 @@ int main(void) /* Exceed Winsock select's default 64 descriptors. */ struct receiver receivers[256] = {0}; ss_socket_t sender = socket(AF_INET, SOCK_DGRAM, 0); + assert((int)sender >= 0); for (unsigned i = 0; i < 256; i++) { int fd = (int)socket(AF_INET, SOCK_DGRAM, 0); + assert(fd >= 0); struct sockaddr_in address = {0}; address.sin_family = AF_INET; address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); diff --git a/tests/test_helpers.h b/tests/test_helpers.h index b37e80946..d1744a52f 100644 --- a/tests/test_helpers.h +++ b/tests/test_helpers.h @@ -4,6 +4,9 @@ #include #include #include "platform.h" +#ifndef _WIN32 +#include +#endif static inline void test_network_cleanup(void) { @@ -19,6 +22,25 @@ static inline void test_network_init(void) assert(atexit(test_network_cleanup) == 0); #endif } +/* Make sure at least `want` descriptors can be opened; the default soft + * limit is only 256 on macOS, which is below what descriptor-heavy tests + * such as test_event need. Windows sockets are not bounded by RLIMIT_NOFILE. */ +static inline void test_raise_fd_limit(unsigned want) +{ +#ifndef _WIN32 + struct rlimit limit; + assert(getrlimit(RLIMIT_NOFILE, &limit) == 0); + if (limit.rlim_cur != RLIM_INFINITY && limit.rlim_cur < want) { + limit.rlim_cur = want; + if (limit.rlim_max != RLIM_INFINITY && limit.rlim_max < limit.rlim_cur) { + limit.rlim_cur = limit.rlim_max; + } + assert(setrlimit(RLIMIT_NOFILE, &limit) == 0); + } +#else + (void)want; +#endif +} static inline FILE *test_tempfile(char *path, size_t size) { #ifdef _WIN32