diff options
author | David Phillips <david@yeah.nah.nz> | 2021-02-13 18:21:36 +1300 |
---|---|---|
committer | David Phillips <david@yeah.nah.nz> | 2021-02-13 18:25:28 +1300 |
commit | 4710921284adbf5005421515fbab5dd1aa38e9aa (patch) | |
tree | 18d1850a58c58f0095c8752a76ef8584a2628dbc | |
parent | 8ab11293cdd2a47e7950fa9c488cbc504325cad3 (diff) | |
download | altimeter-4710921284adbf5005421515fbab5dd1aa38e9aa.tar.xz |
Add test runner
This patch adds a "magic" test runner which discovers all test cases in the
executable into which it is linked. This is achieved through the use of a
special macro used to declare tests, so that they are added onto a linker-
generated list. The linker list logic is largely taken from U-Boot source code,
with minor some tweaks made.
This linker-generated list is then used at runtime in order to pick out every
test case which can be run, resulting in a cross-file "suite" of unity unit
tests, rather than separate test executables per module. A sample test module,
test_test.c is provided to illustrate the test runner's ability to correctly
discover tests across multiple translation units.
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | linker_list.lds | 8 | ||||
-rw-r--r-- | test_runner.c | 21 | ||||
-rw-r--r-- | test_runner.h | 32 | ||||
-rw-r--r-- | test_test.c | 7 | ||||
-rw-r--r-- | test_util.c | 21 |
6 files changed, 76 insertions, 17 deletions
@@ -51,9 +51,11 @@ clean: test: \ $(UNITY_O) \ + build/native/test_runner.o \ build/native/test_util.o \ + build/native/test_test.o \ build/native/util.o - $(CC) $(LDFLAGS) -o $@ $^ + $(CC) $(LDFLAGS) -Wl,-T,linker_list.lds -o $@ $^ ./$@ # ELF for real hardware. No mock/sim hardware included (except barometer, diff --git a/linker_list.lds b/linker_list.lds new file mode 100644 index 0000000..bb70bd8 --- /dev/null +++ b/linker_list.lds @@ -0,0 +1,8 @@ +SECTIONS { + __test_runner_list_start = .; + .test_runner_list : { + KEEP(*(SORT(.test_runner_list*))); + } + __test_runner_list_end = .; +} +INSERT BEFORE .bss; diff --git a/test_runner.c b/test_runner.c new file mode 100644 index 0000000..9d3ad65 --- /dev/null +++ b/test_runner.c @@ -0,0 +1,21 @@ +#include <stdio.h> + +#include "test_runner.h" +#include "unity.h" + +/* unity requires these, even if empty */ +void setUp(){}; +void tearDown(){}; + +int main(void) +{ + struct test_fn *tests = list_head(struct test_fn, test_list); + size_t count = list_entry_count(struct test_fn, test_list); + printf("Suite has %zd tests\n", count); + UNITY_BEGIN(); + for (size_t i = 0; i < count; i++) { + UnitySetTestFile(tests[i].file); + UnityDefaultTestRun(tests[i].fn, tests[i].name, tests[i].line); + } + UNITY_END(); +} diff --git a/test_runner.h b/test_runner.h new file mode 100644 index 0000000..5b891b0 --- /dev/null +++ b/test_runner.h @@ -0,0 +1,32 @@ +#pragma once + +/* Linker-generated lists derived from U-Boot source `include/linker_lists.h` + * with minor modification to store function pointers */ +#define list_head(entry_type, list_name) \ + ({ static char head[0] __attribute__((__aligned__(4), unused, section(".test_runner_list_2_"#list_name"_1")));(entry_type*)&head; }) + +#define list_tail(entry_type, list_name) \ + ({ static char tail[0] __attribute__((__aligned__(4), unused, section(".test_runner_list_2_"#list_name"_3")));(entry_type*)&tail; }) + +#define list_entry_count(entry_type, list_name) \ + ({ \ + entry_type* head = list_head(entry_type, list_name); \ + entry_type* tail = list_tail(entry_type, list_name); \ + (size_t)(tail - head); \ + }) + +#define list_entry(entry_type, list_name, entry_name) \ + entry_type _test_runner_list_2_##list_name_2_##entry_name __attribute__((__aligned__(4), unused, section(".test_runner_list_2_"#list_name"_2_"#entry_name))) + +#define RUNNER_DECLARE_TEST(test_name) \ + void test_name(void);\ + list_entry(struct test_fn, test_list, test_name) = { .name = #test_name, .file = __FILE__, .line = __LINE__, .fn = test_name}; \ + void test_name(void) + +struct test_fn { + const char *name; + const char *file; + int line; + void (*fn)(); +}; + diff --git a/test_test.c b/test_test.c new file mode 100644 index 0000000..9a78c3e --- /dev/null +++ b/test_test.c @@ -0,0 +1,7 @@ +#include "test_runner.h" +#include "unity.h" + +RUNNER_DECLARE_TEST(test_test) +{ + TEST_ASSERT_EQUAL(0,0); +} diff --git a/test_util.c b/test_util.c index 81c3db7..c6b9b83 100644 --- a/test_util.c +++ b/test_util.c @@ -1,14 +1,13 @@ #include <string.h> +#include "test_runner.h" #include "unity.h" #include "util.h" -void setUp(){}; -void tearDown(){}; /* blank_to_eol should pad a buffer with spaces as far as it can, after an * existing string therein, without overrunning, while still terminating */ -void test_blank_to_eol_happy(void) +RUNNER_DECLARE_TEST(test_blank_to_eol_happy) { char buf[11] = "hello"; blank_to_eol(buf, sizeof(buf)); @@ -17,7 +16,7 @@ void test_blank_to_eol_happy(void) /* blanking an empty string to end of buffer should fill entire buffer with * spaces until the last byte, which should be null terminator */ -void test_blank_to_eol_empty(void) +RUNNER_DECLARE_TEST(test_blank_to_eol_empty) { char buf[5] = ""; blank_to_eol(buf, sizeof(buf)); @@ -25,7 +24,7 @@ void test_blank_to_eol_empty(void) } /* blanking should not overrun the specified buffer size at all */ -void test_blank_to_eol_no_overrun(void) +RUNNER_DECLARE_TEST(test_blank_to_eol_no_overrun) { char buf[20] = { 'h', 'e', 'l', 'l', 'o', '\0', @@ -41,7 +40,7 @@ void test_blank_to_eol_no_overrun(void) /* blanking a buffer which already has no '\0' within it should result in the * buffer being terminated, truncating contents minimally as necessary to fit * a '\0' */ -void test_blank_to_eol_overrun(void) +RUNNER_DECLARE_TEST(test_blank_to_eol_overrun) { char buf[20] = "hello, world!"; blank_to_eol(buf, 6); @@ -50,13 +49,3 @@ void test_blank_to_eol_overrun(void) * doesn't test OOB reads, only writes */ TEST_ASSERT_EQUAL_STRING(" world!", &buf[6]); } - -int main(void) -{ - UNITY_BEGIN(); - RUN_TEST(test_blank_to_eol_happy); - RUN_TEST(test_blank_to_eol_empty); - RUN_TEST(test_blank_to_eol_no_overrun); - RUN_TEST(test_blank_to_eol_overrun); - return UNITY_END(); -} |