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

@ -63,7 +63,7 @@ target_compile_options(testbinary PRIVATE
)
target_link_options(testbinary PRIVATE
-T "${ZEPHYR_BASE}/subsys/testsuite/include/ztest.ld"
-T "${ZEPHYR_BASE}/subsys/testsuite/include/ztest_unittest.ld"
)
target_link_libraries(testbinary PRIVATE

View file

@ -145,11 +145,3 @@
#ifdef CONFIG_USERSPACE
_static_kernel_objects_end = .;
#endif
#if defined(CONFIG_ZTEST)
ITERABLE_SECTION_RAM(ztest_suite_node, 4)
#if defined(CONFIG_ZTEST_NEW_API)
ITERABLE_SECTION_RAM(ztest_unit_test, 4)
ITERABLE_SECTION_RAM(ztest_test_rule, 4)
#endif /* CONFIG_ZTEST_NEW_API */
#endif /* CONFIG_ZTEST */

View file

@ -1,15 +1,17 @@
/* SPDX-License-Identifier: Apache-2.0 */
#include "common-rom/common-rom-kernel-devices.ld"
#include <zephyr/linker/common-rom/common-rom-kernel-devices.ld>
#include "common-rom/common-rom-cpp.ld"
#include <zephyr/linker/common-rom/common-rom-ztest.ld>
#include "common-rom/common-rom-net.ld"
#include <zephyr/linker/common-rom/common-rom-cpp.ld>
#include "common-rom/common-rom-bt.ld"
#include <zephyr/linker/common-rom/common-rom-net.ld>
#include "common-rom/common-rom-logging.ld"
#include <zephyr/linker/common-rom/common-rom-bt.ld>
#include "common-rom/common-rom-debug.ld"
#include <zephyr/linker/common-rom/common-rom-logging.ld>
#include "common-rom/common-rom-misc.ld"
#include <zephyr/linker/common-rom/common-rom-debug.ld>
#include <zephyr/linker/common-rom/common-rom-misc.ld>

View file

@ -0,0 +1,14 @@
/*
* Copyright (c) 2022 Google Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
SECTION_PROLOGUE(ztest,,)
{
Z_LINK_ITERABLE(ztest_suite_node);
Z_LINK_ITERABLE(ztest_unit_test);
Z_LINK_ITERABLE(ztest_test_rule);
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)

View file

@ -107,7 +107,11 @@ kobjects = OrderedDict([
("sys_mutex", (None, True, False)),
("k_futex", (None, True, False)),
("k_condvar", (None, False, True)),
("k_event", ("CONFIG_EVENTS", False, True))
("k_event", ("CONFIG_EVENTS", False, True)),
("ztest_suite_node", ("CONFIG_ZTEST", True, False)),
("ztest_suite_stats", ("CONFIG_ZTEST", True, False)),
("ztest_unit_test", ("CONFIG_ZTEST_NEW_API", True, False)),
("ztest_test_rule", ("CONFIG_ZTEST_NEW_API", True, False))
])
def kobject_to_enum(kobj):

View file

@ -183,6 +183,7 @@ SECTIONS
#define GROUP_ROM_LINK_IN(vregion, lregion) > RODATA_REGION AT > lregion
#include <zephyr/linker/common-rom/common-rom-cpp.ld>
#include <zephyr/linker/common-rom/common-rom-kernel-devices.ld>
#include <zephyr/linker/common-rom/common-rom-ztest.ld>
#include <zephyr/linker/common-rom/common-rom-net.ld>
#include <zephyr/linker/common-rom/common-rom-bt.ld>
#include <zephyr/linker/common-rom/common-rom-debug.ld>

View file

@ -196,6 +196,7 @@ SECTIONS
#define GROUP_ROM_LINK_IN(vregion, lregion) > RODATA_REGION AT > lregion
#include <zephyr/linker/common-rom/common-rom-cpp.ld>
#include <zephyr/linker/common-rom/common-rom-kernel-devices.ld>
#include <zephyr/linker/common-rom/common-rom-ztest.ld>
#include <zephyr/linker/common-rom/common-rom-net.ld>
#include <zephyr/linker/common-rom/common-rom-bt.ld>
#include <zephyr/linker/common-rom/common-rom-debug.ld>

View file

@ -187,6 +187,7 @@ SECTIONS
#define GROUP_ROM_LINK_IN(vregion, lregion) > RODATA_REGION AT > lregion
#include <zephyr/linker/common-rom/common-rom-cpp.ld>
#include <zephyr/linker/common-rom/common-rom-kernel-devices.ld>
#include <zephyr/linker/common-rom/common-rom-ztest.ld>
#include <zephyr/linker/common-rom/common-rom-net.ld>
#include <zephyr/linker/common-rom/common-rom-bt.ld>
#include <zephyr/linker/common-rom/common-rom-debug.ld>

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

View file

@ -6,7 +6,7 @@ This test verifies that we can define functions in SRAM (and
successfully execute them from SRAM) in ARM XIP images. It
also verifies that the .ramfunc section is accessible by
nPRIV code when building with support for user mode
(CONFIG_USERSPACE=y). Only for ARM Cortex-M targets.
(CONFIG_TEST_USERSPACE=y). Only for ARM Cortex-M targets.
---------------------------------------------------------------------------

View file

@ -1,4 +1,4 @@
CONFIG_USERSPACE=y
CONFIG_TEST_USERSPACE=y
CONFIG_APPLICATION_DEFINED_SYSCALL=y
CONFIG_ASSERT=y
CONFIG_LOG=y

View file

@ -8,4 +8,4 @@ tests:
- CONFIG_USERSPACE=n
arch.interrupt.nmi.kpti:
extra_configs:
- CONFIG_USERSPACE=y
- CONFIG_TEST_USERSPACE=y

View file

@ -4,7 +4,7 @@ CONFIG_MP_NUM_CPUS=1
CONFIG_LOG=y
CONFIG_LOG_MODE_MINIMAL=y
CONFIG_THREAD_NAME=y
CONFIG_USERSPACE=y
CONFIG_TEST_USERSPACE=y
CONFIG_DYNAMIC_OBJECTS=y
CONFIG_HW_STACK_PROTECTION=y
CONFIG_APPLICATION_DEFINED_SYSCALL=y

View file

@ -1,4 +1,3 @@
CONFIG_KSCAN=y
CONFIG_USERSPACE=y
CONFIG_TEST_USERSPACE=y
CONFIG_ZTEST=y

View file

@ -1,6 +1,6 @@
CONFIG_STACK_CANARIES=n
CONFIG_INIT_STACKS=y
CONFIG_USERSPACE=y
CONFIG_TEST_USERSPACE=y
CONFIG_ZTEST=y
CONFIG_TEST_USERSPACE=y

View file

@ -13,4 +13,4 @@ tests:
edac.ibecc.injection.user:
extra_configs:
- CONFIG_EDAC_ERROR_INJECT=y
- CONFIG_USERSPACE=y
- CONFIG_TEST_USERSPACE=y

View file

@ -76,7 +76,7 @@ static void reset_state(enum phase phase)
global_state.phase = phase;
for (int i = 0; i < num_registered_suites; ++i) {
stats_snapshot[i] = _ztest_suite_node_list_start[i].stats;
stats_snapshot[i] = *_ztest_suite_node_list_start[i].stats;
}
}
@ -88,7 +88,7 @@ static void take_stats_snapshot(void)
{
for (int i = 0; i < num_registered_suites; ++i) {
struct ztest_suite_stats *snapshot = stats_snapshot + i;
struct ztest_suite_stats *current = &_ztest_suite_node_list_start[i].stats;
struct ztest_suite_stats *current = _ztest_suite_node_list_start[i].stats;
snapshot->run_count = current->run_count - snapshot->run_count;
snapshot->skip_count = current->skip_count - snapshot->skip_count;