tests: Use heap memory instead of linker tricks for registering test cases

(Test case registration buffer gets freed before the test case runs.)
This commit is contained in:
Angus Gratton 2016-02-17 08:28:56 +11:00
parent 2105f2b035
commit b2b1e42c59
4 changed files with 54 additions and 54 deletions

View file

@ -1,6 +1,5 @@
PROGRAM=tests PROGRAM=tests
EXTRA_LINKER_SCRIPTS = $(PROGRAM_DIR)ld/tests.ld
PROGRAM_SRC_DIR = $(PROGRAM_DIR) $(PROGRAM_DIR)cases PROGRAM_SRC_DIR = $(PROGRAM_DIR) $(PROGRAM_DIR)cases
# Add unity test framework headers & core source file # Add unity test framework headers & core source file

View file

@ -20,15 +20,15 @@ typedef enum {
typedef void (testcase_fn_t)(void); typedef void (testcase_fn_t)(void);
typedef struct { typedef struct {
char *name; const char *name;
char *file; const char *file;
uint8_t line; int line;
testcase_type_t type; testcase_type_t type;
testcase_fn_t *a_fn; testcase_fn_t *a_fn;
testcase_fn_t *b_fn; testcase_fn_t *b_fn;
} testcase_t; } 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, /* Register a test case using these macros. Use DEFINE_SOLO_TESTCASE for single-MCU tests,
and DEFINE_TESTCASE for all other test types. 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_TESTCASE_COMMON(NAME, TYPE, A_##NAME, B_##NAME)
#define _DEFINE_TESTCASE_COMMON(NAME, TYPE, A_FN, B_FN) \ #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, \
}; \
void __attribute__((constructor)) testcase_ctor_##NAME() { \ 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 #endif

View file

@ -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
}

View file

@ -1,10 +1,7 @@
#include "testcase.h" #include "testcase.h"
#include <stdlib.h> #include <stdlib.h>
#include <esp/uart.h> #include <esp/uart.h>
#include <string.h>
/* Linker sets up these pointers to the registered test cases */
extern const testcase_t _testcases_start;
extern const testcase_t _testcases_end;
/* Convert requirement enum to a string we can print */ /* Convert requirement enum to a string we can print */
static const char *get_requirements_name(const testcase_type_t arg) { 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) void user_init(void)
{ {
const testcase_t *cases_start = &_testcases_start;
const testcase_t *cases_end= &_testcases_end;
uart_set_baud(0, 115200); uart_set_baud(0, 115200);
printf("esp-open-rtos test runner.\n"); 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", testcases_count);
printf("%d test cases are defined:\n\n", cases_end-cases_start); for(int i = 0; i < testcases_count; i++) {
for(const testcase_t *icase=cases_start; icase != cases_end; icase++) { printf("CASE %d = %s %s\n", i, testcases[i].name, get_requirements_name(testcases[i].type));
printf("CASE %d = %s %s\n", icase-cases_start, icase->name, get_requirements_name(icase->type));
} }
printf("Enter A or B then number of test case to run, ie A0.\n"); 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) { if(case_idx == -1) {
printf("Invalid case index"); 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"); 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"); printf("No ESP type B for 'SOLO' test cases.\n");
} else { } else {
break; break;
@ -78,25 +93,23 @@ void user_init(void)
type = 'A'; type = 'A';
else if (type == 'b') else if (type == 'b')
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, 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, testcase.name, get_requirements_name(testcase.type), type,
tcase->file, tcase->line); testcase.file, testcase.line);
Unity.CurrentTestName = tcase->name; Unity.CurrentTestName = testcase.name;
Unity.TestFile = tcase->file; Unity.TestFile = testcase.file;
Unity.CurrentTestLineNumber = tcase->line; Unity.CurrentTestLineNumber = testcase.line;
Unity.NumberOfTests = 1; Unity.NumberOfTests = 1;
if(type=='A') if(type=='A')
cases_start[case_idx].a_fn(); testcase.a_fn();
else else
cases_start[case_idx].b_fn(); testcase.b_fn();
TEST_FAIL_MESSAGE("\n\nTest initialisation routine returned without calling TEST_PASS. Buggy test?"); 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)
{
}