[libc-commits] [libc] [libc] Update some syscall tests to be more tolerance to QEMU behaviors. (PR #208920)
via libc-commits
libc-commits at lists.llvm.org
Sun Jul 12 19:19:35 PDT 2026
https://github.com/lntue updated https://github.com/llvm/llvm-project/pull/208920
>From d67fed2fedbc478de6323227058de99c2dec61bf Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Sat, 11 Jul 2026 15:12:15 +0000
Subject: [PATCH 1/2] [libc] Update some syscall tests to be more tolerance of
QEMU behaviors.
---
libc/test/src/sys/mman/linux/madvise_test.cpp | 11 +++++--
libc/test/src/sys/mman/linux/mincore_test.cpp | 3 +-
libc/test/src/sys/mman/linux/mlock_test.cpp | 26 +++++++++++++++--
libc/test/src/sys/mman/linux/mremap_test.cpp | 4 ++-
.../src/sys/mman/linux/posix_madvise_test.cpp | 9 ++++--
.../sys/mman/linux/remap_file_pages_test.cpp | 16 ++++++++++
.../sys/socket/linux/sendrecvmmsg_test.cpp | 29 ++++++++++++++-----
.../src/sys/socket/linux/socketopt_test.cpp | 7 +++--
.../src/sys/socket/linux/socketpair_test.cpp | 5 +++-
libc/test/src/unistd/pread_pwrite_test.cpp | 3 +-
libc/test/src/unistd/read_write_test.cpp | 3 +-
11 files changed, 94 insertions(+), 22 deletions(-)
diff --git a/libc/test/src/sys/mman/linux/madvise_test.cpp b/libc/test/src/sys/mman/linux/madvise_test.cpp
index 5818696ce3840..68846d9b3d665 100644
--- a/libc/test/src/sys/mman/linux/madvise_test.cpp
+++ b/libc/test/src/sys/mman/linux/madvise_test.cpp
@@ -37,6 +37,13 @@ TEST_F(LlvmLibcMadviseTest, NoError) {
}
TEST_F(LlvmLibcMadviseTest, Error_BadPtr) {
- EXPECT_THAT(LIBC_NAMESPACE::madvise(nullptr, 8, MADV_SEQUENTIAL),
- Fails(ENOMEM));
+ int err = LIBC_NAMESPACE::madvise(nullptr, 8, MADV_SEQUENTIAL);
+ if (err == 0) {
+ // Under emulators like QEMU, madvise with hint flags is a no-op returning
+ // 0.
+ return;
+ }
+ EXPECT_EQ(err, -1);
+ EXPECT_EQ(static_cast<int>(libc_errno), ENOMEM);
+ libc_errno = 0;
}
diff --git a/libc/test/src/sys/mman/linux/mincore_test.cpp b/libc/test/src/sys/mman/linux/mincore_test.cpp
index 65a184dc383ae..60817955ca200 100644
--- a/libc/test/src/sys/mman/linux/mincore_test.cpp
+++ b/libc/test/src/sys/mman/linux/mincore_test.cpp
@@ -36,7 +36,8 @@ TEST_F(LlvmLibcMincoreTest, UnalignedAddr) {
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
EXPECT_NE(addr, MAP_FAILED);
EXPECT_EQ(reinterpret_cast<unsigned long>(addr) % page_size, 0ul);
- int res = LIBC_NAMESPACE::mincore(static_cast<char *>(addr) + 1, 1, nullptr);
+ unsigned char vec;
+ int res = LIBC_NAMESPACE::mincore(static_cast<char *>(addr) + 1, 1, &vec);
EXPECT_THAT(res, Fails(EINVAL, -1));
EXPECT_THAT(LIBC_NAMESPACE::munmap(addr, page_size), Succeeds());
}
diff --git a/libc/test/src/sys/mman/linux/mlock_test.cpp b/libc/test/src/sys/mman/linux/mlock_test.cpp
index f8742e1b6749f..01fc19e2b9178 100644
--- a/libc/test/src/sys/mman/linux/mlock_test.cpp
+++ b/libc/test/src/sys/mman/linux/mlock_test.cpp
@@ -75,8 +75,20 @@ TEST_F(LlvmLibcMlockTest, Overflow) {
Fails(EINVAL));
}
+static bool mlock2_supported() {
+ static bool supported = []() {
+ LIBC_NAMESPACE::mlock2(nullptr, 0, 0);
+ int err = libc_errno;
+ libc_errno = 0;
+ return err != ENOSYS;
+ }();
+ return supported;
+}
+
#ifdef SYS_mlock2
TEST_F(LlvmLibcMlockTest, MLock2) {
+ if (!mlock2_supported())
+ return;
PageHolder holder;
EXPECT_TRUE(holder.is_valid());
EXPECT_THAT(LIBC_NAMESPACE::madvise(holder.addr, holder.size, MADV_DONTNEED),
@@ -110,10 +122,20 @@ TEST_F(LlvmLibcMlockTest, InvalidFlag) {
EXPECT_NE(addr, MAP_FAILED);
// Invalid mlock2 flags.
- EXPECT_THAT(LIBC_NAMESPACE::mlock2(addr, alloc_size, 1234), Fails(EINVAL));
+ if (mlock2_supported()) {
+ EXPECT_THAT(LIBC_NAMESPACE::mlock2(addr, alloc_size, 1234), Fails(EINVAL));
+ }
// Invalid mlockall flags.
- EXPECT_THAT(LIBC_NAMESPACE::mlockall(1234), Fails(EINVAL));
+ int mlockall_ret = LIBC_NAMESPACE::mlockall(1234);
+ if (mlockall_ret == 0) {
+ // Under QEMU, mlockall can be a stub returning 0. Clean up immediately.
+ LIBC_NAMESPACE::munlockall();
+ } else {
+ EXPECT_EQ(mlockall_ret, -1);
+ EXPECT_EQ(static_cast<int>(libc_errno), EINVAL);
+ libc_errno = 0;
+ }
// man 2 mlockall says EINVAL is a valid return code when MCL_ONFAULT was
// specified without MCL_FUTURE or MCL_CURRENT, but this seems to fail on
diff --git a/libc/test/src/sys/mman/linux/mremap_test.cpp b/libc/test/src/sys/mman/linux/mremap_test.cpp
index 400719daf3af1..7811011c7d781 100644
--- a/libc/test/src/sys/mman/linux/mremap_test.cpp
+++ b/libc/test/src/sys/mman/linux/mremap_test.cpp
@@ -59,7 +59,9 @@ TEST_F(LlvmLibcMremapTest, Error_InvalidSize) {
// Attempt to re-map the memory with an invalid new size (0).
void *new_addr =
LIBC_NAMESPACE::mremap(addr, initial_size, 0, MREMAP_MAYMOVE);
- EXPECT_THAT(new_addr, Fails(EINVAL, MAP_FAILED));
+ EXPECT_EQ(new_addr, MAP_FAILED);
+ EXPECT_TRUE(libc_errno == EINVAL || libc_errno == ENOMEM);
+ libc_errno = 0; // Clear errno
// Clean up the original mapping.
EXPECT_THAT(LIBC_NAMESPACE::munmap(addr, initial_size), Succeeds());
diff --git a/libc/test/src/sys/mman/linux/posix_madvise_test.cpp b/libc/test/src/sys/mman/linux/posix_madvise_test.cpp
index 59c580f61a5f9..a0932fdaf5314 100644
--- a/libc/test/src/sys/mman/linux/posix_madvise_test.cpp
+++ b/libc/test/src/sys/mman/linux/posix_madvise_test.cpp
@@ -44,7 +44,10 @@ TEST_F(LlvmLibcPosixMadviseTest, Error_BadPtr) {
EXPECT_EQ(LIBC_NAMESPACE::posix_madvise(nullptr, 8, POSIX_MADV_DONTNEED), 0);
// posix_madvise doesn't set errno, but the return value is actually the error
- // code.
- EXPECT_EQ(LIBC_NAMESPACE::posix_madvise(nullptr, 8, POSIX_MADV_SEQUENTIAL),
- ENOMEM);
+ // code. Under emulators like QEMU, madvise with hint flags can be a no-op, so
+ // posix_madvise might return 0.
+ int ret = LIBC_NAMESPACE::posix_madvise(nullptr, 8, POSIX_MADV_SEQUENTIAL);
+ if (ret != 0) {
+ EXPECT_EQ(ret, ENOMEM);
+ }
}
diff --git a/libc/test/src/sys/mman/linux/remap_file_pages_test.cpp b/libc/test/src/sys/mman/linux/remap_file_pages_test.cpp
index 94e4c63286ad2..31104bab3f012 100644
--- a/libc/test/src/sys/mman/linux/remap_file_pages_test.cpp
+++ b/libc/test/src/sys/mman/linux/remap_file_pages_test.cpp
@@ -26,7 +26,19 @@ using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
using LlvmLibcRemapFilePagesTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+static bool remap_file_pages_supported() {
+ static bool supported = []() {
+ LIBC_NAMESPACE::remap_file_pages(nullptr, 0, 0, 0, 0);
+ int err = libc_errno;
+ libc_errno = 0;
+ return err != ENOSYS;
+ }();
+ return supported;
+}
+
TEST_F(LlvmLibcRemapFilePagesTest, NoError) {
+ if (!remap_file_pages_supported())
+ return;
size_t page_size = PAGE_SIZE;
ASSERT_GT(page_size, size_t(0));
@@ -53,6 +65,8 @@ TEST_F(LlvmLibcRemapFilePagesTest, NoError) {
}
TEST_F(LlvmLibcRemapFilePagesTest, ErrorInvalidFlags) {
+ if (!remap_file_pages_supported())
+ return;
size_t page_size = PAGE_SIZE;
ASSERT_GT(page_size, size_t(0));
@@ -80,6 +94,8 @@ TEST_F(LlvmLibcRemapFilePagesTest, ErrorInvalidFlags) {
}
TEST_F(LlvmLibcRemapFilePagesTest, ErrorInvalidAddress) {
+ if (!remap_file_pages_supported())
+ return;
size_t page_size = PAGE_SIZE;
ASSERT_GT(page_size, size_t(0));
diff --git a/libc/test/src/sys/socket/linux/sendrecvmmsg_test.cpp b/libc/test/src/sys/socket/linux/sendrecvmmsg_test.cpp
index 75f352802f124..dcb37784cc826 100644
--- a/libc/test/src/sys/socket/linux/sendrecvmmsg_test.cpp
+++ b/libc/test/src/sys/socket/linux/sendrecvmmsg_test.cpp
@@ -72,13 +72,19 @@ TEST_F(LlvmLibcSendRecvMmsgTest, SendRecvMmsgSucceedsWithSocketPair) {
}
struct timespec invalid_timeout = {-1, 0};
- ASSERT_THAT(LIBC_NAMESPACE::recvmmsg(sockpair[1], recv_msg_hdr,
- MESSAGES_COUNT, 0, &invalid_timeout),
- Fails<int>(EINVAL));
+ int recv_res = LIBC_NAMESPACE::recvmmsg(sockpair[1], recv_msg_hdr,
+ MESSAGES_COUNT, 0, &invalid_timeout);
+ if (recv_res == -1) {
+ ASSERT_EQ(static_cast<int>(libc_errno), EINVAL);
+ libc_errno = 0;
- ASSERT_THAT(LIBC_NAMESPACE::recvmmsg(sockpair[1], recv_msg_hdr,
- MESSAGES_COUNT, 0, nullptr),
- Succeeds<int>(MESSAGES_COUNT));
+ ASSERT_THAT(LIBC_NAMESPACE::recvmmsg(sockpair[1], recv_msg_hdr,
+ MESSAGES_COUNT, 0, nullptr),
+ Succeeds<int>(MESSAGES_COUNT));
+ } else {
+ // Under QEMU, the call might succeed and return MESSAGES_COUNT.
+ ASSERT_EQ(recv_res, static_cast<int>(MESSAGES_COUNT));
+ }
for (size_t i = 0; i < MESSAGES_COUNT; ++i) {
ASSERT_EQ(static_cast<size_t>(recv_msg_hdr[i].msg_len),
@@ -94,6 +100,13 @@ TEST_F(LlvmLibcSendRecvMmsgTest, SendMmsgFails) {
TEST_F(LlvmLibcSendRecvMmsgTest, RecvmmsgFails) {
struct mmsghdr msg_hdrs = {};
- ASSERT_THAT(LIBC_NAMESPACE::recvmmsg(-1, &msg_hdrs, 1, 0, nullptr),
- Fails(EBADF, -1));
+ int ret = LIBC_NAMESPACE::recvmmsg(-1, &msg_hdrs, 1, 0, nullptr);
+ if (ret == 1) {
+ // Under QEMU, recvmmsg(-1, ...) is buggy and can return 1 instead of -1
+ // (EBADF).
+ return;
+ }
+ EXPECT_EQ(ret, -1);
+ EXPECT_EQ(static_cast<int>(libc_errno), EBADF);
+ libc_errno = 0;
}
diff --git a/libc/test/src/sys/socket/linux/socketopt_test.cpp b/libc/test/src/sys/socket/linux/socketopt_test.cpp
index 8fd03c2cf24d4..979eb57fc29c1 100644
--- a/libc/test/src/sys/socket/linux/socketopt_test.cpp
+++ b/libc/test/src/sys/socket/linux/socketopt_test.cpp
@@ -139,8 +139,11 @@ TEST_F(LlvmLibcSocketOptTest, ReceiveTimeout) {
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);
+ // Under QEMU getsockopt(SO_RCVTIMEO) may return a length of 0.
+ ASSERT_TRUE(retrieved_optlen == optlen || retrieved_optlen == 0);
+ if (retrieved_optlen == optlen) {
+ ASSERT_EQ(retrieved_tv.tv_sec, tv.tv_sec);
+ }
char buffer[10];
struct timespec start, end;
diff --git a/libc/test/src/sys/socket/linux/socketpair_test.cpp b/libc/test/src/sys/socket/linux/socketpair_test.cpp
index 94b7412f8c061..cd450ef61392a 100644
--- a/libc/test/src/sys/socket/linux/socketpair_test.cpp
+++ b/libc/test/src/sys/socket/linux/socketpair_test.cpp
@@ -34,5 +34,8 @@ TEST_F(LlvmLibcSocketPairTest, LocalSocket) {
TEST_F(LlvmLibcSocketPairTest, SocketFails) {
int sockpair[2] = {-1, -1};
- ASSERT_THAT(LIBC_NAMESPACE::socketpair(-1, -1, -1, sockpair), Fails(EINVAL));
+ int ret = LIBC_NAMESPACE::socketpair(-1, -1, -1, sockpair);
+ EXPECT_EQ(ret, -1);
+ EXPECT_TRUE(libc_errno == EINVAL || libc_errno == EAFNOSUPPORT);
+ libc_errno = 0;
}
diff --git a/libc/test/src/unistd/pread_pwrite_test.cpp b/libc/test/src/unistd/pread_pwrite_test.cpp
index 7cbac04b60a95..ed7d2113b502a 100644
--- a/libc/test/src/unistd/pread_pwrite_test.cpp
+++ b/libc/test/src/unistd/pread_pwrite_test.cpp
@@ -74,5 +74,6 @@ TEST_F(LlvmLibcUniStd, PWriteFails) {
TEST_F(LlvmLibcUniStd, PReadFails) {
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
- EXPECT_THAT(LIBC_NAMESPACE::pread(-1, nullptr, 1, 0), Fails<ssize_t>(EBADF));
+ char buf[1];
+ EXPECT_THAT(LIBC_NAMESPACE::pread(-1, buf, 1, 0), Fails<ssize_t>(EBADF));
}
diff --git a/libc/test/src/unistd/read_write_test.cpp b/libc/test/src/unistd/read_write_test.cpp
index 8952e6083f97d..39953a5cd43d6 100644
--- a/libc/test/src/unistd/read_write_test.cpp
+++ b/libc/test/src/unistd/read_write_test.cpp
@@ -58,7 +58,8 @@ TEST_F(LlvmLibcUniStd, WriteFails) {
TEST_F(LlvmLibcUniStd, ReadFails) {
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
- EXPECT_THAT(LIBC_NAMESPACE::read(-1, nullptr, 1), Fails<ssize_t>(EBADF));
+ char buf[1];
+ EXPECT_THAT(LIBC_NAMESPACE::read(-1, buf, 1), Fails<ssize_t>(EBADF));
EXPECT_THAT(LIBC_NAMESPACE::read(0, reinterpret_cast<void *>(-1), 1),
Fails<ssize_t>(EFAULT));
}
>From 95e3a5f5306ba1cd8d49be3f8e427d2dd7b4f51e Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Mon, 13 Jul 2026 02:18:54 +0000
Subject: [PATCH 2/2] Address comments.
---
libc/cmake/modules/LLVMLibCTestRules.cmake | 4 +
libc/test/UnitTest/ErrnoSetterMatcher.h | 120 ++++++++++++++++--
libc/test/src/sys/mman/linux/CMakeLists.txt | 6 +
libc/test/src/sys/mman/linux/madvise_test.cpp | 15 +--
libc/test/src/sys/mman/linux/mincore_test.cpp | 6 +
libc/test/src/sys/mman/linux/mlock_test.cpp | 35 ++---
libc/test/src/sys/mman/linux/mremap_test.cpp | 5 +-
.../src/sys/mman/linux/posix_madvise_test.cpp | 15 ++-
.../sys/mman/linux/remap_file_pages_test.cpp | 16 ---
.../sys/socket/linux/sendrecvmmsg_test.cpp | 40 +++---
.../src/sys/socket/linux/socketopt_test.cpp | 5 +
.../src/sys/socket/linux/socketpair_test.cpp | 7 +-
libc/test/src/unistd/pread_pwrite_test.cpp | 5 +
libc/test/src/unistd/read_write_test.cpp | 5 +
14 files changed, 189 insertions(+), 95 deletions(-)
diff --git a/libc/cmake/modules/LLVMLibCTestRules.cmake b/libc/cmake/modules/LLVMLibCTestRules.cmake
index d86fb4369a381..b7d3a98059723 100644
--- a/libc/cmake/modules/LLVMLibCTestRules.cmake
+++ b/libc/cmake/modules/LLVMLibCTestRules.cmake
@@ -28,6 +28,10 @@ function(_get_common_test_compile_options output_var c_test flags)
list(APPEND compile_options "-DLIBC_TEST_SKIP_DEATH_TESTS")
endif()
+ if(CMAKE_CROSSCOMPILING_EMULATOR)
+ list(APPEND compile_options "-DLIBC_TEST_UNDER_EMULATOR")
+ endif()
+
if(LLVM_LIBC_COMPILER_IS_GCC_COMPATIBLE)
list(APPEND compile_options "-fpie")
diff --git a/libc/test/UnitTest/ErrnoSetterMatcher.h b/libc/test/UnitTest/ErrnoSetterMatcher.h
index e883308314087..a7f21843e602c 100644
--- a/libc/test/UnitTest/ErrnoSetterMatcher.h
+++ b/libc/test/UnitTest/ErrnoSetterMatcher.h
@@ -5,6 +5,31 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+/// \file
+/// ErrnoSetterMatcher is a testing utility to assert system call/function
+/// return values and the expected libc_errno.
+///
+/// Usage:
+///
+/// 1. Asserting success (return value matches and libc_errno is 0):
+///
+/// EXPECT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+///
+/// 2. Asserting failure (return value is -1 and libc_errno matches expected):
+///
+/// EXPECT_THAT(LIBC_NAMESPACE::read(-1, buf, 1), Fails(EBADF));
+///
+/// 3. Asserting failure with custom return value:
+///
+/// EXPECT_THAT(LIBC_NAMESPACE::mmap(nullptr, size, ...),
+/// Fails(ENOMEM, MAP_FAILED));
+///
+/// 4. Asserting failure with multiple possible errnos (e.g. QEMU vs actual
+/// hardware):
+///
+/// EXPECT_THAT(LIBC_NAMESPACE::socketpair(-1, -1, -1, sv),
+/// Fails(any_of(EINVAL, EAFNOSUPPORT)));
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_TEST_ERRNOSETTERMATCHER_H
#define LLVM_LIBC_TEST_ERRNOSETTERMATCHER_H
@@ -20,6 +45,11 @@
namespace LIBC_NAMESPACE_DECL {
namespace testing {
+struct ErrnoList {
+ int errs[4];
+ size_t count;
+};
+
namespace internal {
enum class CompareAction { EQ = 0, GE, GT, LE, LT, NE };
@@ -59,9 +89,71 @@ template <typename T> struct Comparator {
#endif
};
+class ErrnoCheck {
+ enum class Type { EQ = 0, NE, ANY_OF } type;
+ int expected;
+ int expected_list[4];
+ size_t list_count;
+
+public:
+ ErrnoCheck() : type(Type::EQ), expected(0), list_count(0) {}
+ ErrnoCheck(int val) : type(Type::EQ), expected(val), list_count(0) {}
+ template <typename T> ErrnoCheck(Comparator<T> cmp) : list_count(0) {
+ if (cmp.cmp == CompareAction::NE) {
+ type = Type::NE;
+ } else {
+ type = Type::EQ;
+ }
+ expected = static_cast<int>(cmp.expected);
+ }
+ ErrnoCheck(const ErrnoList &list)
+ : type(Type::ANY_OF), expected(0), list_count(list.count) {
+ for (size_t i = 0; i < list.count && i < 4; ++i) {
+ expected_list[i] = list.errs[i];
+ }
+ }
+
+ bool compare(int actual) const {
+ if (type == Type::EQ) {
+ return actual == expected;
+ } else if (type == Type::NE) {
+ return actual != expected;
+ } else if (type == Type::ANY_OF) {
+ for (size_t i = 0; i < list_count; ++i) {
+ if (actual == expected_list[i])
+ return true;
+ }
+ return false;
+ }
+ return false;
+ }
+
+ void print_expected() const {
+ if (type == Type::EQ) {
+ auto expected_str = try_get_errno_name(expected);
+ tlog << "equal to " << (expected_str ? *expected_str : "<unknown>") << "("
+ << expected << ")";
+ } else if (type == Type::NE) {
+ auto expected_str = try_get_errno_name(expected);
+ tlog << "not equal to " << (expected_str ? *expected_str : "<unknown>")
+ << "(" << expected << ")";
+ } else if (type == Type::ANY_OF) {
+ tlog << "one of [";
+ for (size_t i = 0; i < list_count; ++i) {
+ auto expected_str = try_get_errno_name(expected_list[i]);
+ tlog << (expected_str ? *expected_str : "<unknown>") << "("
+ << expected_list[i] << ")";
+ if (i + 1 < list_count)
+ tlog << ", ";
+ }
+ tlog << "]";
+ }
+ }
+};
+
template <typename T> class ErrnoSetterMatcher : public Matcher<T> {
Comparator<T> return_cmp;
- Comparator<int> errno_cmp;
+ ErrnoCheck errno_cmp;
T actual_return;
int actual_errno;
@@ -78,10 +170,10 @@ template <typename T> class ErrnoSetterMatcher : public Matcher<T> {
public:
ErrnoSetterMatcher(Comparator<T> rcmp) : return_cmp(rcmp) {}
- ErrnoSetterMatcher(Comparator<T> rcmp, Comparator<int> ecmp)
+ ErrnoSetterMatcher(Comparator<T> rcmp, ErrnoCheck ecmp)
: return_cmp(rcmp), errno_cmp(ecmp) {}
- ErrnoSetterMatcher<T> with_errno(Comparator<int> ecmp) {
+ ErrnoSetterMatcher<T> with_errno(ErrnoCheck ecmp) {
errno_cmp = ecmp;
return *this;
}
@@ -101,13 +193,11 @@ template <typename T> class ErrnoSetterMatcher : public Matcher<T> {
if constexpr (!ignore_errno()) {
if (!errno_cmp.compare(actual_errno)) {
- auto expected_str = try_get_errno_name(errno_cmp.expected);
auto actual_str = try_get_errno_name(actual_errno);
- tlog << "Expected errno to be " << errno_cmp.str() << " "
- << (expected_str ? *expected_str : "<unknown>") << "("
- << errno_cmp.expected << ") but got "
- << (actual_str ? *actual_str : "<unknown>") << "(" << actual_errno
- << ").\n";
+ tlog << "Expected errno to be ";
+ errno_cmp.print_expected();
+ tlog << " but got " << (actual_str ? *actual_str : "<unknown>") << "("
+ << actual_errno << ").\n";
}
}
}
@@ -166,12 +256,22 @@ internal::ErrnoSetterMatcher<RetT> Fails(int ExpectedErrno,
EQ(ExpectedErrno));
}
+template <typename RetT = int>
+internal::ErrnoSetterMatcher<RetT> Fails(const ErrnoList &ExpectedErrs,
+ RetT ExpectedReturn = -1) {
+ return internal::ErrnoSetterMatcher<RetT>(EQ(ExpectedReturn), ExpectedErrs);
+}
+
+template <typename... Args> inline ErrnoList any_of(Args... args) {
+ return ErrnoList{{static_cast<int>(args)...}, sizeof...(args)};
+}
+
template <typename RetT = int> class ErrnoSetterMatcherBuilder {
public:
template <typename T> using Cmp = internal::Comparator<T>;
ErrnoSetterMatcherBuilder(Cmp<RetT> cmp) : return_cmp(cmp) {}
- internal::ErrnoSetterMatcher<RetT> with_errno(Cmp<int> cmp) {
+ internal::ErrnoSetterMatcher<RetT> with_errno(internal::ErrnoCheck cmp) {
return internal::ErrnoSetterMatcher<RetT>(return_cmp, cmp);
}
diff --git a/libc/test/src/sys/mman/linux/CMakeLists.txt b/libc/test/src/sys/mman/linux/CMakeLists.txt
index a841d047b9fd7..9d989d4f877c2 100644
--- a/libc/test/src/sys/mman/linux/CMakeLists.txt
+++ b/libc/test/src/sys/mman/linux/CMakeLists.txt
@@ -187,6 +187,11 @@ add_libc_unittest(
libc.test.UnitTest.ErrnoSetterMatcher
)
+if(CMAKE_CROSSCOMPILING_EMULATOR)
+ if(LIBC_CMAKE_VERBOSE_LOGGING)
+ message(STATUS "Skipping remap_file_pages_test under emulator.")
+ endif()
+else()
add_libc_unittest(
remap_file_pages_test
SUITE
@@ -207,6 +212,7 @@ add_libc_unittest(
libc.src.unistd.close
libc.src.unistd.sysconf
)
+endif()
add_libc_unittest(
shm_test
diff --git a/libc/test/src/sys/mman/linux/madvise_test.cpp b/libc/test/src/sys/mman/linux/madvise_test.cpp
index 68846d9b3d665..d3bbb6425a9e3 100644
--- a/libc/test/src/sys/mman/linux/madvise_test.cpp
+++ b/libc/test/src/sys/mman/linux/madvise_test.cpp
@@ -36,14 +36,11 @@ TEST_F(LlvmLibcMadviseTest, NoError) {
EXPECT_THAT(LIBC_NAMESPACE::munmap(addr, alloc_size), Succeeds());
}
+// QEMU user space emulation stubs madvise hints (e.g. MADV_SEQUENTIAL) to
+// return 0, which makes this test fail as it expects ENOMEM on nullptr.
+#ifndef LIBC_TEST_UNDER_EMULATOR
TEST_F(LlvmLibcMadviseTest, Error_BadPtr) {
- int err = LIBC_NAMESPACE::madvise(nullptr, 8, MADV_SEQUENTIAL);
- if (err == 0) {
- // Under emulators like QEMU, madvise with hint flags is a no-op returning
- // 0.
- return;
- }
- EXPECT_EQ(err, -1);
- EXPECT_EQ(static_cast<int>(libc_errno), ENOMEM);
- libc_errno = 0;
+ EXPECT_THAT(LIBC_NAMESPACE::madvise(nullptr, 8, MADV_SEQUENTIAL),
+ Fails(ENOMEM));
}
+#endif // LIBC_TEST_UNDER_EMULATOR
diff --git a/libc/test/src/sys/mman/linux/mincore_test.cpp b/libc/test/src/sys/mman/linux/mincore_test.cpp
index 60817955ca200..fbbe6d6b00d2f 100644
--- a/libc/test/src/sys/mman/linux/mincore_test.cpp
+++ b/libc/test/src/sys/mman/linux/mincore_test.cpp
@@ -36,8 +36,14 @@ TEST_F(LlvmLibcMincoreTest, UnalignedAddr) {
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
EXPECT_NE(addr, MAP_FAILED);
EXPECT_EQ(reinterpret_cast<unsigned long>(addr) % page_size, 0ul);
+#ifdef LIBC_TEST_UNDER_EMULATOR
+ // QEMU user space emulation returns EFAULT instead of EINVAL because it
+ // validates the pointer parameters first.
unsigned char vec;
int res = LIBC_NAMESPACE::mincore(static_cast<char *>(addr) + 1, 1, &vec);
+#else
+ int res = LIBC_NAMESPACE::mincore(static_cast<char *>(addr) + 1, 1, nullptr);
+#endif
EXPECT_THAT(res, Fails(EINVAL, -1));
EXPECT_THAT(LIBC_NAMESPACE::munmap(addr, page_size), Succeeds());
}
diff --git a/libc/test/src/sys/mman/linux/mlock_test.cpp b/libc/test/src/sys/mman/linux/mlock_test.cpp
index 01fc19e2b9178..4e81cf4a3d9e4 100644
--- a/libc/test/src/sys/mman/linux/mlock_test.cpp
+++ b/libc/test/src/sys/mman/linux/mlock_test.cpp
@@ -75,20 +75,9 @@ TEST_F(LlvmLibcMlockTest, Overflow) {
Fails(EINVAL));
}
-static bool mlock2_supported() {
- static bool supported = []() {
- LIBC_NAMESPACE::mlock2(nullptr, 0, 0);
- int err = libc_errno;
- libc_errno = 0;
- return err != ENOSYS;
- }();
- return supported;
-}
-
-#ifdef SYS_mlock2
+// QEMU user space emulation does not support mlock2 and returns ENOSYS.
+#if defined(SYS_mlock2) && !defined(LIBC_TEST_UNDER_EMULATOR)
TEST_F(LlvmLibcMlockTest, MLock2) {
- if (!mlock2_supported())
- return;
PageHolder holder;
EXPECT_TRUE(holder.is_valid());
EXPECT_THAT(LIBC_NAMESPACE::madvise(holder.addr, holder.size, MADV_DONTNEED),
@@ -112,8 +101,11 @@ TEST_F(LlvmLibcMlockTest, MLock2) {
EXPECT_EQ(vec & 1, 1);
EXPECT_THAT(LIBC_NAMESPACE::munlock(holder.addr, holder.size), Succeeds());
}
-#endif
+#endif // defined(SYS_mlock2) && !defined(LIBC_TEST_UNDER_EMULATOR)
+// QEMU user space emulation stubs mlockall to return 0 instead of EINVAL on
+// invalid flags.
+#ifndef LIBC_TEST_UNDER_EMULATOR
TEST_F(LlvmLibcMlockTest, InvalidFlag) {
size_t alloc_size = 128; // page size
void *addr = LIBC_NAMESPACE::mmap(nullptr, alloc_size, PROT_READ,
@@ -122,20 +114,10 @@ TEST_F(LlvmLibcMlockTest, InvalidFlag) {
EXPECT_NE(addr, MAP_FAILED);
// Invalid mlock2 flags.
- if (mlock2_supported()) {
- EXPECT_THAT(LIBC_NAMESPACE::mlock2(addr, alloc_size, 1234), Fails(EINVAL));
- }
+ EXPECT_THAT(LIBC_NAMESPACE::mlock2(addr, alloc_size, 1234), Fails(EINVAL));
// Invalid mlockall flags.
- int mlockall_ret = LIBC_NAMESPACE::mlockall(1234);
- if (mlockall_ret == 0) {
- // Under QEMU, mlockall can be a stub returning 0. Clean up immediately.
- LIBC_NAMESPACE::munlockall();
- } else {
- EXPECT_EQ(mlockall_ret, -1);
- EXPECT_EQ(static_cast<int>(libc_errno), EINVAL);
- libc_errno = 0;
- }
+ EXPECT_THAT(LIBC_NAMESPACE::mlockall(1234), Fails(EINVAL));
// man 2 mlockall says EINVAL is a valid return code when MCL_ONFAULT was
// specified without MCL_FUTURE or MCL_CURRENT, but this seems to fail on
@@ -146,6 +128,7 @@ TEST_F(LlvmLibcMlockTest, InvalidFlag) {
LIBC_NAMESPACE::munmap(addr, alloc_size);
}
+#endif // LIBC_TEST_UNDER_EMULATOR
TEST_F(LlvmLibcMlockTest, MLockAll) {
{
diff --git a/libc/test/src/sys/mman/linux/mremap_test.cpp b/libc/test/src/sys/mman/linux/mremap_test.cpp
index 7811011c7d781..418cf741d1e46 100644
--- a/libc/test/src/sys/mman/linux/mremap_test.cpp
+++ b/libc/test/src/sys/mman/linux/mremap_test.cpp
@@ -14,6 +14,7 @@
#include "test/UnitTest/ErrnoSetterMatcher.h"
#include "test/UnitTest/Test.h"
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::any_of;
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
using LlvmLibcMremapTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
@@ -59,9 +60,7 @@ TEST_F(LlvmLibcMremapTest, Error_InvalidSize) {
// Attempt to re-map the memory with an invalid new size (0).
void *new_addr =
LIBC_NAMESPACE::mremap(addr, initial_size, 0, MREMAP_MAYMOVE);
- EXPECT_EQ(new_addr, MAP_FAILED);
- EXPECT_TRUE(libc_errno == EINVAL || libc_errno == ENOMEM);
- libc_errno = 0; // Clear errno
+ EXPECT_THAT(new_addr, Fails(any_of(EINVAL, ENOMEM), MAP_FAILED));
// Clean up the original mapping.
EXPECT_THAT(LIBC_NAMESPACE::munmap(addr, initial_size), Succeeds());
diff --git a/libc/test/src/sys/mman/linux/posix_madvise_test.cpp b/libc/test/src/sys/mman/linux/posix_madvise_test.cpp
index a0932fdaf5314..05edb39d590c4 100644
--- a/libc/test/src/sys/mman/linux/posix_madvise_test.cpp
+++ b/libc/test/src/sys/mman/linux/posix_madvise_test.cpp
@@ -44,10 +44,13 @@ TEST_F(LlvmLibcPosixMadviseTest, Error_BadPtr) {
EXPECT_EQ(LIBC_NAMESPACE::posix_madvise(nullptr, 8, POSIX_MADV_DONTNEED), 0);
// posix_madvise doesn't set errno, but the return value is actually the error
- // code. Under emulators like QEMU, madvise with hint flags can be a no-op, so
- // posix_madvise might return 0.
- int ret = LIBC_NAMESPACE::posix_madvise(nullptr, 8, POSIX_MADV_SEQUENTIAL);
- if (ret != 0) {
- EXPECT_EQ(ret, ENOMEM);
- }
+ // code.
+#ifdef LIBC_TEST_UNDER_EMULATOR
+ // QEMU stubs madvise hints, so posix_madvise returns 0.
+ EXPECT_EQ(LIBC_NAMESPACE::posix_madvise(nullptr, 8, POSIX_MADV_SEQUENTIAL),
+ 0);
+#else
+ EXPECT_EQ(LIBC_NAMESPACE::posix_madvise(nullptr, 8, POSIX_MADV_SEQUENTIAL),
+ ENOMEM);
+#endif
}
diff --git a/libc/test/src/sys/mman/linux/remap_file_pages_test.cpp b/libc/test/src/sys/mman/linux/remap_file_pages_test.cpp
index 31104bab3f012..94e4c63286ad2 100644
--- a/libc/test/src/sys/mman/linux/remap_file_pages_test.cpp
+++ b/libc/test/src/sys/mman/linux/remap_file_pages_test.cpp
@@ -26,19 +26,7 @@ using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
using LlvmLibcRemapFilePagesTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
-static bool remap_file_pages_supported() {
- static bool supported = []() {
- LIBC_NAMESPACE::remap_file_pages(nullptr, 0, 0, 0, 0);
- int err = libc_errno;
- libc_errno = 0;
- return err != ENOSYS;
- }();
- return supported;
-}
-
TEST_F(LlvmLibcRemapFilePagesTest, NoError) {
- if (!remap_file_pages_supported())
- return;
size_t page_size = PAGE_SIZE;
ASSERT_GT(page_size, size_t(0));
@@ -65,8 +53,6 @@ TEST_F(LlvmLibcRemapFilePagesTest, NoError) {
}
TEST_F(LlvmLibcRemapFilePagesTest, ErrorInvalidFlags) {
- if (!remap_file_pages_supported())
- return;
size_t page_size = PAGE_SIZE;
ASSERT_GT(page_size, size_t(0));
@@ -94,8 +80,6 @@ TEST_F(LlvmLibcRemapFilePagesTest, ErrorInvalidFlags) {
}
TEST_F(LlvmLibcRemapFilePagesTest, ErrorInvalidAddress) {
- if (!remap_file_pages_supported())
- return;
size_t page_size = PAGE_SIZE;
ASSERT_GT(page_size, size_t(0));
diff --git a/libc/test/src/sys/socket/linux/sendrecvmmsg_test.cpp b/libc/test/src/sys/socket/linux/sendrecvmmsg_test.cpp
index dcb37784cc826..16900d6f01a26 100644
--- a/libc/test/src/sys/socket/linux/sendrecvmmsg_test.cpp
+++ b/libc/test/src/sys/socket/linux/sendrecvmmsg_test.cpp
@@ -71,20 +71,21 @@ TEST_F(LlvmLibcSendRecvMmsgTest, SendRecvMmsgSucceedsWithSocketPair) {
recv_msg_hdr[i].msg_hdr.msg_iovlen = 1;
}
+#ifdef LIBC_TEST_UNDER_EMULATOR
+ // QEMU does not validate timespec when messages are available in the queue.
+ ASSERT_THAT(LIBC_NAMESPACE::recvmmsg(sockpair[1], recv_msg_hdr,
+ MESSAGES_COUNT, 0, nullptr),
+ Succeeds<int>(MESSAGES_COUNT));
+#else
struct timespec invalid_timeout = {-1, 0};
- int recv_res = LIBC_NAMESPACE::recvmmsg(sockpair[1], recv_msg_hdr,
- MESSAGES_COUNT, 0, &invalid_timeout);
- if (recv_res == -1) {
- ASSERT_EQ(static_cast<int>(libc_errno), EINVAL);
- libc_errno = 0;
+ ASSERT_THAT(LIBC_NAMESPACE::recvmmsg(sockpair[1], recv_msg_hdr,
+ MESSAGES_COUNT, 0, &invalid_timeout),
+ Fails<int>(EINVAL));
- ASSERT_THAT(LIBC_NAMESPACE::recvmmsg(sockpair[1], recv_msg_hdr,
- MESSAGES_COUNT, 0, nullptr),
- Succeeds<int>(MESSAGES_COUNT));
- } else {
- // Under QEMU, the call might succeed and return MESSAGES_COUNT.
- ASSERT_EQ(recv_res, static_cast<int>(MESSAGES_COUNT));
- }
+ ASSERT_THAT(LIBC_NAMESPACE::recvmmsg(sockpair[1], recv_msg_hdr,
+ MESSAGES_COUNT, 0, nullptr),
+ Succeeds<int>(MESSAGES_COUNT));
+#endif
for (size_t i = 0; i < MESSAGES_COUNT; ++i) {
ASSERT_EQ(static_cast<size_t>(recv_msg_hdr[i].msg_len),
@@ -98,15 +99,12 @@ TEST_F(LlvmLibcSendRecvMmsgTest, SendMmsgFails) {
ASSERT_THAT(LIBC_NAMESPACE::sendmmsg(-1, &msg_hdrs, 1, 0), Fails(EBADF, -1));
}
+// QEMU user space emulation has a bug where recvmmsg on fd -1 returns 1 instead
+// of -1 (with EBADF).
+#ifndef LIBC_TEST_UNDER_EMULATOR
TEST_F(LlvmLibcSendRecvMmsgTest, RecvmmsgFails) {
struct mmsghdr msg_hdrs = {};
- int ret = LIBC_NAMESPACE::recvmmsg(-1, &msg_hdrs, 1, 0, nullptr);
- if (ret == 1) {
- // Under QEMU, recvmmsg(-1, ...) is buggy and can return 1 instead of -1
- // (EBADF).
- return;
- }
- EXPECT_EQ(ret, -1);
- EXPECT_EQ(static_cast<int>(libc_errno), EBADF);
- libc_errno = 0;
+ ASSERT_THAT(LIBC_NAMESPACE::recvmmsg(-1, &msg_hdrs, 1, 0, nullptr),
+ Fails(EBADF, -1));
}
+#endif // LIBC_TEST_UNDER_EMULATOR
diff --git a/libc/test/src/sys/socket/linux/socketopt_test.cpp b/libc/test/src/sys/socket/linux/socketopt_test.cpp
index 979eb57fc29c1..629f1b6ae75b9 100644
--- a/libc/test/src/sys/socket/linux/socketopt_test.cpp
+++ b/libc/test/src/sys/socket/linux/socketopt_test.cpp
@@ -139,11 +139,16 @@ TEST_F(LlvmLibcSocketOptTest, ReceiveTimeout) {
ASSERT_THAT(LIBC_NAMESPACE::getsockopt(sv[0], SOL_SOCKET, SO_RCVTIMEO,
&retrieved_tv, &retrieved_optlen),
Succeeds(0));
+#ifdef LIBC_TEST_UNDER_EMULATOR
// Under QEMU getsockopt(SO_RCVTIMEO) may return a length of 0.
ASSERT_TRUE(retrieved_optlen == optlen || retrieved_optlen == 0);
if (retrieved_optlen == optlen) {
ASSERT_EQ(retrieved_tv.tv_sec, tv.tv_sec);
}
+#else
+ ASSERT_EQ(retrieved_optlen, optlen);
+ ASSERT_EQ(retrieved_tv.tv_sec, tv.tv_sec);
+#endif
char buffer[10];
struct timespec start, end;
diff --git a/libc/test/src/sys/socket/linux/socketpair_test.cpp b/libc/test/src/sys/socket/linux/socketpair_test.cpp
index cd450ef61392a..b7a5f0ae6a251 100644
--- a/libc/test/src/sys/socket/linux/socketpair_test.cpp
+++ b/libc/test/src/sys/socket/linux/socketpair_test.cpp
@@ -16,6 +16,7 @@
#include <sys/socket.h> // For AF_UNIX and SOCK_DGRAM
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::any_of;
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
using LlvmLibcSocketPairTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
@@ -34,8 +35,6 @@ TEST_F(LlvmLibcSocketPairTest, LocalSocket) {
TEST_F(LlvmLibcSocketPairTest, SocketFails) {
int sockpair[2] = {-1, -1};
- int ret = LIBC_NAMESPACE::socketpair(-1, -1, -1, sockpair);
- EXPECT_EQ(ret, -1);
- EXPECT_TRUE(libc_errno == EINVAL || libc_errno == EAFNOSUPPORT);
- libc_errno = 0;
+ ASSERT_THAT(LIBC_NAMESPACE::socketpair(-1, -1, -1, sockpair),
+ Fails(any_of(EINVAL, EAFNOSUPPORT)));
}
diff --git a/libc/test/src/unistd/pread_pwrite_test.cpp b/libc/test/src/unistd/pread_pwrite_test.cpp
index ed7d2113b502a..b1491a07f15fb 100644
--- a/libc/test/src/unistd/pread_pwrite_test.cpp
+++ b/libc/test/src/unistd/pread_pwrite_test.cpp
@@ -74,6 +74,11 @@ TEST_F(LlvmLibcUniStd, PWriteFails) {
TEST_F(LlvmLibcUniStd, PReadFails) {
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+#ifdef LIBC_TEST_UNDER_EMULATOR
+ // QEMU returns EFAULT instead of EBADF when reading into a nullptr.
char buf[1];
EXPECT_THAT(LIBC_NAMESPACE::pread(-1, buf, 1, 0), Fails<ssize_t>(EBADF));
+#else
+ EXPECT_THAT(LIBC_NAMESPACE::pread(-1, nullptr, 1, 0), Fails<ssize_t>(EBADF));
+#endif
}
diff --git a/libc/test/src/unistd/read_write_test.cpp b/libc/test/src/unistd/read_write_test.cpp
index 39953a5cd43d6..3fd9c5047fb5d 100644
--- a/libc/test/src/unistd/read_write_test.cpp
+++ b/libc/test/src/unistd/read_write_test.cpp
@@ -58,8 +58,13 @@ TEST_F(LlvmLibcUniStd, WriteFails) {
TEST_F(LlvmLibcUniStd, ReadFails) {
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+#ifdef LIBC_TEST_UNDER_EMULATOR
+ // QEMU returns EFAULT instead of EBADF when reading into a nullptr.
char buf[1];
EXPECT_THAT(LIBC_NAMESPACE::read(-1, buf, 1), Fails<ssize_t>(EBADF));
+#else
+ EXPECT_THAT(LIBC_NAMESPACE::read(-1, nullptr, 1), Fails<ssize_t>(EBADF));
+#endif
EXPECT_THAT(LIBC_NAMESPACE::read(0, reinterpret_cast<void *>(-1), 1),
Fails<ssize_t>(EFAULT));
}
More information about the libc-commits
mailing list