build: wire new src dirs, crt objects, and make check into Autotools
This commit is contained in:
@@ -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 */
|
||||
Reference in New Issue
Block a user