Makefile: wire up the clar unit testing framework

Wire up the clar unit testing framework by introducing a new "unit-tests" executable. In contrast to the existing framework, this will result in a single executable for all test suites. The ability to pick specific tests to execute is retained via functionality built into the clar itself. Note that we need to be a bit careful about how we need to invalidate our Makefile rules. While we obviously have to regenerate the clar suite when our test suites change, we also have to invalidate it in case any of the test suites gets removed. We do so by using our typical pattern of creating a `GIT-TEST-SUITES` file that gets updated whenever the set of test suites changes, so that we can easily depend on that file. Another specialty is that we generate a "clar-decls.h" file. The test functions are neither static, nor do they have external declarations. This is because they are getting parsed via "generate.py", which then creates the external generations that get populated into an array. These declarations are only seen by the main function though. The consequence is that we will get a bunch of "missing prototypes" errors from our compiler for each of these test functions. To fix those errors, we extract the `extern` declarations from "clar.suite" and put them into a standalone header that then gets included by each of our unit tests. This gets rid of compiler warnings for every function which has been extracted by "generate.py". More importantly though, it does _not_ get rid of warnings in case a function really isn't being used by anything. Thus, it would cause a compiler error if a function name was mistyped and thus not picked up by "generate.py". The test driver "unit-test.c" is an empty stub for now. It will get implemented in the next commit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Sep 4, 2024 at 16:17 UTC 8bc5d33bd86cd21be975522a54f4fa6e1e646a16
7 files changed +95 -4
.gitignore
+1
@@ -9,6 +9,7 @@
9 /GIT-PYTHON-VARS
10 /GIT-SCRIPT-DEFINES
11 /GIT-SPATCH-DEFINES
12 +/GIT-TEST-SUITES
13 /GIT-USER-AGENT
14 /GIT-VERSION-FILE
15 /bin-wrappers/
Makefile
+32 -4
@@ -914,6 +914,8 @@ REFTABLE_TEST_LIB = reftable/libreftable_test.a
914 GENERATED_H += command-list.h
915 GENERATED_H += config-list.h
916 GENERATED_H += hook-list.h
917 +GENERATED_H += $(UNIT_TEST_DIR)/clar-decls.h
918 +GENERATED_H += $(UNIT_TEST_DIR)/clar.suite
919
920 .PHONY: generated-hdrs
921 generated-hdrs: $(GENERATED_H)
@@ -1334,6 +1336,11 @@ THIRD_PARTY_SOURCES += sha1dc/%
1336 THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/%
1337 THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/%
1338
1339 +UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X)
1340 +UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES))
1341 +UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
1342 +UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/unit-test.o
1343 +
1344 UNIT_TEST_PROGRAMS += t-ctype
1345 UNIT_TEST_PROGRAMS += t-example-decorate
1346 UNIT_TEST_PROGRAMS += t-hash
@@ -2714,6 +2721,7 @@ OBJECTS += $(XDIFF_OBJS)
2721 OBJECTS += $(FUZZ_OBJS)
2722 OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)
2723 OBJECTS += $(UNIT_TEST_OBJS)
2724 +OBJECTS += $(UNIT_TESTS_OBJS)
2725
2726 ifndef NO_CURL
2727 OBJECTS += http.o http-walker.o remote-curl.o
@@ -3216,7 +3224,7 @@ endif
3224
3225 test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
3226
3219 -all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS)
3227 +all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG)
3228
3229 bin-wrappers/%: wrap-for-bin.sh
3230 $(call mkdir_p_parent_template)
@@ -3648,7 +3656,7 @@ endif
3656
3657 artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \
3658 GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \
3651 - $(UNIT_TEST_PROGS) $(MOFILES)
3659 + $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) $(MOFILES)
3660 $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) \
3661 SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
3662 test -n "$(ARTIFACTS_DIRECTORY)"
@@ -3704,6 +3712,7 @@ cocciclean:
3712
3713 clean: profile-clean coverage-clean cocciclean
3714 $(RM) -r .build $(UNIT_TEST_BIN)
3715 + $(RM) GIT-TEST-SUITES
3716 $(RM) po/git.pot po/git-core.pot
3717 $(RM) git.res
3718 $(RM) $(OBJECTS)
@@ -3863,7 +3872,26 @@ $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \
3872 $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
3873 $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
3874
3875 +GIT-TEST-SUITES: FORCE
3876 + @FLAGS='$(UNIT_TESTS_SUITES)'; \
3877 + if test x"$$FLAGS" != x"`cat GIT-TEST-SUITES 2>/dev/null`" ; then \
3878 + echo >&2 " * new test suites"; \
3879 + echo "$$FLAGS" >GIT-TEST-SUITES; \
3880 + fi
3881 +
3882 +$(UNIT_TEST_DIR)/clar-decls.h: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(UNIT_TESTS_SUITES)) GIT-TEST-SUITES
3883 + $(QUIET_GEN)for suite in $(UNIT_TESTS_SUITES); do \
3884 + sed -ne "s/^\(void test_$${suite}__[a-zA-Z_0-9][a-zA-Z_0-9]*(void)$$\)/extern \1;/p" $(UNIT_TEST_DIR)/$$suite.c; \
3885 + done >$@
3886 +$(UNIT_TEST_DIR)/clar.suite: $(UNIT_TEST_DIR)/clar-decls.h
3887 + $(QUIET_GEN)awk -f $(UNIT_TEST_DIR)/clar-generate.awk $< >$(UNIT_TEST_DIR)/clar.suite
3888 +$(UNIT_TESTS_OBJS): $(UNIT_TEST_DIR)/clar-decls.h
3889 +$(UNIT_TESTS_OBJS): EXTRA_CPPFLAGS = -I$(UNIT_TEST_DIR)
3890 +$(UNIT_TESTS_PROG): $(UNIT_TEST_DIR)/clar.suite $(UNIT_TESTS_OBJS) $(GITLIBS) GIT-LDFLAGS
3891 + $(call mkdir_p_parent_template)
3892 + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
3893 +
3894 .PHONY: build-unit-tests unit-tests
3867 -build-unit-tests: $(UNIT_TEST_PROGS)
3868 -unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X
3895 +build-unit-tests: $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG)
3896 +unit-tests: $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) t/helper/test-tool$X
3897 $(MAKE) -C t/ unit-tests
t/Makefile
+1
@@ -48,6 +48,7 @@ CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.tes
48 CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl
49 UNIT_TEST_SOURCES = $(wildcard unit-tests/t-*.c)
50 UNIT_TEST_PROGRAMS = $(patsubst unit-tests/%.c,unit-tests/bin/%$(X),$(UNIT_TEST_SOURCES))
51 +UNIT_TEST_PROGRAMS += unit-tests/bin/unit-tests$(X)
52 UNIT_TESTS = $(sort $(UNIT_TEST_PROGRAMS))
53 UNIT_TESTS_NO_DIR = $(notdir $(UNIT_TESTS))
54
t/unit-tests/.gitignore
+2
@@ -1 +1,3 @@
1 /bin
2 +/clar.suite
3 +/clar-decls.h
t/unit-tests/clar-generate.awk new
+50
@@ -0,0 +1,50 @@
1 +function add_suite(suite, initialize, cleanup, count) {
2 + if (!suite) return
3 + suite_count++
4 + callback_count += count
5 + suites = suites " {\n"
6 + suites = suites " \"" suite "\",\n"
7 + suites = suites " " initialize ",\n"
8 + suites = suites " " cleanup ",\n"
9 + suites = suites " _clar_cb_" suite ", " count ", 1\n"
10 + suites = suites " },\n"
11 +}
12 +
13 +BEGIN {
14 + suites = "static struct clar_suite _clar_suites[] = {\n"
15 +}
16 +
17 +{
18 + print
19 + name = $3; sub(/\(.*$/, "", name)
20 + suite = name; sub(/^test_/, "", suite); sub(/__.*$/, "", suite)
21 + short_name = name; sub(/^.*__/, "", short_name)
22 + cb = "{ \"" short_name "\", &" name " }"
23 + if (suite != prev_suite) {
24 + add_suite(prev_suite, initialize, cleanup, count)
25 + if (callbacks) callbacks = callbacks "};\n"
26 + callbacks = callbacks "static const struct clar_func _clar_cb_" suite "[] = {\n"
27 + initialize = "{ NULL, NULL }"
28 + cleanup = "{ NULL, NULL }"
29 + count = 0
30 + prev_suite = suite
31 + }
32 + if (short_name == "initialize") {
33 + initialize = cb
34 + } else if (short_name == "cleanup") {
35 + cleanup = cb
36 + } else {
37 + callbacks = callbacks " " cb ",\n"
38 + count++
39 + }
40 +}
41 +
42 +END {
43 + add_suite(suite, initialize, cleanup, count)
44 + suites = suites "};"
45 + if (callbacks) callbacks = callbacks "};"
46 + print callbacks
47 + print suites
48 + print "static const size_t _clar_suite_count = " suite_count ";"
49 + print "static const size_t _clar_callback_count = " callback_count ";"
50 +}
t/unit-tests/unit-test.c new
+6
@@ -0,0 +1,6 @@
1 +#include "unit-test.h"
2 +
3 +int cmd_main(int argc UNUSED, const char **argv UNUSED)
4 +{
5 + return 0;
6 +}
t/unit-tests/unit-test.h new
+3
@@ -0,0 +1,3 @@
1 +#include "git-compat-util.h"
2 +#include "clar/clar.h"
3 +#include "clar-decls.h"