From b2b1e42c5971d1199afd080d946a238093dc25b2 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 17 Feb 2016 08:28:56 +1100 Subject: [PATCH] tests: Use heap memory instead of linker tricks for registering test cases (Test case registration buffer gets freed before the test case runs.) --- tests/Makefile | 1 - tests/include/testcase.h | 27 ++++++++-------- tests/ld/tests.ld | 11 ------- tests/test_main.c | 69 ++++++++++++++++++++++++---------------- 4 files changed, 54 insertions(+), 54 deletions(-) delete mode 100644 tests/ld/tests.ld diff --git a/tests/Makefile b/tests/Makefile index bd95ab3..476fc7b 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,6 +1,5 @@ PROGRAM=tests -EXTRA_LINKER_SCRIPTS = $(PROGRAM_DIR)ld/tests.ld PROGRAM_SRC_DIR = $(PROGRAM_DIR) $(PROGRAM_DIR)cases # Add unity test framework headers & core source file diff --git a/tests/include/testcase.h b/tests/include/testcase.h index 01c3198..c696596 100644 --- a/tests/include/testcase.h +++ b/tests/include/testcase.h @@ -20,15 +20,15 @@ typedef enum { typedef void (testcase_fn_t)(void); typedef struct { - char *name; - char *file; - uint8_t line; + const char *name; + const char *file; + int line; testcase_type_t type; testcase_fn_t *a_fn; testcase_fn_t *b_fn; } testcase_t; -void testcase_register(const testcase_t *ignored); +void testcase_register(const testcase_t *testcase); /* Register a test case using these macros. Use DEFINE_SOLO_TESTCASE for single-MCU tests, and DEFINE_TESTCASE for all other test types. @@ -43,17 +43,16 @@ void testcase_register(const testcase_t *ignored); _DEFINE_TESTCASE_COMMON(NAME, TYPE, A_##NAME, B_##NAME) -#define _DEFINE_TESTCASE_COMMON(NAME, TYPE, A_FN, B_FN) \ - const __attribute__((section(".testcases.text"))) \ - testcase_t testcase_##NAME = { .name = #NAME, \ - .file = __FILE__, \ - .line = __LINE__, \ - .type = TYPE, \ - .a_fn = A_FN, \ - .b_fn = B_FN, \ - }; \ +#define _DEFINE_TESTCASE_COMMON(NAME, TYPE, A_FN, B_FN) \ void __attribute__((constructor)) testcase_ctor_##NAME() { \ - testcase_register(&testcase_##NAME); \ + const testcase_t testcase = { .name = #NAME, \ + .file = __FILE__, \ + .line = __LINE__, \ + .type = TYPE, \ + .a_fn = A_FN, \ + .b_fn = B_FN, \ + }; \ + testcase_register(&testcase); \ } #endif diff --git a/tests/ld/tests.ld b/tests/ld/tests.ld deleted file mode 100644 index 2825c64..0000000 --- a/tests/ld/tests.ld +++ /dev/null @@ -1,11 +0,0 @@ -/* Extra linker script sections used only for test case registration */ -SECTIONS -{ - .irom0.text : ALIGN(4) - { - _testcases_start = ABSOLUTE(.); - *(.testcases.text) - _testcases_end = ABSOLUTE(.); - } > irom0_0_seg -} - diff --git a/tests/test_main.c b/tests/test_main.c index 48f2684..a93d6e1 100644 --- a/tests/test_main.c +++ b/tests/test_main.c @@ -1,10 +1,7 @@ #include "testcase.h" #include #include - -/* Linker sets up these pointers to the registered test cases */ -extern const testcase_t _testcases_start; -extern const testcase_t _testcases_end; +#include /* Convert requirement enum to a string we can print */ static const char *get_requirements_name(const testcase_type_t arg) { @@ -20,16 +17,34 @@ static const char *get_requirements_name(const testcase_type_t arg) { } } +static testcase_t *testcases; +static uint32_t testcases_count; +static uint32_t testcases_alloc; + +void testcase_register(const testcase_t *testcase) +{ + /* Grow the testcases buffer to fit the new test case, + this buffer will be freed before the test runs. + */ + if(testcases_count == testcases_alloc) { + testcases_alloc += 1; + testcases = realloc(testcases, testcases_alloc * sizeof(testcase_t)); + if(!testcases) { + printf("Failed to reallocate test case register length %d\n", testcases_alloc); + testcases_count = 0; + testcases_alloc = 0; + } + } + memcpy(&testcases[testcases_count++], testcase, sizeof(testcase_t)); +} + void user_init(void) { - const testcase_t *cases_start = &_testcases_start; - const testcase_t *cases_end= &_testcases_end; uart_set_baud(0, 115200); printf("esp-open-rtos test runner.\n"); - printf("testcases_start %p testcases_end %p\n", cases_start, cases_end); - printf("%d test cases are defined:\n\n", cases_end-cases_start); - for(const testcase_t *icase=cases_start; icase != cases_end; icase++) { - printf("CASE %d = %s %s\n", icase-cases_start, icase->name, get_requirements_name(icase->type)); + printf("%d test cases are defined:\n\n", testcases_count); + for(int i = 0; i < testcases_count; i++) { + printf("CASE %d = %s %s\n", i, testcases[i].name, get_requirements_name(testcases[i].type)); } printf("Enter A or B then number of test case to run, ie A0.\n"); @@ -65,10 +80,10 @@ void user_init(void) if(case_idx == -1) { printf("Invalid case index"); } - else if(case_idx < 0 || case_idx >= cases_end-cases_start) { + else if(case_idx < 0 || case_idx >= testcases_count) { printf("Test case index out of range.\n"); } - else if((type == 'b' || type =='B') && cases_start[case_idx].type == SOLO) { + else if((type == 'b' || type =='B') && testcases[case_idx].type == SOLO) { printf("No ESP type B for 'SOLO' test cases.\n"); } else { break; @@ -78,25 +93,23 @@ void user_init(void) type = 'A'; else if (type == 'b') type = 'B'; - const testcase_t *tcase = &cases_start[case_idx]; + testcase_t testcase; + memcpy(&testcase, &testcases[case_idx], sizeof(testcase_t)); + /* Free the register of test cases now we have the one we're running */ + free(testcases); + testcases_alloc = 0; + testcases_count = 0; + printf("\nRunning test case %d (%s %s) as instance %c \nDefinition at %s:%d\n***\n", case_idx, - tcase->name, get_requirements_name(tcase->type), type, - tcase->file, tcase->line); - Unity.CurrentTestName = tcase->name; - Unity.TestFile = tcase->file; - Unity.CurrentTestLineNumber = tcase->line; + testcase.name, get_requirements_name(testcase.type), type, + testcase.file, testcase.line); + Unity.CurrentTestName = testcase.name; + Unity.TestFile = testcase.file; + Unity.CurrentTestLineNumber = testcase.line; Unity.NumberOfTests = 1; if(type=='A') - cases_start[case_idx].a_fn(); + testcase.a_fn(); else - cases_start[case_idx].b_fn(); + testcase.b_fn(); TEST_FAIL_MESSAGE("\n\nTest initialisation routine returned without calling TEST_PASS. Buggy test?"); } - -/* testcase_register is a no-op function, we just need it so the linker - knows to pull in the argument at link time. - */ -void testcase_register(const testcase_t __attribute__((unused)) *ignored) -{ - -}