feat(headers): stddef/stdint/stdbool/limits/float/assert + features wiring
This commit is contained in:
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* vlibc — public header skeleton test (todo 5).
|
||||
*
|
||||
* Exercises the full public header inventory in one translation unit:
|
||||
*
|
||||
* 1. compile-time: every new header plus the scaffold headers is included
|
||||
* TWICE, so the include guards are proven to tolerate re-inclusion in
|
||||
* any order; static_asserts pin the type widths and the standard macro
|
||||
* values;
|
||||
* 2. runtime: a happy-path main() checks the type sizes, the C23 keywords
|
||||
* the headers provide (nullptr, alignas/alignof, iso646 spellings),
|
||||
* stdatomic, stdbit, stdckdint and stdarg behavior end to end;
|
||||
* 3. failure mode (-f): assert(0) must write the diagnostic to fd 2 and
|
||||
* terminate with SIGILL — __vlibc_assert_fail() ends in
|
||||
* __builtin_trap(), so the harness expects exit status 132 (128 + 4).
|
||||
* When built with -DNDEBUG the -f run prints that assert was swallowed
|
||||
* and exits 0 instead.
|
||||
*
|
||||
* All output goes through the raw SYS_write layer, like tests/syscall_test.c:
|
||||
* no host <stdio.h> is included, so the TU exercises only vlibc's own
|
||||
* headers through -Iinclude and stays fully self-contained.
|
||||
*
|
||||
* Not part of the library proper; compiled manually for this todo (the
|
||||
* tests/ + make check wiring is owned by a later todo).
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <cpio.h>
|
||||
#include <float.h>
|
||||
#include <iso646.h>
|
||||
#include <limits.h>
|
||||
#include <stdalign.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdbit.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdckdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdnoreturn.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <tar.h>
|
||||
#include <tgmath.h>
|
||||
#include <threads.h>
|
||||
#include <uchar.h>
|
||||
#include <vlibc.h>
|
||||
|
||||
/* Second round: the guards must make every re-inclusion a no-op. */
|
||||
#include <assert.h>
|
||||
#include <cpio.h>
|
||||
#include <float.h>
|
||||
#include <iso646.h>
|
||||
#include <limits.h>
|
||||
#include <stdalign.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdbit.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdckdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdnoreturn.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <tar.h>
|
||||
#include <tgmath.h>
|
||||
#include <threads.h>
|
||||
#include <uchar.h>
|
||||
#include <vlibc.h>
|
||||
|
||||
#include "../src/internal/syscall.h"
|
||||
|
||||
/* ---- Compile-time gates (features.h wiring) ---- */
|
||||
|
||||
#if !VLIBC_HAS_POSIX
|
||||
#error "VLIBC_HAS_POSIX must be 1 at level 1"
|
||||
#endif
|
||||
#if !defined(VLIBC_HAS_HEADER_SYS_TYPES_H) || !defined(VLIBC_HAS_HEADER_UCHAR_H) || \
|
||||
!defined(VLIBC_HAS_HEADER_CPIO_H) || !defined(VLIBC_HAS_HEADER_TAR_H) || \
|
||||
!defined(VLIBC_HAS_HEADER_THREADS_H)
|
||||
#error "level-1 header gates missing from features.h"
|
||||
#endif
|
||||
|
||||
/* ---- Compile-time checks: stdint.h / limits.h / float.h ---- */
|
||||
|
||||
static_assert(sizeof(int8_t) == 1 && sizeof(uint8_t) == 1, "8-bit exact types");
|
||||
static_assert(sizeof(int16_t) == 2 && sizeof(uint16_t) == 2, "16-bit exact types");
|
||||
static_assert(sizeof(int32_t) == 4 && sizeof(uint32_t) == 4, "32-bit exact types");
|
||||
static_assert(sizeof(int64_t) == 8 && sizeof(uint64_t) == 8, "64-bit exact types");
|
||||
static_assert(sizeof(intptr_t) == sizeof(void *) && sizeof(uintptr_t) == sizeof(void *),
|
||||
"pointer-width types");
|
||||
static_assert(sizeof(intmax_t) == 8 && sizeof(uintmax_t) == 8, "max-width types");
|
||||
static_assert(INT64_WIDTH == 64 && UINT64_WIDTH == 64, "exact widths");
|
||||
static_assert(SIZE_WIDTH == 64 && PTRDIFF_WIDTH == 64, "pointer-class widths");
|
||||
static_assert(WCHAR_WIDTH == 32, "wchar_t is 32-bit int on x86_64");
|
||||
static_assert(INT32_MAX == 2147483647 && INT32_MIN == (-2147483647 - 1), "int32 limits");
|
||||
static_assert(UINT32_MAX == 4294967295U, "uint32 limit");
|
||||
static_assert(INT64_MAX == INT64_C(0x7fffffffffffffff), "INT64_C produces long");
|
||||
static_assert(UINT64_MAX == UINT64_C(0xffffffffffffffff), "UINT64_C produces unsigned long");
|
||||
static_assert(CHAR_BIT == 8 && SCHAR_MIN == -128 && UCHAR_MAX == 255, "char limits");
|
||||
static_assert(SHRT_MAX == 32767 && USHRT_MAX == 65535, "short limits");
|
||||
static_assert(INT_MAX == 2147483647 && UINT_MAX == 4294967295U, "int limits");
|
||||
static_assert(LONG_MAX == 0x7fffffffffffffffL && ULONG_MAX == 0xffffffffffffffffUL,
|
||||
"long limits (LP64)");
|
||||
static_assert(LLONG_MAX == 0x7fffffffffffffffLL && ULLONG_MAX == 0xffffffffffffffffULL,
|
||||
"long long limits");
|
||||
static_assert(FLT_RADIX == 2, "binary floating point");
|
||||
static_assert(FLT_MANT_DIG == 24 && DBL_MANT_DIG == 53, "IEC 60559 mantissa widths");
|
||||
static_assert(FLT_ROUNDS == 1, "default rounding mode");
|
||||
static_assert(FLT_IS_IEC_60559 == 1 && DBL_IS_IEC_60559 == 1, "IEC 60559 types");
|
||||
|
||||
/* ---- Compile-time checks: keyword / feature-test plumbing ---- */
|
||||
|
||||
#ifndef __bool_true_false_are_defined
|
||||
#error "stdbool.h must define __bool_true_false_are_defined"
|
||||
#endif
|
||||
#ifndef __alignas_is_defined
|
||||
#error "stdalign.h must define __alignas_is_defined"
|
||||
#endif
|
||||
#ifndef __alignof_is_defined
|
||||
#error "stdalign.h must define __alignof_is_defined"
|
||||
#endif
|
||||
_Static_assert(sizeof(int) == 4, "_Static_assert stays usable alongside static_assert");
|
||||
|
||||
/* ---- Runtime harness ---- */
|
||||
|
||||
static int failures;
|
||||
|
||||
/* Write a NUL-terminated string to fd via the raw syscall layer. */
|
||||
static void
|
||||
say(int fd, const char *s)
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
while (s[n] != '\0')
|
||||
{
|
||||
n++;
|
||||
}
|
||||
(void)__syscall3(SYS_write, fd, (long)s, (long)n);
|
||||
}
|
||||
|
||||
static void
|
||||
check(int cond, const char *what)
|
||||
{
|
||||
if (!cond)
|
||||
{
|
||||
say(2, "FAIL: ");
|
||||
say(2, what);
|
||||
say(2, "\n");
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sum n int arguments via <stdarg.h>. */
|
||||
static int
|
||||
sum(int n, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int total = 0;
|
||||
int i;
|
||||
|
||||
va_start(ap, n);
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
total += va_arg(ap, int);
|
||||
}
|
||||
va_end(ap);
|
||||
return total;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'f')
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
say(1, "NDEBUG: assert(0) swallowed\n");
|
||||
return 0;
|
||||
#else
|
||||
say(1, "about to assert(0): expect SIGILL\n");
|
||||
assert(0);
|
||||
return 1; /* unreachable: __vlibc_assert_fail traps */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* stddef.h: the C23 pieces. */
|
||||
{
|
||||
nullptr_t np = nullptr;
|
||||
struct of_test
|
||||
{
|
||||
char c;
|
||||
int i;
|
||||
};
|
||||
|
||||
check(sizeof(size_t) == 8 && sizeof(ptrdiff_t) == 8, "size_t/ptrdiff_t widths");
|
||||
check(sizeof(wchar_t) == 4, "wchar_t width");
|
||||
check(sizeof(nullptr_t) == sizeof(void *), "nullptr_t size");
|
||||
check(np == nullptr, "nullptr constant round-trip");
|
||||
check(offsetof(struct of_test, i) == 4, "offsetof");
|
||||
check(NULL == 0, "NULL");
|
||||
}
|
||||
|
||||
/* stdalign.h / C23 keywords. */
|
||||
{
|
||||
alignas(16) int aligned_obj;
|
||||
|
||||
check(alignof(double) >= 8, "alignof");
|
||||
check(((uintptr_t)&aligned_obj & 15) == 0, "alignas(16) honored");
|
||||
}
|
||||
|
||||
/* iso646.h: alternative spellings are macros in C23. */
|
||||
{
|
||||
int alt = (1 and 1) or 0;
|
||||
|
||||
check(alt == 1, "iso646 and/or");
|
||||
check(not(alt == 0), "iso646 not");
|
||||
}
|
||||
|
||||
/* stdbit.h: type-generic dispatch and per-width spellings. */
|
||||
check(stdc_leading_zeros(1u) == 31, "stdc_leading_zeros");
|
||||
check(stdc_leading_zeros_uc(0x80u) == 0, "stdc_leading_zeros_uc");
|
||||
check(stdc_leading_ones(0xffffffffu) == 32, "stdc_leading_ones full");
|
||||
check(stdc_trailing_zeros(0x100u) == 8, "stdc_trailing_zeros");
|
||||
check(stdc_trailing_ones(7u) == 3, "stdc_trailing_ones");
|
||||
check(stdc_count_ones(0xffu) == 8, "stdc_count_ones");
|
||||
check(stdc_count_zeros(0xffu) == 24, "stdc_count_zeros");
|
||||
check(stdc_has_single_bit(0x40u) == 1, "stdc_has_single_bit true");
|
||||
check(stdc_has_single_bit(0x41u) == 0, "stdc_has_single_bit false");
|
||||
check(stdc_bit_width(0u) == 0, "stdc_bit_width(0)");
|
||||
check(stdc_bit_width(0x40u) == 7, "stdc_bit_width");
|
||||
check(stdc_bit_floor(0x33u) == 0x20u, "stdc_bit_floor");
|
||||
check(stdc_bit_ceil(0x33u) == 0x40u, "stdc_bit_ceil");
|
||||
check(stdc_first_leading_one((unsigned char)0x80) == 0, "stdc_first_leading_one");
|
||||
check(stdc_first_leading_zero(0xfffffffeu) == 31, "stdc_first_leading_zero");
|
||||
check(stdc_first_trailing_one(0x80u) == 7, "stdc_first_trailing_one");
|
||||
check(stdc_first_trailing_zero(1u) == 1, "stdc_first_trailing_zero");
|
||||
|
||||
/* stdckdint.h: overflow detection and result preservation. */
|
||||
{
|
||||
unsigned int ures = 0;
|
||||
int sres = 0;
|
||||
|
||||
check(ckd_add(&ures, 0xffffffffu, 1u) == 1, "ckd_add detects overflow");
|
||||
check(ures == 0, "ckd_add leaves result unmodified on overflow");
|
||||
check(ckd_add(&ures, 1u, 2u) == 0 && ures == 3u, "ckd_add computes");
|
||||
check(ckd_mul(&sres, 100000, 100000) == 1, "ckd_mul detects overflow");
|
||||
check(ckd_sub(&ures, 5u, 7u) == 1, "ckd_sub detects underflow");
|
||||
}
|
||||
|
||||
/* stdatomic.h: the macro operations over GCC builtins. */
|
||||
{
|
||||
atomic_int counter = 7;
|
||||
atomic_flag flag = ATOMIC_FLAG_INIT;
|
||||
int expected;
|
||||
|
||||
check(atomic_load(&counter) == 7, "atomic_load");
|
||||
atomic_store(&counter, 5);
|
||||
check(atomic_load(&counter) == 5, "atomic_store");
|
||||
check(atomic_fetch_add(&counter, 2) == 5 && atomic_load(&counter) == 7, "atomic_fetch_add");
|
||||
check(atomic_exchange(&counter, 1) == 7, "atomic_exchange");
|
||||
expected = 1;
|
||||
check(atomic_compare_exchange_strong(&counter, &expected, 9) == 1 &&
|
||||
atomic_load(&counter) == 9,
|
||||
"compare_exchange success");
|
||||
expected = 3;
|
||||
check(atomic_compare_exchange_strong(&counter, &expected, 0) == 0 && expected == 9,
|
||||
"compare_exchange failure updates expected");
|
||||
check(atomic_flag_test_and_set(&flag) == 0, "atomic_flag initially clear");
|
||||
check(atomic_flag_test_and_set(&flag) == 1, "atomic_flag now set");
|
||||
atomic_flag_clear(&flag);
|
||||
check(atomic_flag_test_and_set(&flag) == 0, "atomic_flag cleared");
|
||||
check(atomic_is_lock_free(&counter) != 0, "atomic_int lock-free");
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
atomic_signal_fence(memory_order_relaxed);
|
||||
}
|
||||
|
||||
/* stdarg.h: variadic round-trip. */
|
||||
check(sum(3, 1, 2, 3) == 6, "stdarg sum");
|
||||
check(sum(0) == 0, "stdarg empty call");
|
||||
|
||||
/* assert.h happy path: a true assertion is a no-op. */
|
||||
assert(1);
|
||||
assert(sizeof(int) == 4);
|
||||
check(1, "assert(true) no-op");
|
||||
|
||||
/* uchar.h: C23 keyword types and the declared-only conversions. */
|
||||
check(sizeof(char16_t) == 2, "char16_t width");
|
||||
check(sizeof(char32_t) == 4, "char32_t width");
|
||||
{
|
||||
mbstate_t mbs;
|
||||
|
||||
mbs.state[0] = 0;
|
||||
mbs.state[1] = 0;
|
||||
check(mbs.state[0] == 0 && mbs.state[1] == 0, "mbstate_t usable");
|
||||
}
|
||||
|
||||
/* sys/types.h: the POSIX scalar set (x86_64 LP64). */
|
||||
static_assert(sizeof(ssize_t) == 8 && sizeof(off_t) == 8, "signed 64-bit types");
|
||||
static_assert(sizeof(time_t) == 8 && sizeof(clock_t) == 8, "time types");
|
||||
static_assert(sizeof(pid_t) == 4 && sizeof(uid_t) == 4 && sizeof(gid_t) == 4, "id types");
|
||||
static_assert(sizeof(mode_t) == 4 && sizeof(id_t) == 4 && sizeof(key_t) == 4,
|
||||
"mode/id/key types");
|
||||
static_assert(sizeof(dev_t) == 8 && sizeof(ino_t) == 8 && sizeof(nlink_t) == 8,
|
||||
"file identity types");
|
||||
static_assert(sizeof(blkcnt_t) == 8 && sizeof(blksize_t) == 8, "block types");
|
||||
static_assert(sizeof(suseconds_t) == 8 && sizeof(useconds_t) == 4, "usec types");
|
||||
|
||||
/* cpio.h / tar.h: the archive constants. */
|
||||
check(C_IRUSR == 0400 && C_IRGRP == 0040 && C_IROTH == 0004, "cpio permission bits");
|
||||
check(C_ISREG == 0100000 && C_ISDIR == 0040000 && C_ISLNK == 0120000, "cpio type bits");
|
||||
check(TMAGIC[0] == 'u' && TMAGIC[1] == 's' && TMAGLEN == 6, "tar magic");
|
||||
check(TVERSION[0] == '0' && TVERSLEN == 2, "tar version");
|
||||
check(REGTYPE == '0' && AREGTYPE == '\0' && LNKTYPE == '1' && DIRTYPE == '5', "tar typeflags");
|
||||
check(TSUID == 04000 && TUREAD == 00400 && TGREAD == 00040 && TOREAD == 00004, "tar mode bits");
|
||||
|
||||
/* threads.h stub: the types exist; the functions stay unimplemented. */
|
||||
{
|
||||
once_flag of = ONCE_FLAG_INIT;
|
||||
thrd_t t = 0;
|
||||
|
||||
check(of.opaque == 0 && t == 0, "threads.h stub types");
|
||||
check(TSS_DTOR_ITERATIONS == 4, "TSS_DTOR_ITERATIONS");
|
||||
}
|
||||
|
||||
if (failures > 0)
|
||||
{
|
||||
say(2, "FAILED\n");
|
||||
}
|
||||
return failures == 0 ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user