Compare commits

...

6 Commits

Author SHA1 Message Date
a48d8e998d Added a firstpass of a pre-push githook
This checksout every new commit and runs a testfile on it.
2025-12-23 21:46:32 -08:00
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
0924650090 Added Makefile Logic to work with Submodules
These changes allow for projects that were created and built using the
same Makefile to be placed under the submodules directory. This Makefile
will recursively build the submodules and link to the top project.

The submodules will be built as a static library using ar rcs.
2025-12-23 21:46:32 -08:00
3677b50fe1 Added Resillance to SegFaults by forking tests
A child process is created which runs the test operation. It then
returns the exit status if exited properly and termination signal if
not.

A Termination signal of (11) corresponds to a segfault so keep that in
mind.
2025-12-23 21:46:32 -08:00
a490341129 Created Initial Attempt Testing Framework
The framework, completely contained in x_tfw4c.c, uses pre-compiler
MACROS to create an environment where it is easy to create unit tests
and run regression tests.

It iterates through a linked list of test structs that contain the test
function that should be executed and the name of the test to be
displayed.

This is a simple rework of a prior test framework that did not
dynamically add new tests at compile time.

Next steps would be to have it display more metadata and have some
resilience to crashes in the tests.
2025-12-23 21:46:32 -08:00
4320c26105 Added Template Makefile
This Makefile will be used as a way to start new projects with the same
hierarchy. It will also be tightly coupled with the x testing framework
for c (x_tfw4c).

It has targets to setup the source, include, object, and tests
directories using the `make dirs` command.

There is some fun interesting things that I used to get the target to
recompile when the header files are updated using the -MMD and -MP flags
in gcc.

These flags basically allow for the generation of dependencies from each
source file that are exported and then included using the -include
$(DEPENDENCIES) line at the bottem of the Makefile.
2025-12-23 21:46:32 -08:00
4 changed files with 324 additions and 0 deletions

106
c_projects/x_tfw4c/Makefile Normal file
View File

@ -0,0 +1,106 @@
# MakeFile for placeholder
# Created mm/dd/yyyy
# Authored by: xavi
# ----------------------
# | THIS IS A TEMPLATE |
# ----------------------
# TODO:
# %s/placeholder/project_name/g
# Change date file was created to today
# run make dirs to create project hierarchy
# Place headers in include and source files in src
.POSIX:
# DIRECTORIES
SRC_DIR := src
INCLUDE_DIR := include
OBJ_DIR := obj
TESTS_DIR := tests
SUBMODULES_DIR := submodules
# Final Binary Name
PROJECT_NAME := placeholder
PROJECT_OBJ := $(OBJ_DIR)/$(PROJECT_NAME).o
# SUBMODULE VARS FOR GENERATING STATIC LIBS
SUBMODULES := $(wildcard $(SUBMODULES_DIR)/*)
SUBBARE := $(SUBMODULES:$(SUBMODULES_DIR)/%=%)
SUBMODULE_SLIB := $(foreach m,$(SUBBARE),$(SUBMODULES_DIR)/$(m)/$(m).a)
STATIC_LIBS = $(shell find ./ -name "*.a")
# C compiler settings
CC := gcc
INCLUDE_LIST := $(foreach m,$(SUBMODULES),$(m)/$(INCLUDE_DIR)) $(INCLUDE_DIR)
INCLUDES := $(addprefix -I,$(INCLUDE_LIST))
CFLAGS := -ggdb $(INCLUDES) -Wall -Wextra -MMD -MP
DFLAGS :=
# Main Executable Filename
MAIN := main.c
# Files
SRC := $(filter-out $(SRC_DIR)/$(MAIN), $(wildcard $(SRC_DIR)/*.c))
OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
TESTS := $(wildcard $(TESTS_DIR)/*.c)
# Get Dependencies from gcc using -MMD and -MP
DEPENDENCIES := $(OBJ:%.o=%.d)
all: $(PROJECT_NAME)
# CREATE STATIC LIBRARY OF MODULE
$(PROJECT_NAME).a: $(PROJECT_OBJ) check_submods
ar rcs $(@) $(<)
# CREATE TEST EXECUTABLES
$(TESTS:$(TESTS_DIR)/%.c=%): %: $(OBJ_DIR)/%.o $(OBJ) check_submods
$(CC) $(CFLAGS) $(DFLAGS) -o $(@) $(<) $(OBJ) $(STATIC_LIBS)
# CREATE TEST OBJS
$(OBJ_DIR)/%.o: $(TESTS_DIR)/%.c $(OBJ)
$(CC) $(CFLAGS) $(DFLAGS) -c -o $(@) $(^)
# CREATE MAIN OBJ
$(PROJECT_NAME): $(MAIN:%.c=$(OBJ_DIR)/%.o) $(OBJ) check_submods
$(CC) $(CFLAGS) $(DFLAGS) -o $(@) $(<) $(OBJ) $(STATIC_LIBS)
# CREATE OBJS
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
$(CC) $(CFLAGS) $(DFLAGS) -c -o $(@) $(<)
# RECURSE THROUGH SUBMODULES AND RUN MAKE *.a
$(SUBMODULE_SLIB):
$(MAKE) -C $(dir $(@)) $(notdir $(@))
check_submods: $(SUBMODULE_SLIB)
# RECURSE AND CLEAN ALL FILES
clean:
@for sm in $(SUBMODULES) ; do \
$(MAKE) -C $$sm clean ; \
done
rm -f $(PROJECT_NAME) $(OBJ_DIR)/* $(TESTS:$(TESTS_DIR)/%.c=%) ./*.a
-include $(DEPENDENCIES)
help:
@echo "SUBMODULES: $(SUBMODULES)"
@echo "SUBMODULE_SLIB: $(SUBMODULE_SLIB)"
@echo "STATIC_LIBS: $(STATIC_LIBS)"
.PHONY: clean all check_submods help
# Create Project Hierarchy
dirs: $(SRC_DIR) $(OBJ_DIR) $(TESTS_DIR) $(INCLUDE_DIR) $(SUBMODULES_DIR) $(SRC_DIR)/$(MAIN)
$(SRC_DIR) $(OBJ_DIR) $(TESTS_DIR) $(INCLUDE_DIR) $(SUBMODULES_DIR):
mkdir -p $(@)
$(SRC_DIR)/$(MAIN):
touch $(SRC_DIR)/$(MAIN)

View File

@ -0,0 +1,27 @@
#!/bin/bash
while read -r local_ref local_sha remote_ref remote_sha; do
echo $local_ref
echo $local_sha
echo $remote_ref
echo $remote_sha
done
for commit in $(git log main..test_framework --pretty=format:%h)
do
git checkout $commit
make clean
make
./x_tfw4c
rc=$?
if [ $rc -eq 1 ]; then
git checkout $current_branch
echo -e "\n"
echo -e "Tests completed with \e[31mFAILURES\e[0m"
echo "ABORTING git push"
exit 1
fi
done
git checkout $current_branch
exit 0

View File

@ -0,0 +1,162 @@
#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;
}

View File

@ -0,0 +1,29 @@
#include "test.h"
// INCLUDE PROJECT HEADERS
#include "hello_world.h"
// DEFINE TESTS
TEST(success){
return 0;
}
TEST(fail){
int i = 1;
assert(i == 0);
printf("after assert\n");
return i;
}
TEST(segfault){
int* x = NULL;
int y = *x;
return y;
}
// END OF TEST DEFINITIONS
int main(void){
return run_tests();
}