aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMosder <xmosder12@gmail.com>2026-07-31 18:51:21 +0200
committerMosder <xmosder12@gmail.com>2026-07-31 18:51:21 +0200
commit08389a6d306570fc4575988e62a8397a5ff49bee (patch)
treef52e537a17f9707cdb28116b1b72f9188ef7343c /src
parent4f4466155b6d9b5259ccd956551a675ad693e773 (diff)
feat: created tester
Diffstat (limited to 'src')
-rw-r--r--src/tester.c191
1 files changed, 191 insertions, 0 deletions
diff --git a/src/tester.c b/src/tester.c
index e69de29..361cfb9 100644
--- a/src/tester.c
+++ b/src/tester.c
@@ -0,0 +1,191 @@
+#include "moslib/tester.h"
+
+#include <setjmp.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "moslib/mem/alloc.h"
+
+#define INIT_GROUP_CAP 8
+#define INIT_TEST_CAP 16
+
+#define RED 91
+#define GREEN 92
+#define WHITE 97
+
+#define RENDER_WIDTH 80
+
+int failed = 0;
+
+#define print_color(template, ...) printf("\033[%dm" template "\033[0m", __VA_ARGS__)
+#define nl printf("\n")
+
+void print_center(const char *text, int color, char padding_char) {
+ size_t post_char_count = (RENDER_WIDTH - strlen(text)) / 2;
+ size_t pre_char_count = RENDER_WIDTH - post_char_count - strlen(text);
+
+ for (size_t i = 1; i < pre_char_count; i++)
+ printf("%c", padding_char);
+
+ print_color(" %s ", color, text);
+
+ for (size_t i = 1; i < post_char_count; i++)
+ printf("%c", padding_char);
+
+ nl;
+}
+
+typedef struct {
+ TestFn test;
+ const char *name;
+} TestName;
+
+struct Tester {
+ TestGroup *groups;
+ size_t n_groups;
+ size_t group_cap;
+};
+
+struct TestGroup {
+ const char *name;
+ TestName *tests;
+ size_t n_tests;
+ size_t test_cap;
+};
+
+Tester *new_tester() {
+ Tester *tester = safe_malloc(sizeof(Tester));
+ *tester = (Tester){
+ .groups = safe_malloc(INIT_GROUP_CAP * sizeof(TestGroup)),
+ .n_groups = 0,
+ .group_cap = INIT_GROUP_CAP,
+ };
+ return tester;
+}
+
+TestGroup *add_test_group_fn(Tester *tester, const char *group_name) {
+ if (tester->n_groups >= tester->group_cap) {
+ tester->group_cap *= 2;
+ tester->groups = safe_realloc(tester->groups, tester->group_cap * sizeof(TestGroup));
+ }
+
+ TestGroup *group = tester->groups + tester->n_groups++;
+ // clang-format off
+ *group = (TestGroup){
+ .name = group_name,
+ .tests = safe_malloc(INIT_TEST_CAP * sizeof(TestName)),
+ .n_tests = 0,
+ .test_cap = INIT_TEST_CAP
+ };
+ // clang-format on
+ return group;
+}
+
+void test_assert(int expression, const char *fail_message) {
+ if (!expression) {
+ failed = 1;
+ print_color("ASSERT FAILED: ", RED);
+ printf("%s\n", fail_message);
+ }
+}
+
+jmp_buf jump_env;
+int expected_code;
+const char *fn_name;
+void exit(int code) {
+ if (code != expected_code) {
+ failed = 1;
+ print_color("ASSERT FAILED: ", RED);
+ printf("exit code was %d, expected %d (in function %s)\n", code, expected_code, fn_name);
+ }
+ longjmp(jump_env, 1);
+}
+
+void test_assert_exit_fn(TestFn function, int code, const char *name) {
+ expected_code = code;
+ fn_name = name;
+ if (setjmp(jump_env) == 0) {
+ function();
+ failed = 1;
+ print_color("ASSERT FAILED: ", RED);
+ printf("exit was not called (in function %s)\n", name);
+ }
+}
+
+void add_test_fn(TestGroup *group, TestFn test, const char *name) {
+ if (group->n_tests >= group->test_cap) {
+ group->test_cap *= 2;
+ group->tests = safe_realloc(group->tests, group->test_cap * sizeof(TestName));
+ }
+
+ group->tests[group->n_tests++] = (TestName){.test = test, .name = name};
+}
+
+void run_tests(Tester *tester) {
+ if (!tester)
+ return;
+
+ size_t total_passed = 0;
+ size_t total_failed = 0;
+ char buf[80];
+
+ print_center("START", WHITE, '=');
+
+ for (size_t i = 0; i < tester->n_groups; i++) {
+ nl;
+ TestGroup *group = tester->groups + i;
+ size_t tests_passed = 0;
+ size_t tests_failed = 0;
+
+ print_center(group->name, WHITE, '-');
+
+ for (size_t j = 0; j < group->n_tests; j++) {
+ failed = 0;
+ printf("%s:\n", group->tests[j].name);
+ group->tests[j].test();
+ if (failed) {
+ tests_failed += 1;
+ print_color("--- FAILED ---\n", RED);
+ }
+ else {
+ tests_passed += 1;
+ print_color("--- PASSED ---\n", GREEN);
+ }
+ }
+
+ print_center("GROUP SUMMARY", WHITE, '-');
+
+ sprintf(buf, "%zu PASSED", tests_passed);
+ print_center(buf, GREEN, ' ');
+ sprintf(buf, "%zu FAILED", tests_failed);
+ print_center(buf, RED, ' ');
+ if (i < tester->n_groups - 1) {
+ nl;
+ print_center("TESTING", WHITE, '=');
+ }
+
+ total_passed += tests_passed;
+ total_failed += tests_failed;
+ }
+
+ nl;
+ print_center("TESTS SUMMARY", WHITE, '=');
+ sprintf(buf, "%zu PASSED", total_passed);
+ print_center(buf, GREEN, ' ');
+ sprintf(buf, "%zu FAILED", total_failed);
+ print_center(buf, RED, ' ');
+ print_center("END", WHITE, '=');
+}
+
+void free_tester(Tester *tester) {
+ if (!tester)
+ return;
+
+ for (size_t i = 0; i < tester->n_groups; i++) {
+ free(tester->groups[i].tests);
+ }
+ free(tester->groups);
+ free(tester);
+}