From f3377656e4e0aa2b96492bd79ad1b98a60e036c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zo=C3=ABy=20Noort?= Date: Sat, 31 May 2025 19:34:20 +0200 Subject: [PATCH] feat: added tests --- .gitmodules | 3 ++ Makefile | 50 ++++++++++++++++++++++++++------- benchmarks/libs/musl | 1 + tests/char.c | 62 ++++++++++++++++++++++++++++++++++++++++ tests/output.c | 61 ++++++++++++++++++++++++++++++++++++++++ tests/test.h | 67 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 234 insertions(+), 10 deletions(-) create mode 100644 .gitmodules create mode 160000 benchmarks/libs/musl create mode 100644 tests/char.c create mode 100644 tests/output.c create mode 100644 tests/test.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6075e75 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "benchmarks/libs/musl"] + path = benchmarks/libs/musl + url = git://git.musl-libc.org/musl diff --git a/Makefile b/Makefile index 8a0ee05..ae81e54 100644 --- a/Makefile +++ b/Makefile @@ -1,31 +1,61 @@ CC = clang -CFLAGS = -Wall -Wextra -Werror -O3 +CFLAGS = -Wall -Wextra -Werror -O2 LDFLAGS = -flto SRC_DIR = srcs OBJ_DIR = obj BUILD_DIR = build -HDR_DIR = hdrs -SRCS = $(wildcard $(SRC_DIR)/*.c) -OBJS = $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) +HDRS_DIR = hdrs LIBRARY = $(BUILD_DIR)/libft.a +# Source and object files for library +SRCS = $(shell find $(SRC_DIR) -type f -name "*.c") +OBJS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRCS)) + +# Test configuration +TESTS_DIR = tests +TESTS_SRCS = $(wildcard $(TESTS_DIR)/*.c) +TESTS_OBJS = $(patsubst $(TESTS_DIR)/%.c,$(OBJ_DIR)/%.test.o,$(TESTS_SRCS)) +TESTS_BINS = $(patsubst $(TESTS_DIR)/%.c,$(BUILD_DIR)/%_test,$(TESTS_SRCS)) + all: $(LIBRARY) $(OBJ_DIR) $(BUILD_DIR): @mkdir -p $@ -$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(HDR_DIR)/*.h | $(OBJ_DIR) - $(CC) $(CFLAGS) -I$(HDR_DIR) -c $< -o $@ +# Build object files for library +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(HDRS_DIR)/*.h | $(OBJ_DIR) + @mkdir -p $(dir $@) + @echo -e " CC\t$<" + @$(CC) $(CFLAGS) -I$(HDRS_DIR) -c $< -o $@ $(LIBRARY): $(OBJS) | $(BUILD_DIR) - $(AR) rcs $(LIBRARY) $(OBJS) + @echo -e " AR\t$@" + @$(AR) rcs $(LIBRARY) $(OBJS) +# Build object files for tests +$(OBJ_DIR)/%.test.o: $(TESTS_DIR)/%.c $(HDRS_DIR)/*.h | $(OBJ_DIR) + @mkdir -p $(dir $@) + @echo -e " CC\t$<" + @$(CC) $(CFLAGS) -I$(HDRS_DIR) -I$(SRC_DIR) -c $< -o $@ + +# Link each test binary +$(BUILD_DIR)/%_test: $(OBJ_DIR)/%.test.o $(LIBRARY) | $(BUILD_DIR) + @echo -e " LD\t$@" + @$(CC) $(CFLAGS) $< -L$(BUILD_DIR) -lft -o $@ + +tests: $(LIBRARY) $(TESTS_BINS) + @for testbin in $(TESTS_BINS); do \ + ./$$testbin; \ + done clean: - rm -rf $(OBJ_DIR)/*.o + @echo " CLEAN" + @rm -rf $(OBJ_DIR) fclean: clean - rm -rf $(BUILD_DIR) + @echo " FCLEAN" + @rm -rf $(BUILD_DIR) + @rm -f $(TESTS_BIN) re: fclean all -.PHONY: all clean fclean re +.PHONY: all clean fclean re tests diff --git a/benchmarks/libs/musl b/benchmarks/libs/musl new file mode 160000 index 0000000..ae3a8c9 --- /dev/null +++ b/benchmarks/libs/musl @@ -0,0 +1 @@ +Subproject commit ae3a8c93a663b553e65f096498937083dad210d2 diff --git a/tests/char.c b/tests/char.c new file mode 100644 index 0000000..8268a0e --- /dev/null +++ b/tests/char.c @@ -0,0 +1,62 @@ +#include "test.h" +#include "libft.h" + +TEST(test_isalpha) { + ASSERT(ft_isalpha('a')); + ASSERT(ft_isalpha('Z')); + ASSERT(!ft_isalpha('1')); + ASSERT(!ft_isalpha('@')); + ASSERT(!ft_isalpha(' ')); +} + +TEST(test_isdigit) { + ASSERT(ft_isdigit('0')); + ASSERT(ft_isdigit('9')); + ASSERT(!ft_isdigit('a')); + ASSERT(!ft_isdigit(' ')); + ASSERT(!ft_isdigit('#')); +} + +TEST(test_islower) { + ASSERT(ft_islower('a')); + ASSERT(ft_islower('z')); + ASSERT(!ft_islower('A')); + ASSERT(!ft_islower('0')); + ASSERT(!ft_islower(' ')); +} + +TEST(test_isupper) { + ASSERT(ft_isupper('A')); + ASSERT(ft_isupper('Z')); + ASSERT(!ft_isupper('a')); + ASSERT(!ft_isupper('0')); + ASSERT(!ft_isupper(' ')); +} + +TEST(test_tolower) { + ASSERT(ft_tolower('A') == 'a'); + ASSERT(ft_tolower('Z') == 'z'); + ASSERT(ft_tolower('a') == 'a'); + ASSERT(ft_tolower('z') == 'z'); + ASSERT(ft_tolower('1') == '1'); + ASSERT(ft_tolower(' ') == ' '); +} + +TEST(test_toupper) { + ASSERT(ft_toupper('a') == 'A'); + ASSERT(ft_toupper('z') == 'Z'); + ASSERT(ft_toupper('A') == 'A'); + ASSERT(ft_toupper('Z') == 'Z'); + ASSERT(ft_toupper('1') == '1'); + ASSERT(ft_toupper(' ') == ' '); +} + +int main(void) { + RUN_TEST(test_isalpha); + RUN_TEST(test_isdigit); + RUN_TEST(test_islower); + RUN_TEST(test_isupper); + RUN_TEST(test_tolower); + RUN_TEST(test_toupper); + return 0; +} diff --git a/tests/output.c b/tests/output.c new file mode 100644 index 0000000..fefecbd --- /dev/null +++ b/tests/output.c @@ -0,0 +1,61 @@ +#include "test.h" +#include "libft.h" + +TEST(test_putchar) { + int ret = ft_putchar('A'); + ASSERT(ret == 1); // Should print 'A' +} + +TEST(test_putstr) { + int ret = ft_putstr("Hello"); + ASSERT(ret == 5); // Should print 'Hello' + ret = ft_putstr(NULL); + ASSERT(ret == 6 || ret == 0); // Depending on your implementation: "(null)" or nothing +} + +TEST(test_putnbr) { + int ret = ft_putnbr(42); + ASSERT(ret == 2); // Should print "42" + ret = ft_putnbr(-1234); + ASSERT(ret == 5); // Should print "-1234" +} + +TEST(test_putunbr) { + int ret = ft_putunbr(12345U); + ASSERT(ret == 5); // Should print "12345" +} + +TEST(test_puthex) { + int ret = ft_puthex(0x2A, 0); + ASSERT(ret == 2); // Should print "2a" + ret = ft_puthex(0x2A, 1); + ASSERT(ret == 2); // Should print "2A" +} + +TEST(test_putaddr) { + int ret = ft_putaddr(0x1234abcd); + ASSERT(ret >= 3); // Should print "0x..." (at least 3 chars) +} + +TEST(test_putptr) { + int x = 42; + int ret = ft_putptr(&x); + ASSERT(ret >= 3); // Should print "0x..." (at least 3 chars) +} + +TEST(test_printf) { + int ret = ft_printf("Hello %s %d %x\n", "world", 42, 255); + ASSERT(ret > 0); // Should print "Hello world 42 ff\n" +} + +int main(void) { + RUN_TEST(test_putchar); + RUN_TEST(test_putstr); + RUN_TEST(test_putnbr); + RUN_TEST(test_putunbr); + RUN_TEST(test_puthex); + RUN_TEST(test_putaddr); + RUN_TEST(test_putptr); + RUN_TEST(test_printf); + return 0; +} diff --git a/tests/test.h b/tests/test.h new file mode 100644 index 0000000..e16c8b7 --- /dev/null +++ b/tests/test.h @@ -0,0 +1,67 @@ +#ifndef TEST_H +#define TEST_H + +#include +#include +#include +#include +#include + +static int _stdout_fd_backup = -1; +static int _devnull_fd = -1; + +static void mute_stdout(void) { + fflush(stdout); + _stdout_fd_backup = dup(STDOUT_FILENO); + _devnull_fd = open("/dev/null", O_WRONLY); + dup2(_devnull_fd, STDOUT_FILENO); +} + +static void unmute_stdout(void) { + fflush(stdout); + if (_stdout_fd_backup != -1) { + dup2(_stdout_fd_backup, STDOUT_FILENO); + close(_stdout_fd_backup); + _stdout_fd_backup = -1; + } + if (_devnull_fd != -1) { + close(_devnull_fd); + _devnull_fd = -1; + } +} + + +#define GREEN "\033[1;32m" +#define RED "\033[1;31m" +#define RESET "\033[0m" + +#define TEST(name) void name(void) +#define RUN_TEST(test) do { \ + mute_stdout(); \ + int result = test_runner(test); \ + unmute_stdout(); \ + if (result) \ + printf(" OK\t%s\n", #test); \ + else \ + printf(" FAIL\t%s\n", #test); \ +} while (0) + +static jmp_buf env; + +static int test_runner(void (*test_func)(void)) { + int failed = 0; + if (setjmp(env) == 0) { + test_func(); + } else { + failed = 1; + } + return !failed; +} + +#define ASSERT(expr) do { \ + if (!(expr)) { \ + longjmp(env, 1); \ + } \ +} while (0) + +#endif