code_snippets/c_projects/x_tfw4c/tests/include/test.h
xavi 5905cb67b0 Added the test framework to the tests directory
Separated the test framework into the header. Test definitions should be
placed in the test.c.
2025-12-23 21:46:32 -08:00

163 lines
3.0 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <assert.h>
// printf Formating
#define X_RED "\x1B[31m"
#define X_GREEN "\x1B[32m"
#define X_YELLOW "\x1B[33m"
#define X_RST "\x1B[0m"
// TEST FRAMEWORK STRUCT
typedef struct testnode{
char* name;
int (*op)(void);
struct testnode* next;
}testnode;
// LL nodes
static testnode* head = NULL;
static testnode* tail = NULL;
static testnode* ptr = NULL;
// TEST FRAMEWORK MACRO
// create a new node and pass in the name of the test. Then make a new function
// that adds the test to the LL. __attribute__((constructor)) has the function
// run before main
#define TEST(UNIT)\
static int UNIT(void);\
static testnode UNIT##node = {#UNIT, UNIT, NULL};\
__attribute__((constructor)) \
static void add##UNIT(void){ \
if (head == NULL){\
head = &UNIT##node;\
tail = head;\
return; \
}\
ptr = tail;\
tail = &UNIT##node;\
ptr->next = tail;\
}\
static int UNIT(void)
// TEST HELPER FUNCTIONS and STRUCTS
typedef enum {START, END} header_opt;
static void ptest_headers(header_opt option){
switch (option){
case START:
printf("\n-------------------\n");
printf(" Starting Tests\n");
printf("-------------------\n");
break;
case END:
printf("-------------------\n");
printf(" ENDING TESTS\n");
printf("-------------------\n\n");
break;
default:
printf("error: Wrong option in ptest_headers()\n");
exit(-1);
}
}
typedef enum {PASS, FAIL}rc_opt;
static void ppass_fail(rc_opt rc){
switch (rc){
case PASS:
printf("%s[PASS]%s ", X_GREEN, X_RST);
printf("%s", ptr->name);
break;
case FAIL:
printf("%s[FAIL]%s ", X_RED, X_RST);
printf("%s ", ptr->name);
break;
default:
printf("error: Unknown return code");
exit(-1);
}
}
static int presult(int status){
if (WIFEXITED(status)){
int rc = WEXITSTATUS(status);
if (rc == 0) {
ppass_fail(PASS);
return 0;
}
else{
ppass_fail(FAIL);
printf("- Exit Code %s(%d)%s",
X_YELLOW, rc, X_RST);
return 1;
}
}
else if (WIFSIGNALED(status)){
ppass_fail(FAIL);
printf("- Termination Signal %s(%d)%s",
X_YELLOW, WTERMSIG(status), X_RST);
return 1;
}
else{
printf("%s[FAIL]%s ", X_RED, X_RST);
printf("%s ", ptr->name);
printf("- Unknown Error ");
return 1;
}
}
static int run_child(){
int status;
pid_t cpid = fork();
if (cpid < 0){
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0){
int rc = ptr->op();
_exit(rc);
}else{
if (waitpid(cpid, &status, 0) < 0){
perror("waitpid");
exit(EXIT_FAILURE);
}
}
return status;
}
static int run_tests(){
int total_tests = 0;
int errors = 0;
ptest_headers(START);
ptr = head;
while (ptr != NULL){
total_tests++;
if(presult(run_child())){
errors++;
}
printf("\n");
ptr = ptr->next;
}
printf("\nTests completed with %s%d PASS%s", X_GREEN, total_tests - errors, X_RST);
printf(" and %s%d FAIL%s\n", X_RED, errors, X_RST);
ptest_headers(END);
if (errors > 0){
return 1;
}
return 0;
}