Foundry/lib/test.h

191 lines
5.9 KiB
C

/* SPDX-License-Identifier: AGPL-3.0-or-later */
/* Copyright (C) 2026 David (Corey) Herrera */
/*
* Minimal test framework for Foundry
* Inspired by JACLibc's testing.h but standalone
*/
#ifndef FOUNDRY_TEST_H
#define FOUNDRY_TEST_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
/* Test state */
static int __ft_total = 0;
static int __ft_passed = 0;
static int __ft_failed = 0;
static int __ft_current_failed = 0;
/* Colors */
#define FT_GREEN "\x1b[92m"
#define FT_RED "\x1b[31m"
#define FT_YELLOW "\x1b[93m"
#define FT_RESET "\x1b[0m"
/* Test runner macro */
#define RUN_TEST(name) do { \
__ft_total++; \
__ft_current_failed = 0; \
test_##name(); \
if (__ft_current_failed == 0) { \
__ft_passed++; \
printf(" %s%-50s%s%s[PASS]%s\n", FT_YELLOW, #name, FT_RESET, FT_GREEN, FT_RESET); \
} \
} while(0)
/* Assertions */
#define ASSERT_TRUE(x) do { \
if (!(x)) { \
if (__ft_current_failed == 0) { \
printf(" %s%-50s%s%s[FAIL]%s\n", FT_YELLOW, __func__ + 5, FT_RESET, FT_RED, FT_RESET); \
__ft_failed++; \
} \
printf(" %s:%d - Expected true: %s\n", __FILE__, __LINE__, #x); \
__ft_current_failed = 1; \
return; \
} \
} while(0)
#define ASSERT_FALSE(x) ASSERT_TRUE(!(x))
#define ASSERT_NOT_NULL(ptr) do { \
if ((ptr) == NULL) { \
if (__ft_current_failed == 0) { \
printf(" %s%-50s%s%s[FAIL]%s\n", FT_YELLOW, __func__ + 5, FT_RESET, FT_RED, FT_RESET); \
__ft_failed++; \
} \
printf(" %s:%d - Expected non-NULL: %s\n", __FILE__, __LINE__, #ptr); \
__ft_current_failed = 1; \
return; \
} \
} while(0)
#define ASSERT_NULL(ptr) do { \
if ((ptr) != NULL) { \
if (__ft_current_failed == 0) { \
printf(" %s%-50s%s%s[FAIL]%s\n", FT_YELLOW, __func__ + 5, FT_RESET, FT_RED, FT_RESET); \
__ft_failed++; \
} \
printf(" %s:%d - Expected NULL: %s\n", __FILE__, __LINE__, #ptr); \
__ft_current_failed = 1; \
return; \
} \
} while(0)
#define ASSERT_INT_EQ(a, b) do { \
long __a = (long)(a); \
long __b = (long)(b); \
if (__a != __b) { \
if (__ft_current_failed == 0) { \
printf(" %s%-50s%s%s[FAIL]%s\n", FT_YELLOW, __func__ + 5, FT_RESET, FT_RED, FT_RESET); \
__ft_failed++; \
} \
printf(" %s:%d - Expected %ld == %ld\n", __FILE__, __LINE__, __a, __b); \
__ft_current_failed = 1; \
return; \
} \
} while(0)
#define ASSERT_INT_NE(a, b) do { \
long __a = (long)(a); \
long __b = (long)(b); \
if (__a == __b) { \
if (__ft_current_failed == 0) { \
printf(" %s%-50s%s%s[FAIL]%s\n", FT_YELLOW, __func__ + 5, FT_RESET, FT_RED, FT_RESET); \
__ft_failed++; \
} \
printf(" %s:%d - Expected %ld != %ld\n", __FILE__, __LINE__, __a, __b); \
__ft_current_failed = 1; \
return; \
} \
} while(0)
#define ASSERT_FLT_NEAR(a, b, tol) do { \
float __a = (float)(a); \
float __b = (float)(b); \
float __diff = __a > __b ? __a - __b : __b - __a; \
if (__diff > (float)(tol)) { \
if (__ft_current_failed == 0) { \
printf(" %s%-50s%s%s[FAIL]%s\n", FT_YELLOW, __func__ + 5, FT_RESET, FT_RED, FT_RESET); \
__ft_failed++; \
} \
printf(" %s:%d - Expected %f ≈ %f (diff: %f, tol: %f)\n", __FILE__, __LINE__, __a, __b, __diff, (float)(tol)); \
__ft_current_failed = 1; \
return; \
} \
} while(0)
#define ASSERT_FLT_EQ(a, b) ASSERT_FLT_NEAR(a, b, 0.0001f)
#define ASSERT_DBL_EQ(a, b) do { \
double __a = (double)(a); \
double __b = (double)(b); \
if (__a != __b) { \
if (__ft_current_failed == 0) { \
printf(" %s%-50s%s%s[FAIL]%s\n", FT_YELLOW, __func__ + 5, FT_RESET, FT_RED, FT_RESET); \
__ft_failed++; \
} \
printf(" %s:%d - Expected %f == %f\n", __FILE__, __LINE__, __a, __b); \
__ft_current_failed = 1; \
return; \
} \
} while(0)
#define ASSERT_DBL_GT(a, b) do { \
double __a = (double)(a); \
double __b = (double)(b); \
if (!(__a > __b)) { \
if (__ft_current_failed == 0) { \
printf(" %s%-50s%s%s[FAIL]%s\n", FT_YELLOW, __func__ + 5, FT_RESET, FT_RED, FT_RESET); \
__ft_failed++; \
} \
printf(" %s:%d - Expected %f > %f\n", __FILE__, __LINE__, __a, __b); \
__ft_current_failed = 1; \
return; \
} \
} while(0)
#define ASSERT_DBL_LT(a, b) do { \
double __a = (double)(a); \
double __b = (double)(b); \
if (!(__a < __b)) { \
if (__ft_current_failed == 0) { \
printf(" %s%-50s%s%s[FAIL]%s\n", FT_YELLOW, __func__ + 5, FT_RESET, FT_RED, FT_RESET); \
__ft_failed++; \
} \
printf(" %s:%d - Expected %f < %f\n", __FILE__, __LINE__, __a, __b); \
__ft_current_failed = 1; \
return; \
} \
} while(0)
#define ASSERT_STR_EQ(a, b) do { \
const char* __a = (a); \
const char* __b = (b); \
if (__a == NULL || __b == NULL || strcmp(__a, __b) != 0) { \
if (__ft_current_failed == 0) { \
printf(" %s%-50s%s%s[FAIL]%s\n", FT_YELLOW, __func__ + 5, FT_RESET, FT_RED, FT_RESET); \
__ft_failed++; \
} \
printf(" %s:%d - Expected \"%s\" == \"%s\"\n", __FILE__, __LINE__, __a ? __a : "(null)", __b ? __b : "(null)"); \
__ft_current_failed = 1; \
return; \
} \
} while(0)
/* Test report */
#define TEST_REPORT() do { \
printf("\n%s\n", __ft_failed == 0 \
? FT_GREEN " ALL TESTS PASSED" FT_RESET \
: FT_RED " SOME TESTS FAILED" FT_RESET); \
printf("\n %d total %s%d passed%s %s%d failed%s\n\n", \
__ft_total, \
FT_GREEN, __ft_passed, FT_RESET, \
FT_RED, __ft_failed, FT_RESET); \
} while(0)
#endif /* FOUNDRY_TEST_H */