tests: ztest: add a common fatal and assert hook for special purpose

This is in order to reduce the redundancy code writing for fatal and
assert handler for error case testing. They can be used both in kernel
and userspace, and are also SMP safe.

Signed-off-by: Enjia Mai <enjiax.mai@intel.com>
This commit is contained in:
Enjia Mai 2020-11-20 13:46:43 +08:00 committed by Anas Nashif
commit a420cb4fd5
5 changed files with 241 additions and 1 deletions

View file

@ -143,5 +143,4 @@ config TEST_ARM_CORTEX_M
the testing suite to utilize these exceptions, in tests.
Note that by default, when building with ARM_SECURE_FIRMWARE
set, these exceptions are set to target the Non-Secure state.
endmenu

View file

@ -7,4 +7,5 @@ zephyr_include_directories(
zephyr_library()
zephyr_library_sources( src/ztest.c)
zephyr_library_sources( src/ztest_error_hook.c)
zephyr_library_sources_ifdef(CONFIG_ZTEST_MOCKING src/ztest_mock.c)

View file

@ -54,6 +54,24 @@ config ZTEST_RETEST_IF_PASSED
may be used as an alternative to manual resets when
attempting to reproduce an intermittent failure.
config ZTEST_FATAL_HOOK
bool "Using a pre-defined fatal handler and hook function"
default n
help
Use the pre-defined common fatal error handler and a post hook to
do actions in your test case, this option often enabled when doing
error test case. Remember to add ignore_fault tag in yaml file when
using sanitycheck to run testing.
config ZTEST_ASSERT_HOOK
bool "Using a pre-defined assert handler and hook function"
default n
help
Use the pre-defined common assert fail handler and a post hook to
do actions in your test case, this option often enabled when doing
error test case. Remember to add ignore_fault tag in yaml file when
using sanitycheck to run testing.
endif # ZTEST
config ZTEST_MOCKING

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_ZTEST_FATAL_HOOK_H_
#define ZEPHYR_INCLUDE_ZTEST_FATAL_HOOK_H_
#include <zephyr.h>
#if defined(CONFIG_ZTEST_FATAL_HOOK)
/**
* @brief Set the flag that treat fatal error happened as expected
*
* @details This is used for negative test cases which triggers a fatal
* error. Set the param true will still pass the test case when expected
* fatal error happened. For normal test case, set it false makes it back
* to normal behavior.
*
* @param valid flag indicate fault is expected
*/
__syscall void ztest_set_fault_valid(bool valid);
/* @brief A hook after fatal error handler
*
* @details This is a test case hook that can run code from test case, in
* order to deal with some special case when catching the expected fatal
* error.
*
* Usage: Define your own hook function in your test case code, and do what
* you want to do after fatal error handler.
*
* By default, it will do nothing before leaving error handler.
*/
void ztest_post_fatal_error_hook(unsigned int reason,
const z_arch_esf_t *pEsf);
#endif
#if defined(CONFIG_ZTEST_ASSERT_HOOK)
/**
* @brief Set the flag that treat assert fail happened as expected
*
* @details This is used for negative test cases which triggers a assert
* fail. Set the param true will still pass the test case when expected
* assert fail happened. For normal test case, set it false make it back
* to normal behavior.
*
* @param valid flag indicate assert is expected
*/
__syscall void ztest_set_assert_valid(bool valid);
/* @brief A hook after assert fault handler
*
* @details This is a test case hook that can run code from test case, in
* order to deal with some special case when catching the expected assert
* failed.
*
* Usage: Define your own hook function in your test case code, and do what
* you want to do after assert handler.
*
* By default, it will abort the thread which assert failed.
*/
void ztest_post_assert_fail_hook(void);
#endif
#if defined(CONFIG_ZTEST_FATAL_HOOK)
#include <syscalls/ztest_error_hook.h>
#endif
#endif /* ZEPHYR_INCLUDE_ZTEST_FATAL_HOOK_H_ */

View file

@ -0,0 +1,147 @@
/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <ztest.h>
#if defined(CONFIG_ZTEST_FATAL_HOOK)
/* This is a flag indicate if treating fatal error as expected, then take
* action dealing with it. It's SMP-safe.
*/
ZTEST_BMEM volatile bool fault_in_isr;
ZTEST_BMEM volatile k_tid_t valid_fault_tid;
static inline void reset_stored_fault_status(void)
{
valid_fault_tid = NULL;
fault_in_isr = false;
}
void z_impl_ztest_set_fault_valid(bool valid)
{
if (valid) {
if (k_is_in_isr()) {
fault_in_isr = true;
} else {
valid_fault_tid = k_current_get();
}
} else {
reset_stored_fault_status();
}
}
#if defined(CONFIG_USERSPACE)
static inline void z_vrfy_ztest_set_fault_valid(bool valid)
{
z_impl_ztest_set_fault_valid(valid);
}
#include <syscalls/ztest_set_fault_valid_mrsh.c>
#endif
__weak void ztest_post_fatal_error_hook(unsigned int reason,
const z_arch_esf_t *pEsf)
{
}
void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *pEsf)
{
k_tid_t curr_tid = k_current_get();
bool valid_fault = (curr_tid == valid_fault_tid) || fault_in_isr;
printk("Caught system error -- reason %d %d\n", reason, valid_fault);
if (valid_fault) {
printk("Fatal error expected as part of test case.\n");
/* reset back to normal */
reset_stored_fault_status();
/* do some action after expected fatal error happened */
ztest_post_fatal_error_hook(reason, pEsf);
} else {
printk("Fatal error was unexpected, aborting...\n");
k_fatal_halt(reason);
}
}
#endif
#if defined(CONFIG_ZTEST_ASSERT_HOOK)
/* This is a flag indicate if treating assert fail as expected, then take
* action dealing with it. It's SMP-safe.
*/
ZTEST_BMEM volatile bool assert_in_isr;
ZTEST_BMEM volatile k_tid_t valid_assert_tid;
static inline void reset_stored_assert_status(void)
{
valid_assert_tid = NULL;
assert_in_isr = 0;
}
void z_impl_ztest_set_assert_valid(bool valid)
{
if (valid) {
if (k_is_in_isr()) {
assert_in_isr = true;
} else {
valid_assert_tid = k_current_get();
}
} else {
reset_stored_assert_status();
}
}
#if defined(CONFIG_USERSPACE)
static inline void z_vrfy_ztest_set_assert_valid(bool valid)
{
z_impl_ztest_set_assert_valid(valid);
}
#include <syscalls/ztest_set_assert_valid_mrsh.c>
#endif
__weak void ztest_post_assert_fail_hook(void)
{
k_thread_abort(k_current_get());
}
#ifdef CONFIG_ASSERT_NO_FILE_INFO
void assert_post_action(void)
#else
void assert_post_action(const char *file, unsigned int line)
#endif
{
#ifndef CONFIG_ASSERT_NO_FILE_INFO
ARG_UNUSED(file);
ARG_UNUSED(line);
#endif
printk("Caught assert failed\n");
if ((k_current_get() == valid_assert_tid) || assert_in_isr) {
printk("Assert error expected as part of test case.\n");
/* reset back to normal */
reset_stored_assert_status();
/* It won't go back to caller when assert failed, and it
* will terminate the thread.
*/
ztest_post_assert_fail_hook();
} else {
printk("Assert failed was unexpected, aborting...\n");
#ifdef CONFIG_USERSPACE
/* User threads aren't allowed to induce kernel panics; generate
* an oops instead.
*/
if (_is_user_context()) {
k_oops();
}
#endif
k_panic();
}
}
#endif