mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-05-22 22:32:11 +08:00
test(windows): add shim runtime coverage
Add focused tests for Windows shim headers, file and poll behavior, runtime helpers, and main-output silence so native Windows unit jobs exercise the compatibility layer directly.
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
############################################################################
|
||||
#
|
||||
# Copyright (c) 2026 PX4 Development Team. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# 3. Neither the name PX4 nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
#
|
||||
# Unit tests for the Windows POSIX shim layer. These targets only build
|
||||
# when CMAKE_TESTING is on (BUILD_TESTING is set by include(CTest)).
|
||||
# Tests are gated to Windows hosts because the shim headers are
|
||||
# Windows-only by design; on non-Windows the .cpp files compile to a
|
||||
# trivial sentinel test so the gtest runner still produces output.
|
||||
#
|
||||
|
||||
if(NOT BUILD_TESTING)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Shared static-init hook that silences the MSVC Debug CRT invalid
|
||||
# parameter handler so tests that exercise bad-fd / bad-handle paths do
|
||||
# not abort with a debug-time assertion dialog. Linked into every
|
||||
# Windows shim test target as an EXTRA_SRC.
|
||||
set(_px4_windows_shim_silencer
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_main_silence.cpp
|
||||
)
|
||||
|
||||
# Headers-only tests (libgen, dirent, sched, syslog, mman, time, unistd,
|
||||
# getopt, fcntl). The translation unit pulls the shim headers via the
|
||||
# existing windows_shim include path; the inline fcntl() routes
|
||||
# O_NONBLOCK changes through ioctlsocket() so we need ws2_32 to satisfy
|
||||
# the symbol even on the headers-only target.
|
||||
px4_add_unit_gtest(
|
||||
SRC test_windows_shim_headers.cpp
|
||||
EXTRA_SRCS ${_px4_windows_shim_silencer}
|
||||
LINKLIBS ws2_32
|
||||
)
|
||||
|
||||
# Source-defined shim tests (errno_map, env, sysconf, mman, flock, dlfcn,
|
||||
# ids, if_query, resolver, socket loopback). Pulls in the standalone
|
||||
# Windows shim sources directly so the tests do not need the full PX4
|
||||
# platform/uORB stack.
|
||||
set(_px4_windows_shim_root ${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||||
|
||||
set(_px4_windows_shim_test_srcs
|
||||
${_px4_windows_shim_root}/posix/sys/errno_map.cpp
|
||||
${_px4_windows_shim_root}/posix/sys/sysconf.cpp
|
||||
${_px4_windows_shim_root}/posix/proc/env.cpp
|
||||
${_px4_windows_shim_root}/posix/proc/ids.cpp
|
||||
${_px4_windows_shim_root}/posix/fs/flock.cpp
|
||||
${_px4_windows_shim_root}/posix/fs/mman.cpp
|
||||
${_px4_windows_shim_root}/posix/lib/dlfcn.cpp
|
||||
${_px4_windows_shim_root}/posix/net/if_query.cpp
|
||||
${_px4_windows_shim_root}/posix/net/resolver.cpp
|
||||
)
|
||||
|
||||
px4_add_unit_gtest(
|
||||
SRC test_windows_shim_runtime.cpp
|
||||
EXTRA_SRCS ${_px4_windows_shim_test_srcs} ${_px4_windows_shim_silencer}
|
||||
INCLUDES
|
||||
${PX4_SOURCE_DIR}/platforms/posix/include/windows_shim
|
||||
${_px4_windows_shim_root}/include
|
||||
LINKLIBS ws2_32 iphlpapi
|
||||
)
|
||||
|
||||
# poll() inline implementation - header-only test.
|
||||
px4_add_unit_gtest(
|
||||
SRC test_windows_shim_poll.cpp
|
||||
EXTRA_SRCS ${_px4_windows_shim_silencer}
|
||||
INCLUDES
|
||||
${PX4_SOURCE_DIR}/platforms/posix/include/windows_shim
|
||||
LINKLIBS ws2_32
|
||||
)
|
||||
|
||||
# I/O helpers (pipe, fsync, dprintf, mkdir, lstat). Header-only; the
|
||||
# POSIX names route to MSVC CRT primitives via inline shim wrappers.
|
||||
px4_add_unit_gtest(
|
||||
SRC test_windows_shim_io.cpp
|
||||
EXTRA_SRCS ${_px4_windows_shim_silencer}
|
||||
INCLUDES
|
||||
${PX4_SOURCE_DIR}/platforms/posix/include/windows_shim
|
||||
)
|
||||
@@ -0,0 +1,67 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (C) 2026 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file test_main_silence.cpp
|
||||
*
|
||||
* Suppress the MSVC Debug CRT invalid-parameter handler so unit tests that
|
||||
* intentionally exercise bad-fd / bad-handle paths (e.g. fcntl(0, 0xBAD),
|
||||
* flock(-1, ...), _open on a closed fd) do not abort with a debug-time
|
||||
* assertion dialog. Without this hook the Debug CRT raises Watson and
|
||||
* terminates the process before gtest can record the EXPECT_* result.
|
||||
*
|
||||
* Linked into every shim unit test target via
|
||||
* platforms/posix/src/px4/windows/tests/CMakeLists.txt; takes effect at
|
||||
* static-init time before main() runs gtest.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <stdlib.h>
|
||||
#include <crtdbg.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
static void silent_invalid_param(const wchar_t *, const wchar_t *, const wchar_t *,
|
||||
unsigned, uintptr_t) {}
|
||||
struct InstallSilencer {
|
||||
InstallSilencer()
|
||||
{
|
||||
_set_invalid_parameter_handler(silent_invalid_param);
|
||||
_CrtSetReportMode(_CRT_ASSERT, 0);
|
||||
_CrtSetReportMode(_CRT_ERROR, 0);
|
||||
_CrtSetReportMode(_CRT_WARN, 0);
|
||||
}
|
||||
};
|
||||
static InstallSilencer kInstallSilencer;
|
||||
} // namespace
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,224 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (C) 2026 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file test_windows_shim_io.cpp
|
||||
*
|
||||
* Unit tests for the inline I/O helpers in unistd.h and sys/stat.h:
|
||||
* pipe(), pipe2(), fsync(), fdatasync(), dprintf(), vdprintf(),
|
||||
* px4_mkdir_shim() / mkdir(), and lstat() (the latter is implemented
|
||||
* as a stat() pass-through).
|
||||
*
|
||||
* These all live in the shim headers and so are unit-testable without
|
||||
* pulling in the PX4 platform stack.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
TEST(WindowsShimIo, PipeCreatesUsableFds)
|
||||
{
|
||||
int p[2] = { -1, -1 };
|
||||
ASSERT_EQ(pipe(p), 0);
|
||||
EXPECT_GE(p[0], 0);
|
||||
EXPECT_GE(p[1], 0);
|
||||
const char msg[] = "round";
|
||||
ASSERT_EQ(_write(p[1], msg, (unsigned)sizeof(msg)), (int)sizeof(msg));
|
||||
char buf[16] = {};
|
||||
ASSERT_EQ(_read(p[0], buf, sizeof(buf)), (int)sizeof(msg));
|
||||
EXPECT_STREQ(buf, "round");
|
||||
_close(p[0]);
|
||||
_close(p[1]);
|
||||
}
|
||||
|
||||
TEST(WindowsShimIo, Pipe2IgnoresFlags)
|
||||
{
|
||||
int p[2] = { -1, -1 };
|
||||
/* Flags are documented as ignored on Windows. */
|
||||
ASSERT_EQ(pipe2(p, 0xDEADBEEF), 0);
|
||||
EXPECT_GE(p[0], 0);
|
||||
EXPECT_GE(p[1], 0);
|
||||
_close(p[0]);
|
||||
_close(p[1]);
|
||||
}
|
||||
|
||||
TEST(WindowsShimIo, FsyncOnRegularFile)
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
GetTempPathA(sizeof(path), path);
|
||||
strcat_s(path, sizeof(path), "px4_fsync.dat");
|
||||
int fd = _open(path, _O_CREAT | _O_RDWR | _O_BINARY, _S_IREAD | _S_IWRITE);
|
||||
ASSERT_GE(fd, 0);
|
||||
const char data[] = "hello";
|
||||
ASSERT_EQ(_write(fd, data, (unsigned)sizeof(data)), (int)sizeof(data));
|
||||
EXPECT_EQ(fsync(fd), 0);
|
||||
EXPECT_EQ(fdatasync(fd), 0);
|
||||
_close(fd);
|
||||
DeleteFileA(path);
|
||||
}
|
||||
|
||||
TEST(WindowsShimIo, FsyncBadFdReturnsError)
|
||||
{
|
||||
errno = 0;
|
||||
EXPECT_NE(fsync(-1), 0);
|
||||
}
|
||||
|
||||
TEST(WindowsShimIo, DprintfWritesFormatted)
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
GetTempPathA(sizeof(path), path);
|
||||
strcat_s(path, sizeof(path), "px4_dprintf.txt");
|
||||
int fd = _open(path, _O_CREAT | _O_RDWR | _O_BINARY | _O_TRUNC,
|
||||
_S_IREAD | _S_IWRITE);
|
||||
ASSERT_GE(fd, 0);
|
||||
int n = dprintf(fd, "v=%d s=%s", 42, "ok");
|
||||
EXPECT_GT(n, 0);
|
||||
_lseek(fd, 0, SEEK_SET);
|
||||
char buf[64] = {};
|
||||
int r = _read(fd, buf, sizeof(buf) - 1);
|
||||
ASSERT_GT(r, 0);
|
||||
buf[r] = 0;
|
||||
EXPECT_STREQ(buf, "v=42 s=ok");
|
||||
_close(fd);
|
||||
DeleteFileA(path);
|
||||
}
|
||||
|
||||
static int call_vdprintf(int fd, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int n = vdprintf(fd, fmt, ap);
|
||||
va_end(ap);
|
||||
return n;
|
||||
}
|
||||
|
||||
TEST(WindowsShimIo, VdprintfWritesFormatted)
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
GetTempPathA(sizeof(path), path);
|
||||
strcat_s(path, sizeof(path), "px4_vdprintf.txt");
|
||||
int fd = _open(path, _O_CREAT | _O_RDWR | _O_BINARY | _O_TRUNC,
|
||||
_S_IREAD | _S_IWRITE);
|
||||
ASSERT_GE(fd, 0);
|
||||
int n = call_vdprintf(fd, "x=%d", 7);
|
||||
EXPECT_GT(n, 0);
|
||||
_lseek(fd, 0, SEEK_SET);
|
||||
char buf[16] = {};
|
||||
int r = _read(fd, buf, sizeof(buf) - 1);
|
||||
ASSERT_GT(r, 0);
|
||||
buf[r] = 0;
|
||||
EXPECT_STREQ(buf, "x=7");
|
||||
_close(fd);
|
||||
DeleteFileA(path);
|
||||
}
|
||||
|
||||
TEST(WindowsShimIo, MkdirCreatesDirectory)
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
GetTempPathA(sizeof(path), path);
|
||||
strcat_s(path, sizeof(path), "px4_mkdir_shim_test");
|
||||
RemoveDirectoryA(path); /* tidy from previous run */
|
||||
EXPECT_EQ(mkdir(path, 0755), 0);
|
||||
struct stat st = {};
|
||||
ASSERT_EQ(stat(path, &st), 0);
|
||||
EXPECT_TRUE(S_ISDIR(st.st_mode));
|
||||
RemoveDirectoryA(path);
|
||||
}
|
||||
|
||||
TEST(WindowsShimIo, MkdirExistingFails)
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
GetTempPathA(sizeof(path), path);
|
||||
strcat_s(path, sizeof(path), "px4_mkdir_shim_test2");
|
||||
RemoveDirectoryA(path);
|
||||
ASSERT_EQ(mkdir(path, 0755), 0);
|
||||
EXPECT_NE(mkdir(path, 0755), 0);
|
||||
RemoveDirectoryA(path);
|
||||
}
|
||||
|
||||
TEST(WindowsShimIo, LstatBehavesLikeStatForFile)
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
GetTempPathA(sizeof(path), path);
|
||||
strcat_s(path, sizeof(path), "px4_lstat.dat");
|
||||
int fd = _open(path, _O_CREAT | _O_RDWR | _O_BINARY, _S_IREAD | _S_IWRITE);
|
||||
ASSERT_GE(fd, 0);
|
||||
_close(fd);
|
||||
|
||||
struct stat a = {};
|
||||
struct stat b = {};
|
||||
ASSERT_EQ(lstat(path, &a), 0);
|
||||
ASSERT_EQ(stat(path, &b), 0);
|
||||
EXPECT_EQ(a.st_size, b.st_size);
|
||||
EXPECT_TRUE(S_ISREG(a.st_mode));
|
||||
/* lstat() never reports a symlink on Windows. */
|
||||
EXPECT_FALSE(S_ISLNK(a.st_mode));
|
||||
DeleteFileA(path);
|
||||
}
|
||||
|
||||
TEST(WindowsShimIo, LstatNonexistentFails)
|
||||
{
|
||||
struct stat st = {};
|
||||
EXPECT_NE(lstat("Z:\\definitely\\not\\here\\px4test", &st), 0);
|
||||
}
|
||||
|
||||
TEST(WindowsShimIo, StModeBitsExpose)
|
||||
{
|
||||
/* Smoke test the POSIX permission constants the shim exports. */
|
||||
EXPECT_EQ(S_IRWXU, 0700);
|
||||
EXPECT_EQ(S_IRUSR, 0400);
|
||||
EXPECT_EQ(S_IWUSR, 0200);
|
||||
EXPECT_EQ(S_IXUSR, 0100);
|
||||
EXPECT_EQ(S_IRWXG, 0070);
|
||||
EXPECT_EQ(S_IRWXO, 0007);
|
||||
EXPECT_EQ(S_IFDIR, 0040000);
|
||||
EXPECT_EQ(S_IFREG, 0100000);
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
TEST(WindowsShimIo, BuildSentinel)
|
||||
{
|
||||
SUCCEED();
|
||||
}
|
||||
@@ -0,0 +1,417 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (C) 2026 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file test_windows_shim_poll.cpp
|
||||
*
|
||||
* Unit tests for the inline poll() implementation in
|
||||
* platforms/posix/include/windows_shim/poll.h. The shim classifies fd
|
||||
* kinds (socket / pipe / char / disk / invalid), routes pure-socket
|
||||
* calls through WSAPoll, and otherwise mixes manual readiness probes
|
||||
* with a tight WSAPoll fallback for the socket subset.
|
||||
*
|
||||
* Each branch is exercised: argument validation, socket loopback, pipe
|
||||
* with and without pending data, disk-fd always-ready, ignored fd, and
|
||||
* the timeout/timeout=0 fast paths.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define _WIN32_WINNT 0x0A00
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <chrono>
|
||||
|
||||
#include <poll.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
class WinsockBootstrap
|
||||
{
|
||||
public:
|
||||
WinsockBootstrap()
|
||||
{
|
||||
WSADATA wsa;
|
||||
WSAStartup(MAKEWORD(2, 2), &wsa);
|
||||
}
|
||||
|
||||
~WinsockBootstrap()
|
||||
{
|
||||
WSACleanup();
|
||||
}
|
||||
};
|
||||
|
||||
/* Lightweight UDP loopback fixture used by socket-path tests. */
|
||||
struct LoopbackPair {
|
||||
WinsockBootstrap _ws;
|
||||
SOCKET srv = INVALID_SOCKET;
|
||||
SOCKET cli = INVALID_SOCKET;
|
||||
struct sockaddr_in srv_addr {};
|
||||
|
||||
LoopbackPair()
|
||||
{
|
||||
srv = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
cli = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
srv_addr.sin_family = AF_INET;
|
||||
srv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
srv_addr.sin_port = 0;
|
||||
bind(srv, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
|
||||
int alen = sizeof(srv_addr);
|
||||
getsockname(srv, (struct sockaddr *)&srv_addr, &alen);
|
||||
}
|
||||
|
||||
~LoopbackPair()
|
||||
{
|
||||
if (srv != INVALID_SOCKET) { closesocket(srv); }
|
||||
|
||||
if (cli != INVALID_SOCKET) { closesocket(cli); }
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/* Argument validation -------------------------------------------------- */
|
||||
|
||||
TEST(WindowsShimPoll, NegativeTimeoutBelowMinusOneRejected)
|
||||
{
|
||||
struct pollfd fd;
|
||||
fd.fd = -1;
|
||||
fd.events = 0;
|
||||
fd.revents = 0;
|
||||
errno = 0;
|
||||
int rc = poll(&fd, 1, -42);
|
||||
EXPECT_EQ(rc, -1);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, NullFdsWithCountRejected)
|
||||
{
|
||||
errno = 0;
|
||||
int rc = poll(nullptr, 1, 0);
|
||||
EXPECT_EQ(rc, -1);
|
||||
EXPECT_EQ(errno, EFAULT);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, ZeroNfdsZeroTimeoutReturnsZero)
|
||||
{
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
int rc = poll(nullptr, 0, 0);
|
||||
EXPECT_EQ(rc, 0);
|
||||
auto dt = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - t0).count();
|
||||
EXPECT_LT(dt, 50);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, ZeroNfdsPositiveTimeoutSleeps)
|
||||
{
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
int rc = poll(nullptr, 0, 30);
|
||||
EXPECT_EQ(rc, 0);
|
||||
auto dt = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - t0).count();
|
||||
EXPECT_GE(dt, 25);
|
||||
}
|
||||
|
||||
/* fd-classification helpers -------------------------------------------- */
|
||||
|
||||
TEST(WindowsShimPoll, IgnoredFdHelper)
|
||||
{
|
||||
EXPECT_NE(px4_windows_poll_fd_ignored((SOCKET) - 1), 0);
|
||||
EXPECT_NE(px4_windows_poll_fd_ignored((SOCKET) - 5), 0);
|
||||
EXPECT_EQ(px4_windows_poll_fd_ignored((SOCKET)0), 0);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, ErrnoFromWsaMapping)
|
||||
{
|
||||
EXPECT_EQ(px4_windows_poll_errno_from_wsa(WSAEINTR), EINTR);
|
||||
EXPECT_EQ(px4_windows_poll_errno_from_wsa(WSAEINVAL), EINVAL);
|
||||
EXPECT_EQ(px4_windows_poll_errno_from_wsa(WSAENOBUFS), ENOMEM);
|
||||
EXPECT_EQ(px4_windows_poll_errno_from_wsa(0xBEEF), EBADF);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, ReadyEventsMask)
|
||||
{
|
||||
EXPECT_EQ(px4_windows_poll_ready_events(POLLIN, POLLIN | POLLOUT), POLLIN);
|
||||
EXPECT_EQ(px4_windows_poll_ready_events(POLLIN, POLLOUT), 0);
|
||||
EXPECT_EQ(px4_windows_poll_ready_events(0, POLLIN), 0);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, DiskReventsAlwaysReady)
|
||||
{
|
||||
EXPECT_EQ(px4_windows_poll_disk_revents(POLLIN), POLLIN);
|
||||
EXPECT_EQ(px4_windows_poll_disk_revents(POLLOUT), POLLOUT);
|
||||
EXPECT_EQ(px4_windows_poll_disk_revents(0), 0);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, ClassifyIgnoredFd)
|
||||
{
|
||||
HANDLE h = nullptr;
|
||||
DWORD ft = 0;
|
||||
enum px4_windows_poll_fd_kind k =
|
||||
px4_windows_poll_classify_fd((SOCKET) - 1, &h, &ft);
|
||||
EXPECT_EQ(k, PX4_WINDOWS_POLL_FD_IGNORED);
|
||||
EXPECT_EQ(h, INVALID_HANDLE_VALUE);
|
||||
EXPECT_EQ(ft, FILE_TYPE_UNKNOWN);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, ClassifyInvalidFd)
|
||||
{
|
||||
WinsockBootstrap _ws;
|
||||
HANDLE h = nullptr;
|
||||
DWORD ft = 0;
|
||||
/* A very high CRT-fd-domain value with no backing handle. */
|
||||
enum px4_windows_poll_fd_kind k =
|
||||
px4_windows_poll_classify_fd((SOCKET)999999, &h, &ft);
|
||||
EXPECT_EQ(k, PX4_WINDOWS_POLL_FD_INVALID);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, ClassifyPipeFd)
|
||||
{
|
||||
int p[2] = { -1, -1 };
|
||||
ASSERT_EQ(_pipe(p, 4096, _O_BINARY), 0);
|
||||
HANDLE h = nullptr;
|
||||
DWORD ft = 0;
|
||||
enum px4_windows_poll_fd_kind k =
|
||||
px4_windows_poll_classify_fd((SOCKET)p[0], &h, &ft);
|
||||
EXPECT_EQ(k, PX4_WINDOWS_POLL_FD_PIPE);
|
||||
EXPECT_NE(h, INVALID_HANDLE_VALUE);
|
||||
EXPECT_EQ(ft, (DWORD)FILE_TYPE_PIPE);
|
||||
_close(p[0]);
|
||||
_close(p[1]);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, ClassifyDiskFd)
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
GetTempPathA(sizeof(path), path);
|
||||
strcat_s(path, sizeof(path), "px4_poll_test.dat");
|
||||
int fd = _open(path, _O_CREAT | _O_RDWR | _O_BINARY, _S_IREAD | _S_IWRITE);
|
||||
ASSERT_GE(fd, 0);
|
||||
HANDLE h = nullptr;
|
||||
DWORD ft = 0;
|
||||
enum px4_windows_poll_fd_kind k =
|
||||
px4_windows_poll_classify_fd((SOCKET)fd, &h, &ft);
|
||||
EXPECT_EQ(k, PX4_WINDOWS_POLL_FD_DISK);
|
||||
EXPECT_EQ(ft, (DWORD)FILE_TYPE_DISK);
|
||||
_close(fd);
|
||||
DeleteFileA(path);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, ClassifySocketFd)
|
||||
{
|
||||
WinsockBootstrap _ws;
|
||||
SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
ASSERT_NE(s, INVALID_SOCKET);
|
||||
HANDLE h = nullptr;
|
||||
DWORD ft = 0;
|
||||
enum px4_windows_poll_fd_kind k =
|
||||
px4_windows_poll_classify_fd(s, &h, &ft);
|
||||
EXPECT_EQ(k, PX4_WINDOWS_POLL_FD_SOCKET);
|
||||
EXPECT_EQ(h, INVALID_HANDLE_VALUE);
|
||||
closesocket(s);
|
||||
}
|
||||
|
||||
/* poll() integration --------------------------------------------------- */
|
||||
|
||||
TEST(WindowsShimPoll, OnlyIgnoredFdShortCircuitTimeoutZero)
|
||||
{
|
||||
struct pollfd fds[2];
|
||||
fds[0].fd = -1;
|
||||
fds[0].events = POLLIN;
|
||||
fds[0].revents = 0xff;
|
||||
fds[1].fd = -1;
|
||||
fds[1].events = POLLOUT;
|
||||
fds[1].revents = 0xff;
|
||||
int rc = poll(fds, 2, 0);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_EQ(fds[0].revents, 0);
|
||||
EXPECT_EQ(fds[1].revents, 0);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, SocketWriteAlwaysReady)
|
||||
{
|
||||
LoopbackPair pair;
|
||||
struct pollfd pf;
|
||||
pf.fd = (int)pair.cli;
|
||||
pf.events = POLLOUT;
|
||||
pf.revents = 0;
|
||||
int rc = poll(&pf, 1, 0);
|
||||
EXPECT_GE(rc, 0);
|
||||
/* Writable on a fresh UDP socket. */
|
||||
EXPECT_TRUE((pf.revents & POLLOUT) || rc == 0);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, SocketReadReadyAfterSendto)
|
||||
{
|
||||
LoopbackPair pair;
|
||||
const char msg[] = "x";
|
||||
ASSERT_EQ(sendto(pair.cli, msg, (int)sizeof(msg), 0,
|
||||
(struct sockaddr *)&pair.srv_addr, sizeof(pair.srv_addr)),
|
||||
(int)sizeof(msg));
|
||||
/* Give the kernel a moment to deliver. */
|
||||
struct pollfd pf;
|
||||
pf.fd = (int)pair.srv;
|
||||
pf.events = POLLIN;
|
||||
pf.revents = 0;
|
||||
int rc = poll(&pf, 1, 200);
|
||||
EXPECT_EQ(rc, 1);
|
||||
EXPECT_TRUE((pf.revents & POLLIN) != 0);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, SocketTimeoutNoData)
|
||||
{
|
||||
LoopbackPair pair;
|
||||
struct pollfd pf;
|
||||
pf.fd = (int)pair.srv;
|
||||
pf.events = POLLIN;
|
||||
pf.revents = 0;
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
int rc = poll(&pf, 1, 30);
|
||||
EXPECT_EQ(rc, 0);
|
||||
auto dt = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - t0).count();
|
||||
EXPECT_GE(dt, 20);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, PipeWritableImmediately)
|
||||
{
|
||||
int p[2] = { -1, -1 };
|
||||
ASSERT_EQ(_pipe(p, 4096, _O_BINARY), 0);
|
||||
struct pollfd pf;
|
||||
pf.fd = p[1];
|
||||
pf.events = POLLOUT;
|
||||
pf.revents = 0;
|
||||
int rc = poll(&pf, 1, 0);
|
||||
EXPECT_EQ(rc, 1);
|
||||
EXPECT_TRUE((pf.revents & POLLOUT) != 0);
|
||||
_close(p[0]);
|
||||
_close(p[1]);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, PipeReadableAfterWrite)
|
||||
{
|
||||
int p[2] = { -1, -1 };
|
||||
ASSERT_EQ(_pipe(p, 4096, _O_BINARY), 0);
|
||||
const char msg[] = "ping";
|
||||
ASSERT_EQ(_write(p[1], msg, (unsigned)sizeof(msg)), (int)sizeof(msg));
|
||||
struct pollfd pf;
|
||||
pf.fd = p[0];
|
||||
pf.events = POLLIN;
|
||||
pf.revents = 0;
|
||||
int rc = poll(&pf, 1, 200);
|
||||
EXPECT_EQ(rc, 1);
|
||||
EXPECT_TRUE((pf.revents & POLLIN) != 0);
|
||||
_close(p[0]);
|
||||
_close(p[1]);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, PipeBrokenSetsHup)
|
||||
{
|
||||
int p[2] = { -1, -1 };
|
||||
ASSERT_EQ(_pipe(p, 4096, _O_BINARY), 0);
|
||||
_close(p[1]);
|
||||
struct pollfd pf;
|
||||
pf.fd = p[0];
|
||||
pf.events = POLLIN;
|
||||
pf.revents = 0;
|
||||
int rc = poll(&pf, 1, 50);
|
||||
EXPECT_GE(rc, 1);
|
||||
EXPECT_TRUE((pf.revents & (POLLHUP | POLLIN)) != 0);
|
||||
_close(p[0]);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, DiskFdAlwaysReady)
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
GetTempPathA(sizeof(path), path);
|
||||
strcat_s(path, sizeof(path), "px4_poll_disk.dat");
|
||||
int fd = _open(path, _O_CREAT | _O_RDWR | _O_BINARY, _S_IREAD | _S_IWRITE);
|
||||
ASSERT_GE(fd, 0);
|
||||
struct pollfd pf;
|
||||
pf.fd = fd;
|
||||
pf.events = POLLIN | POLLOUT;
|
||||
pf.revents = 0;
|
||||
int rc = poll(&pf, 1, 0);
|
||||
EXPECT_EQ(rc, 1);
|
||||
EXPECT_TRUE((pf.revents & POLLIN) != 0);
|
||||
EXPECT_TRUE((pf.revents & POLLOUT) != 0);
|
||||
_close(fd);
|
||||
DeleteFileA(path);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, MixedSocketAndPipe)
|
||||
{
|
||||
LoopbackPair pair;
|
||||
int p[2] = { -1, -1 };
|
||||
ASSERT_EQ(_pipe(p, 4096, _O_BINARY), 0);
|
||||
const char msg[] = "z";
|
||||
ASSERT_EQ(_write(p[1], msg, (unsigned)sizeof(msg)), (int)sizeof(msg));
|
||||
struct pollfd fds[2];
|
||||
fds[0].fd = p[0];
|
||||
fds[0].events = POLLIN;
|
||||
fds[0].revents = 0;
|
||||
fds[1].fd = (int)pair.srv;
|
||||
fds[1].events = POLLIN;
|
||||
fds[1].revents = 0;
|
||||
int rc = poll(fds, 2, 100);
|
||||
EXPECT_GE(rc, 1);
|
||||
EXPECT_TRUE((fds[0].revents & POLLIN) != 0);
|
||||
_close(p[0]);
|
||||
_close(p[1]);
|
||||
}
|
||||
|
||||
TEST(WindowsShimPoll, InvalidFdReturnsPollnval)
|
||||
{
|
||||
struct pollfd pf;
|
||||
pf.fd = 999999; /* deliberately bogus */
|
||||
pf.events = POLLIN;
|
||||
pf.revents = 0;
|
||||
int rc = poll(&pf, 1, 0);
|
||||
EXPECT_GE(rc, 1);
|
||||
EXPECT_TRUE((pf.revents & POLLNVAL) != 0);
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
TEST(WindowsShimPoll, BuildSentinel)
|
||||
{
|
||||
SUCCEED();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user