commit 3d2eac4ae2429b93d694efda884f23e5648f104c
parent 85f2ad59703942f0744110e658067d317d2efe1f
Author: hhvn <dev@hhvn.uk>
Date: Sun, 27 Nov 2022 12:05:48 +0000
Testing with libcheck
Diffstat:
9 files changed, 143 insertions(+), 6 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -7,3 +7,4 @@ saves/
core.*
tags
perf.*
+.testing
diff --git a/Makefile b/Makefile
@@ -12,7 +12,7 @@ LDFLAGS = $(RAYLIB) $(DBLIB)
include config.mk
all: db data $(BIN)
-src/data.o: data/icons/*
+src/main.o: .testing
.c.o:
$(CC) $(CFLAGS) -D"SAVEDIR=\"$(SAVEDIR)\"" -c $< -o $@
diff --git a/dev/config.mk b/dev/config.mk
@@ -1,6 +1,7 @@
CFLAGS += -Wall -g3 -O0
CFLAGS += -DDEBUG
CFLAGS += -DCHECK_FRAME_MEM_FREE
+ARGS =
all: tags checks
tags: $(SRC)
@@ -9,10 +10,31 @@ tags: $(SRC)
checks: $(SRC)
./dev/checkalloc.sh
-test: all
- gdb ./$(BIN) -ex 'set confirm on' -ex run -ex bt -ex quit --args $(ARGS)
+# All objects in src/ that react to TEST should have .testing as a dependency.
+# Updating it before and after running will:
+# - Compile them with TEST if they weren't already
+# - Compile them without TEST next time it should be compiled normally
+test:
+ touch .testing
+ cd tests && make
+ make test-run
+ rm -rf test.*
+ touch .testing
+
+clean: test-clean
+test-clean:
+ cd tests && make clean
+
+# Since the 'test' target will create *.c files in tests/, those can't be added
+# to $(SRC), as shell macros are evaluated before the target is run. Hence,
+# this target is required.
+test-run:
+ make run CFLAGS="$(CFLAGS) -DTEST" LDFLAGS="$(LDFLAGS) -lcheck" SRC="$(SRC) $(shell find tests -type f -name "*.c")"
+
+run: all
+ gdb -ex 'set confirm on' -ex run -ex bt -ex quit --args $(BIN) $(ARGS)
gdb: all
- gdb ./$(BIN) --args $(ARGS)
+ gdb --args ./$(BIN) $(ARGS)
VALFILE = dev/valgrind.log
VALSUPP = dev/valgrind-suppress
diff --git a/src/main.c b/src/main.c
@@ -7,6 +7,10 @@
#define DEFSAVE "default"
+#ifdef TEST
+int test_run(void);
+#endif /* TEST */
+
Save *save = NULL;
int sigint = 0;
int sigterm = 0;
@@ -26,12 +30,17 @@ main(void) {
int view_prev;
struct sigaction sa;
+
sa.sa_handler = sighandler;
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
+#ifdef TEST
+ return test_run();
+#endif /* TEST */
+
ui_init();
data_load();
diff --git a/src/main.h b/src/main.h
@@ -20,9 +20,9 @@
#define CUSTOM_FREE
#endif /* CHECK_FRAME_MEM_FREE */
-#ifdef CUSTOM_FREE
+#if defined(CUSTOM_FREE) && !defined(TEST)
#define free(m) _free(m, __FILE__, __LINE__, __func__)
-#endif /* CUSTOM_FREE */
+#endif /* defined(CUSTOM_FREE) && !defined(TEST) */
/* main.c */
extern Save *save;
diff --git a/tests/.gitignore b/tests/.gitignore
@@ -0,0 +1,4 @@
+# except for main.c
+*.c
+*.o
+*.h
diff --git a/tests/Makefile b/tests/Makefile
@@ -0,0 +1,27 @@
+TESTS = $(shell find . -type f -name "*.test")
+TESTC = $(TESTS:.test=.c)
+CHECKS = `echo $(TESTS) | tr ' ' '\n' | sed 's/^.\//test_/;s/\.test$$//'`
+
+all: list.c test.h $(TESTC)
+$(TESTC): mktest.awk Makefile
+
+list.c: test.h
+ printf '#include "test.h"\n\n' > $@
+ printf 'int (*tests[TESTS])(void) = {\n' >> $@
+ for f in $(CHECKS); do \
+ printf '\t%s,\n' $$f >> $@; \
+ done
+ printf '};\n' >> $@
+
+test.h: $(TESTS)
+ printf '%s' "$(CHECKS)" | sed 's/^/int /;s/$$/(void);/' > $@
+ printf '\n#define TESTS %d\n' $$(echo "$(CHECKS)" | wc -l) >> $@
+ printf '\nextern int (*tests[TESTS])(void);\n' >> $@
+
+clean:
+ rm -f `find . -name "*.c" -o -name "*.h" -o -name "*.o" | grep -v main\.c`
+
+.test.c:
+ ./mktest.awk -v "test=$*" < $< > $@
+
+.SUFFIXES: .test .c
diff --git a/tests/main.c b/tests/main.c
@@ -0,0 +1,21 @@
+#include <check.h>
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "test.h"
+#include "../src/main.h"
+
+int
+test_run(void) {
+ int ret, i;
+
+ printf("\033[1mvvv Tests vvv\033[0m\n");
+
+ for (ret = i = 0; i < TESTS; i++)
+ ret += tests[i]();
+
+ printf("\033[1m^^^ Tests ^^^\033[0m\n");
+
+ return !!ret;
+}
diff --git a/tests/mktest.awk b/tests/mktest.awk
@@ -0,0 +1,53 @@
+#!/bin/awk -f
+
+BEGIN {
+ print "#include <check.h>"
+ done = 0
+}
+
+/^%{$/ {
+ print "START_TEST(ck_"test") {"
+}
+
+!/^%{$/ && !/^}$/ {
+ if (done)
+ print "error: excess lines" > "/dev/stderr"
+ else
+ print
+}
+
+/^}$/ {
+ print "}"
+ print "END_TEST"
+ print ""
+ print "#include <limits.h>"
+ print "#include <unistd.h>"
+ print "#include <stdlib.h>"
+ print ""
+ print "int"
+ print "test_"test"(void) {"
+ print " Suite *s = suite_create(\""test"\");"
+ print " TCase *tc = tcase_create(\""test"\");"
+ print " SRunner *sr = srunner_create(s);"
+ print " char restoredir[PATH_MAX];"
+ print " char tmp[] = \"test.XXXXXX\";"
+ print " int ret;"
+ print ""
+ print " getcwd(restoredir, sizeof(restoredir));"
+ print " mkdtemp(tmp);"
+ print " chdir(tmp);"
+ print ""
+ print " suite_add_tcase(s, tc);"
+ print " tcase_add_test(tc, ck_"test");"
+ print ""
+ print " srunner_run_all(sr, CK_ENV);"
+ print " ret = srunner_ntests_failed(sr);"
+ print " srunner_free(sr);"
+ print ""
+ print " chdir(restoredir);"
+ print " /* rmdirp(tmp); */"
+ print ""
+ print " return !!ret;"
+ print "}"
+ done = 1
+}