Am 31.07.24 um 11:04 schrieb Patrick Steinhardt: > 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". > > +$(UNIT_TEST_DIR)/clar.suite: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(UNIT_TESTS_SUITES)) GIT-TEST-SUITES > + $(QUIET_GEN)$(UNIT_TEST_DIR)/clar/generate.py $(UNIT_TEST_DIR) >/dev/null This uses the Python interpreter from the shebang line in generate.py, which is python. On my system I only have python3 and python3.12, but not python. Easily fixed, of course, but a way to configure the interpreter name would be nice. This gave me extra motivation to come up with the clunky patch below to replace Python with sed and awk. That and your statement that clar doesn't have to be perfect in the other thread. ;) It reverses the order of dependencies (builds clar-decls.h first), not sure if that has downsides. And the sed pattern is simpler than the one in generate.py, just out of laziness. René --- .gitignore | 1 - Makefile | 20 ++++++----------- t/unit-tests/.gitignore | 1 - t/unit-tests/generate.awk | 47 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 t/unit-tests/generate.awk diff --git a/.gitignore b/.gitignore index 6687bd6db4..8caf3700c2 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ /GIT-PYTHON-VARS /GIT-SCRIPT-DEFINES /GIT-SPATCH-DEFINES -/GIT-TEST-SUITES /GIT-USER-AGENT /GIT-VERSION-FILE /bin-wrappers/ diff --git a/Makefile b/Makefile index 8ebcbdc95a..1ffde38de5 100644 --- a/Makefile +++ b/Makefile @@ -3706,7 +3706,7 @@ cocciclean: clean: profile-clean coverage-clean cocciclean $(RM) -r .build $(UNIT_TEST_BIN) - $(RM) $(UNIT_TEST_DIR)/clar.suite $(UNIT_TEST_DIR)/.clarcache + $(RM) $(UNIT_TEST_DIR)/clar.suite $(UNIT_TEST_DIR)/clar-decls.h $(RM) po/git.pot po/git-core.pot $(RM) git.res $(RM) $(OBJECTS) @@ -3866,19 +3866,13 @@ $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \ $(filter %.o,$^) $(filter %.a,$^) $(LIBS) -GIT-TEST-SUITES: FORCE - @FLAGS='$(UNIT_TESTS_SUITES)'; \ - if test x"$$FLAGS" != x"`cat GIT-TEST-SUITES 2>/dev/null`" ; then \ - echo >&2 " * new test suites"; \ - echo "$$FLAGS" >GIT-TEST-SUITES; \ - rm -f $(UNIT_TESTS_DIR)/.clarcache; \ - fi +$(UNIT_TEST_DIR)/clar.suite: $(UNIT_TEST_DIR)/clar-decls.h $(UNIT_TEST_DIR)/generate.awk + $(QUIET_GEN)awk -f $(UNIT_TEST_DIR)/generate.awk $(UNIT_TEST_DIR)/clar-decls.h >$@ +$(UNIT_TEST_DIR)/clar-decls.h: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(UNIT_TESTS_SUITES)) + $(QUIET_GEN)for suite in $(UNIT_TESTS_SUITES); do \ + 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; \ + done >$@ -$(UNIT_TEST_DIR)/clar.suite: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(UNIT_TESTS_SUITES)) GIT-TEST-SUITES - $(QUIET_GEN)$(UNIT_TEST_DIR)/clar/generate.py $(UNIT_TEST_DIR) >/dev/null - @touch $@ -$(UNIT_TEST_DIR)/clar-decls.h: $(UNIT_TEST_DIR)/clar.suite - $(QUIET_GEN)grep '^extern void' $^ >$@ $(UNIT_TESTS_OBJS): $(UNIT_TEST_DIR)/clar-decls.h $(UNIT_TESTS_OBJS): EXTRA_CPPFLAGS = -I$(UNIT_TEST_DIR) -I$(UNIT_TEST_DIR)/clar $(UNIT_TESTS_PROG): $(UNIT_TEST_DIR)/clar.suite $(UNIT_TESTS_OBJS) $(GITLIBS) GIT-LDFLAGS diff --git a/t/unit-tests/.gitignore b/t/unit-tests/.gitignore index b8d46f7bb1..d0632ec7f9 100644 --- a/t/unit-tests/.gitignore +++ b/t/unit-tests/.gitignore @@ -1,4 +1,3 @@ /bin -/.clarcache /clar.suite /clar-decls.h diff --git a/t/unit-tests/generate.awk b/t/unit-tests/generate.awk new file mode 100644 index 0000000000..3c78253866 --- /dev/null +++ b/t/unit-tests/generate.awk @@ -0,0 +1,47 @@ +function add_suite(suite, initialize, cleanup, count) { + if (!suite) return + suite_count++ + callback_count += count + suites = suites " {\n" + suites = suites " \"" suite "\",\n" + suites = suites " " initialize ",\n" + suites = suites " " cleanup ",\n" + suites = suites " _clar_cb_" suite ", " count ", 1\n" + suites = suites " },\n" +} +BEGIN { + suites = "static struct clar_suite _clar_suites[] = {\n" +} +{ + print + name = $3; sub(/\(.*$/, "", name) + suite = name; sub(/^test_/, "", suite); sub(/__.*$/, "", suite) + short_name = name; sub(/^.*__/, "", short_name) + cb = "{ \"" short_name "\", &" name " }" + if (suite != prev_suite) { + add_suite(prev_suite, initialize, cleanup, count) + if (callbacks) callbacks = callbacks "};\n" + callbacks = callbacks "static const struct clar_func _clar_cb_" suite "[] = {\n" + initialize = "{ NULL, NULL }" + cleanup = "{ NULL, NULL }" + count = 0 + prev_suite = suite + } + if (short_name == "initialize") { + initialize = cb + } else if (short_name == "cleanup") { + cleanup = cb + } else { + callbacks = callbacks " " cb ",\n" + count++ + } +} +END { + add_suite(suite, initialize, cleanup, count) + suites = suites "};" + if (callbacks) callbacks = callbacks "};" + print callbacks + print suites + print "static const size_t _clar_suite_count = " suite_count ";" + print "static const size_t _clar_callback_count = " callback_count ";" +} -- 2.46.0