Feature/zephyr ztest (#118)
* Leverage (older) embedded unit tests into external unit tests build upon copy of Zephyr's ztest library and CMake. * Expand top-level CMake build to run external unit tests. * Expand Zephyr module extension to run external unit tests via west or sanitycheck. Co-authored-by: Gregory Shue <gregory.shue@legrand.us>
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Modified from zephyr_v2.2.0 subsys/testsuite/ztest/src/ztest.c
|
||||
* because:
|
||||
* 1. This port will never be run in the Zephyr kernel.
|
||||
* This repository is extended to be a Zephyr module for that.
|
||||
* 2. This port will not support multiple CPUs or toolchains.
|
||||
*
|
||||
* Modifications:
|
||||
* a. Deleted code conditionally compiled on the following CPP symbols:
|
||||
* (as they were kernel-specific):
|
||||
* CONFIG_USERSPACE
|
||||
* KERNEL
|
||||
* b. Removed irrelevant inclusion of the following header files:
|
||||
* <app_memory/app_memdomain.h>
|
||||
* <power/reboot.h>
|
||||
* c. Addition of test_skip functionality missing from non-kernel paths.
|
||||
*/
|
||||
|
||||
#include <ztest.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* ZTEST_DMEM and ZTEST_BMEM are used for the application shared memory test */
|
||||
|
||||
ZTEST_DMEM enum {
|
||||
TEST_PHASE_SETUP,
|
||||
TEST_PHASE_TEST,
|
||||
TEST_PHASE_TEARDOWN,
|
||||
TEST_PHASE_FRAMEWORK
|
||||
} phase = TEST_PHASE_FRAMEWORK;
|
||||
|
||||
static ZTEST_BMEM int test_status;
|
||||
|
||||
static int cleanup_test(struct unit_test *test)
|
||||
{
|
||||
int ret = TC_PASS;
|
||||
int mock_status;
|
||||
|
||||
mock_status = z_cleanup_mock();
|
||||
|
||||
if (!ret && mock_status == 1) {
|
||||
PRINT("Test %s failed: Unused mock parameter values\n",
|
||||
test->name);
|
||||
ret = TC_FAIL;
|
||||
} else if (!ret && mock_status == 2) {
|
||||
PRINT("Test %s failed: Unused mock return values\n",
|
||||
test->name);
|
||||
ret = TC_FAIL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void run_test_functions(struct unit_test *test)
|
||||
{
|
||||
phase = TEST_PHASE_SETUP;
|
||||
test->setup();
|
||||
phase = TEST_PHASE_TEST;
|
||||
test->test();
|
||||
}
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define FAIL_FAST 0
|
||||
|
||||
static jmp_buf test_fail;
|
||||
static jmp_buf test_pass;
|
||||
static jmp_buf test_skip;
|
||||
static jmp_buf stack_fail;
|
||||
|
||||
void ztest_test_fail(void)
|
||||
{
|
||||
raise(SIGABRT);
|
||||
}
|
||||
|
||||
void ztest_test_pass(void)
|
||||
{
|
||||
longjmp(test_pass, 1);
|
||||
}
|
||||
|
||||
void ztest_test_skip(void)
|
||||
{
|
||||
longjmp(test_skip, 1);
|
||||
}
|
||||
|
||||
static void handle_signal(int sig)
|
||||
{
|
||||
static const char *const phase_str[] = {
|
||||
"setup",
|
||||
"unit test",
|
||||
"teardown",
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
PRINT(" %d", sig);
|
||||
#else /* defined(_WIN32) */
|
||||
PRINT(" %s", strsignal(sig));
|
||||
#endif /* defined(_WIN32) */
|
||||
switch (phase) {
|
||||
case TEST_PHASE_SETUP:
|
||||
case TEST_PHASE_TEST:
|
||||
case TEST_PHASE_TEARDOWN:
|
||||
PRINT(" at %s function\n", phase_str[phase]);
|
||||
longjmp(test_fail, 1);
|
||||
case TEST_PHASE_FRAMEWORK:
|
||||
PRINT("\n");
|
||||
longjmp(stack_fail, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_testing(void)
|
||||
{
|
||||
signal(SIGABRT, handle_signal);
|
||||
signal(SIGSEGV, handle_signal);
|
||||
|
||||
if (setjmp(stack_fail)) {
|
||||
PRINT("Test suite crashed.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static int run_test(struct unit_test *test)
|
||||
{
|
||||
int ret = TC_PASS;
|
||||
|
||||
TC_START(test->name);
|
||||
|
||||
if (setjmp(test_fail)) {
|
||||
ret = TC_FAIL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (setjmp(test_pass)) {
|
||||
ret = TC_PASS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (setjmp(test_skip)) {
|
||||
ret = TC_SKIP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
run_test_functions(test);
|
||||
out:
|
||||
ret |= cleanup_test(test);
|
||||
Z_TC_END_RESULT(ret, test->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void z_ztest_run_test_suite(const char *name, struct unit_test *suite)
|
||||
{
|
||||
int fail = 0;
|
||||
|
||||
if (test_status < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
init_testing();
|
||||
|
||||
PRINT("Running test suite %s\n", name);
|
||||
PRINT_LINE;
|
||||
while (suite->test) {
|
||||
fail += run_test(suite);
|
||||
suite++;
|
||||
|
||||
if (fail && FAIL_FAST) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fail) {
|
||||
TC_PRINT("Test suite %s failed.\n", name);
|
||||
} else {
|
||||
TC_PRINT("Test suite %s succeeded\n", name);
|
||||
}
|
||||
|
||||
test_status = (test_status || fail) ? 1 : 0;
|
||||
}
|
||||
|
||||
void end_report(void)
|
||||
{
|
||||
if (test_status) {
|
||||
TC_END_REPORT(TC_FAIL);
|
||||
} else {
|
||||
TC_END_REPORT(TC_PASS);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
z_init_mock();
|
||||
test_main();
|
||||
end_report();
|
||||
|
||||
return test_status;
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Modified from zephyr_v2.2.0 subsys/testsuite/ztest/src/ztest_mock.c
|
||||
* because:
|
||||
* 1. This port will never be run in the Zephyr kernel.
|
||||
* This repository is extended to be a Zephyr module for that.
|
||||
* 2. This port will not support multiple CPUs or toolchains.
|
||||
*
|
||||
* Modifications:
|
||||
* a. Code conditionally compiled on the following CPP symbols were deleted
|
||||
* (as they were kernel-specific):
|
||||
* KERNEL
|
||||
*/
|
||||
|
||||
#include <ztest.h>
|
||||
#include <zephyr/types.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct parameter {
|
||||
struct parameter *next;
|
||||
const char *fn;
|
||||
const char *name;
|
||||
uintptr_t value;
|
||||
};
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
static void free_parameter(struct parameter *param)
|
||||
{
|
||||
free(param);
|
||||
}
|
||||
|
||||
static struct parameter *alloc_parameter(void)
|
||||
{
|
||||
struct parameter *param;
|
||||
|
||||
param = calloc(1, sizeof(struct parameter));
|
||||
if (!param) {
|
||||
PRINT("Failed to allocate mock parameter\n");
|
||||
ztest_test_fail();
|
||||
}
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
void z_init_mock(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void printk(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void vprintk(const char *fmt, va_list ap)
|
||||
{
|
||||
vprintf(fmt, ap);
|
||||
}
|
||||
|
||||
static struct parameter *find_and_delete_value(struct parameter *param,
|
||||
const char *fn,
|
||||
const char *name)
|
||||
{
|
||||
struct parameter *value;
|
||||
|
||||
if (!param->next) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strcmp(param->next->name, name) || strcmp(param->next->fn, fn)) {
|
||||
return find_and_delete_value(param->next, fn, name);
|
||||
}
|
||||
|
||||
value = param->next;
|
||||
param->next = param->next->next;
|
||||
value->next = NULL;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void insert_value(struct parameter *param, const char *fn,
|
||||
const char *name, uintptr_t val)
|
||||
{
|
||||
struct parameter *value;
|
||||
|
||||
value = alloc_parameter();
|
||||
value->fn = fn;
|
||||
value->name = name;
|
||||
value->value = val;
|
||||
|
||||
/* Seek to end of linked list to ensure correct discovery order in find_and_delete_value */
|
||||
while (param->next) {
|
||||
param = param->next;
|
||||
}
|
||||
|
||||
/* Append to end of linked list */
|
||||
value->next = param->next;
|
||||
param->next = value;
|
||||
}
|
||||
|
||||
static struct parameter parameter_list = { NULL, "", "", 0 };
|
||||
static struct parameter return_value_list = { NULL, "", "", 0 };
|
||||
|
||||
void z_ztest_expect_value(const char *fn, const char *name, uintptr_t val)
|
||||
{
|
||||
insert_value(¶meter_list, fn, name, val);
|
||||
}
|
||||
|
||||
void z_ztest_check_expected_value(const char *fn, const char *name,
|
||||
uintptr_t val)
|
||||
{
|
||||
struct parameter *param;
|
||||
uintptr_t expected;
|
||||
|
||||
param = find_and_delete_value(¶meter_list, fn, name);
|
||||
if (!param) {
|
||||
PRINT("Failed to find parameter %s for %s\n", name, fn);
|
||||
ztest_test_fail();
|
||||
}
|
||||
|
||||
expected = param->value;
|
||||
free_parameter(param);
|
||||
|
||||
if (expected != val) {
|
||||
/* We need to cast these values since the toolchain doesn't
|
||||
* provide inttypes.h
|
||||
*/
|
||||
PRINT("%s received wrong value: Got %lu, expected %lu\n",
|
||||
fn, (unsigned long)val, (unsigned long)expected);
|
||||
ztest_test_fail();
|
||||
}
|
||||
}
|
||||
|
||||
void z_ztest_returns_value(const char *fn, uintptr_t value)
|
||||
{
|
||||
insert_value(&return_value_list, fn, "", value);
|
||||
}
|
||||
|
||||
|
||||
uintptr_t z_ztest_get_return_value(const char *fn)
|
||||
{
|
||||
uintptr_t value;
|
||||
struct parameter *param = find_and_delete_value(&return_value_list,
|
||||
fn, "");
|
||||
|
||||
if (!param) {
|
||||
PRINT("Failed to find return value for function %s\n", fn);
|
||||
ztest_test_fail();
|
||||
}
|
||||
|
||||
value = param->value;
|
||||
free_parameter(param);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void free_param_list(struct parameter *param)
|
||||
{
|
||||
struct parameter *next;
|
||||
|
||||
while (param) {
|
||||
next = param->next;
|
||||
free_parameter(param);
|
||||
param = next;
|
||||
}
|
||||
}
|
||||
|
||||
int z_cleanup_mock(void)
|
||||
{
|
||||
int fail = 0;
|
||||
|
||||
if (parameter_list.next) {
|
||||
fail = 1;
|
||||
}
|
||||
if (return_value_list.next) {
|
||||
fail = 2;
|
||||
}
|
||||
|
||||
free_param_list(parameter_list.next);
|
||||
free_param_list(return_value_list.next);
|
||||
|
||||
parameter_list.next = NULL;
|
||||
return_value_list.next = NULL;
|
||||
|
||||
return fail;
|
||||
}
|
||||
Reference in New Issue
Block a user