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:
parent
2105f2b035
commit
b2b1e42c59
4 changed files with 54 additions and 54 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue