util: add a global setup option to our munit wrapper

This allows us to handle/manipulate argv and/or pass a userdata to the
tests. Same ELF section approach as we already have with the MUNIT_TEST
macro but this time it's for a global setup.

Note that for the generated __start and __stop section variables we have
to have at least one entry in that section which we do with one
hardcoded (and ignored) one.
This commit is contained in:
Peter Hutterer 2023-10-18 15:08:57 +10:00
parent 6758b9970d
commit f558eedae2
2 changed files with 71 additions and 1 deletions

View file

@ -35,6 +35,14 @@
* __attribute__(section) documentation.
*/
DECLARE_TEST_SECTION();
DECLARE_GLOBAL_SETUP_SECTION();
/* If the tests don't use MUNIT_GLOBAL_SETUP the the above has no linkage, so
* let's always define a noop internal one.
*/
MUNIT_GLOBAL_SETUP(__internal)
{
}
int
munit_tests_run(int argc, char **argv)
@ -55,6 +63,20 @@ munit_tests_run(int argc, char **argv)
tests[idx++] = test;
}
struct munit_setup setup = {
.argc = argc,
.argv = argv,
.userdata = NULL,
};
size_t setup_count = 0;
foreach_setup(s) {
if (!streq(s->name, "__internal")) {
assert(setup_count == 0); /* Only one setup func per suite */
s->func(&setup);
++setup_count;
}
}
MunitSuite suite = {
"",
tests,
@ -67,7 +89,7 @@ munit_tests_run(int argc, char **argv)
const struct rlimit corelimit = { 0, 0 };
setrlimit(RLIMIT_CORE, &corelimit);
int rc = munit_suite_main(&suite, NULL, argc, argv);
int rc = munit_suite_main(&suite, setup.userdata, setup.argc, setup.argv);
for (idx = 0; idx < count; idx++)
free(tests[idx].name);

View file

@ -63,6 +63,34 @@ struct test_function {
munit_test_func_t func; /* test function */
} __attribute__((aligned(16)));
/**
* Put at the top of the file somewhere, declares the start/stop for the setup section we need.
*/
#define DECLARE_GLOBAL_SETUP_SECTION() \
extern const struct global_setup_function __start_setup_functions_section, __stop_setup_functions_section
/**
* Helper to loop through each setup function.
*/
#define foreach_setup(s_) \
for (const struct global_setup_function *s_ = &__start_setup_functions_section; \
s_ < &__stop_setup_functions_section; \
s_++)
struct munit_setup {
int argc;
char **argv;
void *userdata;
};
typedef void (*munit_setup_func_t)(struct munit_setup *setup);
struct global_setup_function {
const char *name; /* function name */
munit_setup_func_t func; /* setup function */
} __attribute__((aligned(16)));
/**
* Defines a struct test_function in a custom ELF section that we can then
* loop over in munit_tests_run() to extract the tests. This removes the
@ -79,5 +107,25 @@ __attribute__((section("test_functions_section"))) = { \
}; \
static MunitResult _func(const MunitParameter params[], void *user_data)
/**
* Defines a struct global_setup_function in a custom ELF section that we can
* then find in munit_tests_run() to do setup based on argv and/or pass userdata
* to the tests.
*
* Note that there can only be one of those per process.
*
* The argument to this function is a pointer to a struct munit_setup that has
* argc/argv and a (initially NULL) userdata pointer.
*/
#define MUNIT_GLOBAL_SETUP(_func) \
static void _func(struct munit_setup *setup); \
static const struct global_setup_function _setup_##_func \
__attribute__((used)) \
__attribute__((section("setup_functions_section"))) = { \
.name = #_func, \
.func = _func, \
}; \
static void _func(struct munit_setup *setup) \
int
munit_tests_run(int argc, char **argv);