build: wire new src dirs, crt objects, and make check into Autotools

This commit is contained in:
2026-09-03 19:36:32 -04:00
parent 558cf8fd12
commit 40a392f454
8 changed files with 1889 additions and 9 deletions
+266
View File
@@ -0,0 +1,266 @@
#ifndef VLIBC_TEST_H
#define VLIBC_TEST_H
/*
* vlibc — shared test contract (todo 6).
*
* Every future per-function test (test_*.c) includes this header and gets:
*
* - TEST_ASSERT_EQ(actual, expected) integer equality, compared as
* signed 64-bit; on failure prints
* both values in hex
* - TEST_ASSERT_TRUE(cond) boolean check
* - TEST_ASSERT_STREQ(actual, expected) NUL-terminated string equality;
* NULL-safe (NULL equals only NULL)
* - TEST_ASSERT_NULL(p) pointer is NULL
* - TEST_MAIN(tests) a main() that runs the array of
* struct vlibc_test entries, prints
* one "RUN <name>: PASS|FAIL" line
* per test and a PASS/FAIL summary,
* and returns 0 when everything
* passed and 1 otherwise
*
* Contract:
* - Dependency-light: needs only vlibc's own <stddef.h> and the internal
* raw-syscall layer (src/internal/syscall.h, via a path-relative
* include). It deliberately includes NO host header and does NOT use
* vlibc stdio (which does not exist yet). All output goes through raw
* SYS_write to fd 1 (progress/summary) and fd 2 (assertion failures).
* - Compile tests with -Iinclude -std=c23 -fno-stack-protector
* -fno-pic -fno-pie (the make check harness does this; see Makefile.am)
* and link statically against the vlibc test archive.
* - A test function returns 0 on success, nonzero on failure; TEST_MAIN
* aggregates both the return values and any assertion failures.
* - The failure counter is per translation unit (one static int); keep
* each test binary a single TU.
* - Tests that need argc/argv (mode flags like -f) write their own
* main() and may still use the TEST_ASSERT_* macros.
* - One test function per checked behavior, named for the output.
* - All macro arguments are evaluated exactly once.
* - Do NOT include host headers in a TU that includes this file: the
* -Iinclude path shadows GCC's internal headers (see tests/test_strerror.c).
*/
#include <stddef.h>
#include "../../../src/internal/syscall.h"
/* One registered test: a name for the runner output and its run function. */
struct vlibc_test
{
const char *name;
int (*run)(void);
};
/* Assertion failures recorded in this translation unit. */
static int vlibc_test_failures;
/* ---- Raw output helpers (no stdio: raw SYS_write only) ---- */
static inline long
vlibc_test_strlen(const char *s)
{
long n = 0;
while (s[n] != '\0')
{
n++;
}
return n;
}
static inline void
vlibc_test_say(int fd, const char *s)
{
(void)__syscall3(SYS_write, fd, (long)s, vlibc_test_strlen(s));
}
/*
* The helpers below all take an (fd, value) or (value, name, file, line)
* argument shape whose order is a fixed diagnostic convention; the
* easily-swappable-parameters warning does not apply.
*/
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
static inline void
vlibc_test_say_dec(int fd, unsigned long v)
{
char buf[24];
int i = (int)sizeof(buf);
buf[--i] = '\0';
do
{
buf[--i] = (char)('0' + (v % 10));
v /= 10;
} while (v != 0);
(void)__syscall3(SYS_write, fd, (long)(buf + i), (long)(sizeof(buf) - 1 - i));
}
static inline void
vlibc_test_say_hex(int fd, unsigned long long v)
{
char buf[18];
int i = (int)sizeof(buf);
buf[--i] = '\0';
do
{
unsigned int d = (unsigned int)(v & 0xf);
buf[--i] = (char)(d < 10 ? '0' + d : 'a' + d - 10);
v >>= 4;
} while (v != 0);
(void)__syscall3(SYS_write, fd, (long)(buf + i), (long)(sizeof(buf) - 1 - i));
}
/* ---- Assertion machinery (implementation side of the macros) ---- */
static inline void
vlibc_test_fail_header(const char *file, int line)
{
vlibc_test_failures++;
vlibc_test_say(2, "ASSERT FAIL: ");
vlibc_test_say(2, file);
vlibc_test_say(2, ":");
vlibc_test_say_dec(2, (unsigned long)line);
vlibc_test_say(2, ": ");
}
static inline void // NOLINT(bugprone-easily-swappable-parameters)
vlibc_test_check(int ok, const char *what, const char *file, int line)
{
if (ok)
{
return;
}
vlibc_test_fail_header(file, line);
vlibc_test_say(2, what);
vlibc_test_say(2, " is false\n");
}
static inline void // NOLINT(bugprone-easily-swappable-parameters)
vlibc_test_check_eq(long long actual, long long expected, const char *actual_s,
const char *expected_s, const char *file, int line)
{
if (actual == expected)
{
return;
}
vlibc_test_fail_header(file, line);
vlibc_test_say(2, "TEST_ASSERT_EQ(");
vlibc_test_say(2, actual_s);
vlibc_test_say(2, ", ");
vlibc_test_say(2, expected_s);
vlibc_test_say(2, "): got 0x");
vlibc_test_say_hex(2, (unsigned long long)actual);
vlibc_test_say(2, ", want 0x");
vlibc_test_say_hex(2, (unsigned long long)expected);
vlibc_test_say(2, "\n");
}
static inline int
vlibc_test_str_eq(const char *a, const char *b)
{
if (a == b)
{
return 1;
}
if (a == NULL || b == NULL)
{
return 0;
}
while (*a != '\0' && *a == *b)
{
a++;
b++;
}
return *a == *b;
}
static inline void // NOLINT(bugprone-easily-swappable-parameters)
vlibc_test_check_streq(const char *actual, const char *expected,
const char *actual_s, const char *expected_s,
const char *file, int line)
{
if (vlibc_test_str_eq(actual, expected))
{
return;
}
vlibc_test_fail_header(file, line);
vlibc_test_say(2, "TEST_ASSERT_STREQ(");
vlibc_test_say(2, actual_s);
vlibc_test_say(2, ", ");
vlibc_test_say(2, expected_s);
vlibc_test_say(2, "): got \"");
vlibc_test_say(2, actual == NULL ? "(null)" : actual);
vlibc_test_say(2, "\", want \"");
vlibc_test_say(2, expected == NULL ? "(null)" : expected);
vlibc_test_say(2, "\"\n");
}
// NOLINTEND(bugprone-easily-swappable-parameters)
/* ---- Public assertion macros ---- */
#define TEST_ASSERT_TRUE(cond) \
do \
{ \
vlibc_test_check((cond) != 0, #cond, __FILE__, __LINE__); \
} while (0)
#define TEST_ASSERT_EQ(actual, expected) \
do \
{ \
vlibc_test_check_eq((long long)(actual), (long long)(expected), \
#actual, #expected, __FILE__, __LINE__); \
} while (0)
#define TEST_ASSERT_NULL(p) \
do \
{ \
vlibc_test_check((p) == NULL, #p " == NULL", __FILE__, __LINE__); \
} while (0)
#define TEST_ASSERT_STREQ(actual, expected) \
do \
{ \
vlibc_test_check_streq((actual), (expected), #actual, #expected, \
__FILE__, __LINE__); \
} while (0)
/* ---- Runner ---- */
#define TEST_MAIN(tests) \
int \
main(void) \
{ \
const size_t vlibc_test_count = sizeof(tests) / sizeof((tests)[0]); \
size_t vlibc_test_i; \
size_t vlibc_test_passed = 0; \
for (vlibc_test_i = 0; vlibc_test_i < vlibc_test_count; vlibc_test_i++) \
{ \
int vlibc_test_before = vlibc_test_failures; \
vlibc_test_say(1, "RUN "); \
vlibc_test_say(1, (tests)[vlibc_test_i].name); \
vlibc_test_say(1, ": "); \
if ((tests)[vlibc_test_i].run() == 0 && \
vlibc_test_failures == vlibc_test_before) \
{ \
vlibc_test_say(1, "PASS\n"); \
vlibc_test_passed++; \
} \
else \
{ \
vlibc_test_say(1, "FAIL\n"); \
} \
} \
vlibc_test_say(1, "SUMMARY: "); \
vlibc_test_say_dec(1, (unsigned long)vlibc_test_passed); \
vlibc_test_say(1, "/"); \
vlibc_test_say_dec(1, (unsigned long)vlibc_test_count); \
vlibc_test_say(1, " passed, "); \
vlibc_test_say_dec(1, (unsigned long)vlibc_test_failures); \
vlibc_test_say(1, " assertion failure(s)\n"); \
return vlibc_test_passed == vlibc_test_count ? 0 : 1; \
}
#endif /* VLIBC_TEST_H */