ztest: Fix userspace ztests in new API

Update the new API to use K_USER as the flags for both
CONFIG_USERSPACE and CONFIG_TEST_USERSPACE. Also, fix the linker
script to properly include the suites, tests, and rules.

Fixes #44108

Signed-off-by: Yuval Peress <peress@google.com>
This commit is contained in:
Yuval Peress 2022-04-05 11:42:31 -06:00 committed by Stephanos Ioannidis
commit 86cadf9283
23 changed files with 184 additions and 123 deletions

View file

@ -8,3 +8,7 @@ zephyr_include_directories_ifdef(CONFIG_TEST
add_subdirectory_ifdef(CONFIG_COVERAGE_GCOV coverage)
zephyr_library_sources_ifdef(CONFIG_TEST_BUSY_SIM busy_sim/busy_sim.c)
if(NOT BOARD STREQUAL unit_testing)
zephyr_linker_sources(RODATA include/ztest.ld)
endif()

View file

@ -3,26 +3,3 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
SECTIONS
{
.data.ztest_suite_node_area : ALIGN(4)
{
_ztest_suite_node_list_start = .;
KEEP(*(SORT_BY_NAME(._ztest_suite_node.static.*)))
_ztest_suite_node_list_end = .;
}
.data.ztest_unit_test_area : ALIGN(4)
{
_ztest_unit_test_list_start = .;
KEEP(*(SORT_BY_NAME(._ztest_unit_test.static.*)))
_ztest_unit_test_list_end = .;
}
.data.ztest_test_rule_area : ALIGN(4)
{
_ztest_test_rule_list_start = .;
KEEP(*(SORT_BY_NAME(._ztest_test_rule.static.*)))
_ztest_test_rule_list_end = .;
}
}
INSERT AFTER .data;

View file

@ -0,0 +1,28 @@
/*
* Copyright 2022 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
SECTIONS
{
.data.ztest_suite_node_area : ALIGN(4)
{
_ztest_suite_node_list_start = .;
KEEP(*(SORT_BY_NAME(._ztest_suite_node.static.*)))
_ztest_suite_node_list_end = .;
}
.data.ztest_unit_test_area : ALIGN(4)
{
_ztest_unit_test_list_start = .;
KEEP(*(SORT_BY_NAME(._ztest_unit_test.static.*)))
_ztest_unit_test_list_end = .;
}
.data.ztest_test_rule_area : ALIGN(4)
{
_ztest_test_rule_list_start = .;
KEEP(*(SORT_BY_NAME(._ztest_test_rule.static.*)))
_ztest_test_rule_list_end = .;
}
}
INSERT AFTER .data;

View file

@ -59,7 +59,7 @@ struct ztest_suite_node {
*/
bool (*predicate)(const void *state);
/** Stats */
struct ztest_suite_stats stats;
struct ztest_suite_stats *stats;
};
extern struct ztest_suite_node _ztest_suite_node_list_start[];
@ -78,10 +78,12 @@ extern struct ztest_suite_node _ztest_suite_node_list_end[];
*/
#define ztest_register_test_suite(SUITE_NAME, PREDICATE, args...) \
ztest_test_suite(SUITE_NAME, ##args); \
struct ztest_suite_stats UTIL_CAT(z_ztest_test_node_stats_, SUITE_NAME); \
static STRUCT_SECTION_ITERABLE(ztest_suite_node, z_ztest_test_node_##SUITE_NAME) = { \
.name = #SUITE_NAME, \
.suite = _##SUITE_NAME, \
.predicate = PREDICATE, \
.stats = &UTIL_CAT(z_ztest_test_node_stats_, SUITE_NAME), \
};
/**

View file

@ -17,6 +17,12 @@
#include <zephyr/init.h>
#include <stdbool.h>
#if defined(CONFIG_USERSPACE)
#define __USERSPACE_FLAGS (K_USER)
#else
#define __USERSPACE_FLAGS (0)
#endif
#ifdef __cplusplus
extern "C" {
#endif
@ -30,6 +36,7 @@ struct ztest_unit_test {
extern struct ztest_unit_test _ztest_unit_test_list_start[];
extern struct ztest_unit_test _ztest_unit_test_list_end[];
#define ZTEST_TEST_COUNT (_ztest_unit_test_list_end - _ztest_unit_test_list_start)
/**
* Stats about a ztest suite
@ -49,31 +56,31 @@ struct ztest_suite_stats {
*/
struct ztest_suite_node {
/** The name of the test suite. */
const char *name;
const char * const name;
/**
* Setup function to run before running this suite
*
* @return Pointer to the data structure that will be used throughout this test suite
*/
void *(*setup)(void);
void *(*const setup)(void);
/**
* Function to run before each test in this suite
*
* @param data The test suite's data returned from setup()
*/
void (*before)(void *data);
void (*const before)(void *data);
/**
* Function to run after each test in this suite
*
* @param data The test suite's data returned from setup()
*/
void (*after)(void *data);
void (*const after)(void *data);
/**
* Teardown function to run after running this suite
*
* @param data The test suite's data returned from setup()
*/
void (*teardown)(void *data);
void (*const teardown)(void *data);
/**
* An optional predicate function to determine if the test should run. If NULL, then the
* test will only run once on the first attempt.
@ -81,13 +88,15 @@ struct ztest_suite_node {
* @param state The current state of the test application.
* @return True if the suite should be run; false to skip.
*/
bool (*predicate)(const void *state);
bool (*const predicate)(const void *state);
/** Stats */
struct ztest_suite_stats stats;
struct ztest_suite_stats * const stats;
};
extern struct ztest_suite_node _ztest_suite_node_list_start[];
extern struct ztest_suite_node _ztest_suite_node_list_end[];
#define ZTEST_SUITE_COUNT (_ztest_suite_node_list_end - _ztest_suite_node_list_start)
/**
* Create and register a ztest suite. Using this macro creates a new test suite (using
@ -103,24 +112,27 @@ extern struct ztest_suite_node _ztest_suite_node_list_end[];
* @param after_fn The function to call after each unit test in this suite
* @param teardown_fn The function to call after running all the tests in this suite
*/
#define ZTEST_SUITE(SUITE_NAME, PREDICATE, setup_fn, before_fn, after_fn, teardown_fn) \
static STRUCT_SECTION_ITERABLE(ztest_suite_node, \
UTIL_CAT(z_ztest_test_node_, SUITE_NAME)) = { \
.name = STRINGIFY(SUITE_NAME), \
.setup = (setup_fn), \
.before = (before_fn), \
.after = (after_fn), \
.teardown = (teardown_fn), \
.predicate = PREDICATE, \
#define ZTEST_SUITE(SUITE_NAME, PREDICATE, setup_fn, before_fn, after_fn, teardown_fn) \
struct ztest_suite_stats UTIL_CAT(z_ztest_test_node_stats_, SUITE_NAME); \
static const STRUCT_SECTION_ITERABLE(ztest_suite_node, \
UTIL_CAT(z_ztest_test_node_, SUITE_NAME)) = { \
.name = STRINGIFY(SUITE_NAME), \
.setup = (setup_fn), \
.before = (before_fn), \
.after = (after_fn), \
.teardown = (teardown_fn), \
.predicate = PREDICATE, \
.stats = &UTIL_CAT(z_ztest_test_node_stats_, SUITE_NAME), \
}
/**
* Run the registered unit tests which return true from their pragma function.
*
* @param state The current state of the machine as it relates to the test executable.
* @return The number of tests that ran.
*/
int ztest_run_test_suites(const void *state);
__syscall int ztest_run_test_suites(const void *state);
#include <syscalls/ztest_test_new.h>
/**
* @brief Fails the test if any of the registered tests did not run.
@ -240,7 +252,7 @@ static inline void unit_test_noop(void)
* @param suite The name of the test suite to attach this test
* @param fn The test function to call.
*/
#define ZTEST_USER(suite, fn) Z_ZTEST(suite, fn, COND_CODE_1(CONFIG_USERSPACE, (K_USER), (0)))
#define ZTEST_USER(suite, fn) Z_ZTEST(suite, fn, K_USER)
/**
* @brief Define a test function
@ -262,7 +274,7 @@ static inline void unit_test_noop(void)
* @param suite The name of the test suite to attach this test
* @param fn The test function to call.
*/
#define ZTEST_USER_F(suite, fn) Z_ZTEST_F(suite, fn, COND_CODE_1(CONFIG_USERSPACE, (K_USER), (0)))
#define ZTEST_USER_F(suite, fn) Z_ZTEST_F(suite, fn, K_USER)
/**
* @brief Test rule callback function signature
@ -352,4 +364,6 @@ extern struct k_mem_partition ztest_mem_partition;
}
#endif
#include <syscalls/ztest_test_new.h>
#endif /* ZEPHYR_TESTSUITE_ZTEST_TEST_H_ */

View file

@ -381,6 +381,7 @@ static int run_test(struct unit_test *test)
test->thread_options | K_INHERIT_PERMS,
K_FOREVER);
k_thread_access_grant(&ztest_thread, test);
if (test->name != NULL) {
k_thread_name_set(&ztest_thread, test->name);
}
@ -468,7 +469,7 @@ int ztest_run_registered_test_suites(const void *state)
int count = 0;
for (ptr = _ztest_suite_node_list_start; ptr < _ztest_suite_node_list_end; ++ptr) {
struct ztest_suite_stats *stats = &ptr->stats;
struct ztest_suite_stats *stats = ptr->stats;
bool should_run = true;
if (ptr->predicate != NULL) {
@ -498,7 +499,7 @@ void ztest_verify_all_registered_test_suites_ran(void)
struct ztest_suite_node *ptr;
for (ptr = _ztest_suite_node_list_start; ptr < _ztest_suite_node_list_end; ++ptr) {
if (ptr->stats.run_count < 1) {
if (ptr->stats->run_count < 1) {
PRINT("ERROR: Test '%s' did not run.\n", ptr->name);
all_tests_run = false;
}

View file

@ -5,7 +5,6 @@
*/
#include <ztest.h>
#include <stdio.h>
#include <zephyr/app_memory/app_memdomain.h>
#ifdef CONFIG_USERSPACE
#include <zephyr/sys/libc-hooks.h>
@ -434,6 +433,7 @@ static int run_test(struct ztest_suite_node *suite, struct ztest_unit_test *test
CONFIG_ZTEST_THREAD_PRIORITY,
test->thread_options | K_INHERIT_PERMS, K_FOREVER);
k_thread_access_grant(&ztest_thread, suite, test, suite->stats);
if (test->name != NULL) {
k_thread_name_set(&ztest_thread, test->name);
}
@ -507,20 +507,20 @@ struct ztest_unit_test *ztest_get_next_test(const char *suite, struct ztest_unit
}
#ifdef CONFIG_ZTEST_SHUFFLE
static void z_ztest_shuffle(void *array, size_t num_items, void *tmp, size_t elem_size)
static void z_ztest_shuffle(void *dest[], intptr_t start, size_t num_items, size_t element_size)
{
char *arr = array;
for (size_t i = 0; i < num_items; ++i) {
int pos = sys_rand32_get() % num_items;
const int start_pos = pos;
for (int i = num_items - 1; i > 0; i--) {
int j = sys_rand32_get() % (i + 1);
if (i != j) {
memcpy(tmp, arr + (j * elem_size), elem_size);
memcpy(arr + (j * elem_size), arr + (i * elem_size), elem_size);
memcpy(arr + (i * elem_size), tmp, elem_size);
/* Get the next valid position */
while (dest[pos] != NULL) {
pos = (pos + 1) % num_items;
__ASSERT_NO_MSG(pos != start_pos);
}
}
dest[pos] = (void *)(start + (i * element_size));
}
}
#endif /* CONFIG_ZTEST_SHUFFLE */
@ -530,10 +530,6 @@ static int z_ztest_run_test_suite_ptr(struct ztest_suite_node *suite)
void *data = NULL;
int fail = 0;
#ifdef CONFIG_ZTEST_SHUFFLE
struct ztest_unit_test tmp;
#endif
if (test_status < 0) {
return test_status;
}
@ -555,11 +551,24 @@ static int z_ztest_run_test_suite_ptr(struct ztest_suite_node *suite)
fail = 0;
#ifdef CONFIG_ZTEST_SHUFFLE
z_ztest_shuffle(_ztest_unit_test_list_start,
_ztest_unit_test_list_end - _ztest_unit_test_list_start, &tmp,
sizeof(struct ztest_unit_test));
#endif
struct ztest_unit_test *tests_to_run[ZTEST_TEST_COUNT];
memset(tests_to_run, 0, ZTEST_TEST_COUNT * sizeof(struct ztest_unit_test *));
z_ztest_shuffle((void **)tests_to_run, (intptr_t)_ztest_unit_test_list_start,
ZTEST_TEST_COUNT, sizeof(struct ztest_unit_test));
for (size_t i = 0; i < ZTEST_TEST_COUNT; ++i) {
test = tests_to_run[i];
/* Make sure that the test belongs to this suite */
if (strcmp(suite->name, test->test_suite_name) != 0) {
continue;
}
fail += run_test(suite, test, data);
if (fail && FAIL_FAST) {
break;
}
}
#else
while (((test = ztest_get_next_test(suite->name, test)) != NULL)) {
fail += run_test(suite, test, data);
@ -567,6 +576,7 @@ static int z_ztest_run_test_suite_ptr(struct ztest_suite_node *suite)
break;
}
}
#endif
test_status = (test_status || fail) ? 1 : 0;
}
@ -598,42 +608,53 @@ void end_report(void)
K_APPMEM_PARTITION_DEFINE(ztest_mem_partition);
#endif
int ztest_run_test_suites(const void *state)
static int __ztest_run_test_suite(struct ztest_suite_node *ptr, const void *state)
{
struct ztest_suite_stats *stats = ptr->stats;
bool should_run = true;
int count = 0;
if (ptr->predicate != NULL) {
should_run = ptr->predicate(state);
} else {
/* If predicate is NULL, only run this test once. */
should_run = stats->run_count == 0;
}
for (int i = 0; i < NUM_ITER_PER_SUITE; i++) {
if (should_run) {
int fail = z_ztest_run_test_suite_ptr(ptr);
count++;
stats->run_count++;
stats->fail_count += (fail != 0) ? 1 : 0;
} else {
stats->skip_count++;
}
}
return count;
}
int z_impl_ztest_run_test_suites(const void *state)
{
struct ztest_suite_node *ptr;
int count = 0;
#ifdef CONFIG_ZTEST_SHUFFLE
struct ztest_suite_node tmp;
struct ztest_suite_node *suites_to_run[ZTEST_SUITE_COUNT];
z_ztest_shuffle(_ztest_suite_node_list_start,
_ztest_suite_node_list_end - _ztest_suite_node_list_start, &tmp,
sizeof(struct ztest_suite_node));
#endif
for (ptr = _ztest_suite_node_list_start; ptr < _ztest_suite_node_list_end; ++ptr) {
struct ztest_suite_stats *stats = &ptr->stats;
bool should_run = true;
if (ptr->predicate != NULL) {
should_run = ptr->predicate(state);
} else {
/* If predicate is NULL, only run this test once. */
should_run = stats->run_count == 0;
}
for (int i = 0; i < NUM_ITER_PER_SUITE; i++) {
if (should_run) {
int fail = z_ztest_run_test_suite_ptr(ptr);
count++;
stats->run_count++;
stats->fail_count += (fail != 0) ? 1 : 0;
} else {
stats->skip_count++;
}
}
memset(suites_to_run, 0, ZTEST_SUITE_COUNT * sizeof(struct ztest_suite_node *));
z_ztest_shuffle((void **)suites_to_run, (intptr_t)_ztest_suite_node_list_start,
ZTEST_SUITE_COUNT, sizeof(struct ztest_suite_node));
for (size_t i = 0; i < ZTEST_SUITE_COUNT; ++i) {
count += __ztest_run_test_suite(suites_to_run[i], state);
}
#else
for (struct ztest_suite_node *ptr = _ztest_suite_node_list_start;
ptr < _ztest_suite_node_list_end; ++ptr) {
count += __ztest_run_test_suite(ptr, state);
}
#endif
return count;
}
@ -645,7 +666,7 @@ void ztest_verify_all_test_suites_ran(void)
struct ztest_unit_test *test;
for (suite = _ztest_suite_node_list_start; suite < _ztest_suite_node_list_end; ++suite) {
if (suite->stats.run_count < 1) {
if (suite->stats->run_count < 1) {
PRINT("ERROR: Test suite '%s' did not run.\n", suite->name);
all_tests_run = false;
}