[unittests] add RUN_UNITTESTS_AT_BOOT build option
This will cause the system to automatically run all of the unit tests after a short pause on boot. Will be used by an automatic test script. At the moment there aren't a lot of unit tests in the list, but this should greatly increase the utility of them since they'll be automatically run.
This commit is contained in:
@@ -14,60 +14,75 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <lk/console_cmd.h>
|
#include <lk/console_cmd.h>
|
||||||
#include <lk/err.h>
|
#include <lk/err.h>
|
||||||
|
#include <lk/init.h>
|
||||||
|
|
||||||
static struct test_case_element *test_case_list = NULL;
|
static struct list_node test_case_list = LIST_INITIAL_VALUE(test_case_list);
|
||||||
static struct test_case_element *failed_test_case_list = NULL;
|
|
||||||
|
#if WITH_LIB_CONSOLE
|
||||||
|
#include <lib/console.h>
|
||||||
|
|
||||||
|
#if RUN_UNITTESTS_AT_BOOT
|
||||||
|
static void unittest_run_at_boot(uint level) {
|
||||||
|
const char run_at_boot_script[] = "sleep 5; ut all";
|
||||||
|
console_t *con = console_create(false);
|
||||||
|
if (con) {
|
||||||
|
console_run_script(con, run_at_boot_script);
|
||||||
|
// TODO: destroy console afterwards
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LK_INIT_HOOK(unittest_at_boot, unittest_run_at_boot, LK_INIT_LEVEL_LAST)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Registers a test case with the unit test framework.
|
* Registers a test case with the unit test framework.
|
||||||
*/
|
*/
|
||||||
void unittest_register_test_case(struct test_case_element *elem) {
|
void unittest_register_test_case(struct test_case_element *elem) {
|
||||||
DEBUG_ASSERT(elem);
|
DEBUG_ASSERT(elem);
|
||||||
DEBUG_ASSERT(elem->next == NULL);
|
DEBUG_ASSERT(!list_in_list(&elem->node));
|
||||||
elem->next = test_case_list;
|
list_add_tail(&test_case_list, &elem->node);
|
||||||
test_case_list = elem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Runs all registered test cases.
|
* Runs all registered test cases.
|
||||||
*/
|
*/
|
||||||
bool run_all_tests(void) {
|
bool run_all_tests(void) {
|
||||||
unsigned int n_tests = 0;
|
unsigned int n_tests = 0;
|
||||||
unsigned int n_success = 0;
|
unsigned int n_success = 0;
|
||||||
unsigned int n_failed = 0;
|
unsigned int n_failed = 0;
|
||||||
|
struct list_node failed_test_case_list = LIST_INITIAL_VALUE(failed_test_case_list);
|
||||||
|
|
||||||
bool all_success = true;
|
bool all_success = true;
|
||||||
struct test_case_element *current = test_case_list;
|
struct test_case_element *current;
|
||||||
while (current) {
|
list_for_every_entry(&test_case_list, current, struct test_case_element, node) {
|
||||||
if (!current->test_case()) {
|
if (!current->test_case()) {
|
||||||
current->failed_next = failed_test_case_list;
|
list_add_tail(&failed_test_case_list, ¤t->failed_node);
|
||||||
failed_test_case_list = current;
|
|
||||||
all_success = false;
|
all_success = false;
|
||||||
|
n_failed++;
|
||||||
|
} else {
|
||||||
|
n_success++;
|
||||||
}
|
}
|
||||||
current = current->next;
|
|
||||||
n_tests++;
|
n_tests++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all_success) {
|
unittest_printf("\n====================================================\n");
|
||||||
n_success = n_tests;
|
unittest_printf(" CASES: %d SUCCESS: %d FAILED: %d ",
|
||||||
unittest_printf("SUCCESS! All test cases passed!\n");
|
n_tests, n_success, n_failed);
|
||||||
} else {
|
unittest_printf("\n====================================================\n");
|
||||||
struct test_case_element *failed = failed_test_case_list;
|
|
||||||
while (failed) {
|
|
||||||
struct test_case_element *failed_next =
|
|
||||||
failed->failed_next;
|
|
||||||
failed->failed_next = NULL;
|
|
||||||
failed = failed_next;
|
|
||||||
n_failed++;
|
|
||||||
}
|
|
||||||
n_success = n_tests - n_failed;
|
|
||||||
failed_test_case_list = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
unittest_printf("\n====================================================\n");
|
if (all_success) {
|
||||||
unittest_printf (" CASES: %d SUCCESS: %d FAILED: %d ",
|
unittest_printf("\nSUCCESS! All test cases passed\n");
|
||||||
n_tests, n_success, n_failed);
|
} else {
|
||||||
unittest_printf("\n====================================================\n");
|
unittest_printf("\nFAILURE! Some test cases failed:\n");
|
||||||
|
struct test_case_element *failed;
|
||||||
|
struct test_case_element *temp;
|
||||||
|
list_for_every_entry_safe(&failed_test_case_list, failed, temp, struct test_case_element, failed_node) {
|
||||||
|
unittest_printf(" %s\n", failed->name);
|
||||||
|
list_delete(&failed->failed_node);
|
||||||
|
}
|
||||||
|
unittest_printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
return all_success;
|
return all_success;
|
||||||
}
|
}
|
||||||
@@ -87,13 +102,15 @@ usage:
|
|||||||
bool result = run_all_tests();
|
bool result = run_all_tests();
|
||||||
printf("UNIT TEST: run_all_tests return %u\n", result);
|
printf("UNIT TEST: run_all_tests return %u\n", result);
|
||||||
} else if (!strcmp(argv[1].str, "list")) {
|
} else if (!strcmp(argv[1].str, "list")) {
|
||||||
for (struct test_case_element *current = test_case_list; current; current = current->next) {
|
struct test_case_element *current;
|
||||||
|
list_for_every_entry(&test_case_list, current, struct test_case_element, node) {
|
||||||
puts(current->name);
|
puts(current->name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// look for unit test that matches the string name
|
// look for unit test that matches the string name
|
||||||
bool found_test = false;
|
bool found_test = false;
|
||||||
for (struct test_case_element *current = test_case_list; current; current = current->next) {
|
struct test_case_element *current;
|
||||||
|
list_for_every_entry(&test_case_list, current, struct test_case_element, node) {
|
||||||
if (strcmp(argv[1].str, current->name) == 0) {
|
if (strcmp(argv[1].str, current->name) == 0) {
|
||||||
found_test = true;
|
found_test = true;
|
||||||
current->test_case();
|
current->test_case();
|
||||||
@@ -112,4 +129,3 @@ usage:
|
|||||||
STATIC_COMMAND_START
|
STATIC_COMMAND_START
|
||||||
STATIC_COMMAND("ut", "run some or all of the unit tests", do_unittests)
|
STATIC_COMMAND("ut", "run some or all of the unit tests", do_unittests)
|
||||||
STATIC_COMMAND_END(unit_tests);
|
STATIC_COMMAND_END(unit_tests);
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,7 @@
|
|||||||
* lib/unittest \
|
* lib/unittest \
|
||||||
*/
|
*/
|
||||||
#include <lk/compiler.h>
|
#include <lk/compiler.h>
|
||||||
|
#include <lk/list.h>
|
||||||
#include <printf.h>
|
#include <printf.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -116,8 +117,8 @@ __END_CDECLS
|
|||||||
return all_ok; \
|
return all_ok; \
|
||||||
} \
|
} \
|
||||||
static struct test_case_element _##case_name##_element = { \
|
static struct test_case_element _##case_name##_element = { \
|
||||||
.next = NULL, \
|
.node = LIST_INITIAL_CLEARED_VALUE, \
|
||||||
.failed_next = NULL, \
|
.failed_node = LIST_INITIAL_CLEARED_VALUE, \
|
||||||
.name = #case_name, \
|
.name = #case_name, \
|
||||||
.test_case = case_name, \
|
.test_case = case_name, \
|
||||||
}; \
|
}; \
|
||||||
@@ -387,8 +388,8 @@ __BEGIN_CDECLS
|
|||||||
* The list of test cases is made up of these elements.
|
* The list of test cases is made up of these elements.
|
||||||
*/
|
*/
|
||||||
struct test_case_element {
|
struct test_case_element {
|
||||||
struct test_case_element *next;
|
struct list_node node;
|
||||||
struct test_case_element *failed_next;
|
struct list_node failed_node;
|
||||||
const char *name;
|
const char *name;
|
||||||
bool (*test_case)(void);
|
bool (*test_case)(void);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,4 +6,9 @@ MODULE_SRCS := \
|
|||||||
$(LOCAL_DIR)/unittest.c \
|
$(LOCAL_DIR)/unittest.c \
|
||||||
$(LOCAL_DIR)/all_tests.c \
|
$(LOCAL_DIR)/all_tests.c \
|
||||||
|
|
||||||
|
ifeq (true,$(call TOBOOL,$(RUN_UNITTESTS_AT_BOOT)))
|
||||||
|
$(info Boot unit tests enabled)
|
||||||
|
MODULE_DEFINES += RUN_UNITTESTS_AT_BOOT=1
|
||||||
|
endif
|
||||||
|
|
||||||
include make/module.mk
|
include make/module.mk
|
||||||
|
|||||||
1
makefile
1
makefile
@@ -27,6 +27,7 @@ export LKINC
|
|||||||
export BUILDROOT
|
export BUILDROOT
|
||||||
export DEFAULT_PROJECT
|
export DEFAULT_PROJECT
|
||||||
export TOOLCHAIN_PREFIX
|
export TOOLCHAIN_PREFIX
|
||||||
|
export RUN_UNITTESTS_AT_BOOT
|
||||||
|
|
||||||
# veneer makefile that calls into the engine with lk as the build root
|
# veneer makefile that calls into the engine with lk as the build root
|
||||||
# if we're the top level invocation, call ourselves with additional args
|
# if we're the top level invocation, call ourselves with additional args
|
||||||
|
|||||||
Reference in New Issue
Block a user