diff --git a/include/linker/common-ram.ld b/include/linker/common-ram.ld index 10d5b898b11..7df193531d4 100644 --- a/include/linker/common-ram.ld +++ b/include/linker/common-ram.ld @@ -141,4 +141,8 @@ #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 */ diff --git a/samples/subsys/testsuite/integration/prj.conf b/samples/subsys/testsuite/integration/prj.conf index 9467c292689..9228251051e 100644 --- a/samples/subsys/testsuite/integration/prj.conf +++ b/samples/subsys/testsuite/integration/prj.conf @@ -1 +1,2 @@ CONFIG_ZTEST=y +CONFIG_ZTEST_NEW_API=y diff --git a/samples/subsys/testsuite/integration/src/main.c b/samples/subsys/testsuite/integration/src/main.c index c18541f7075..60a15b4a10d 100644 --- a/samples/subsys/testsuite/integration/src/main.c +++ b/samples/subsys/testsuite/integration/src/main.c @@ -6,13 +6,16 @@ #include + +ZTEST_SUITE(framework_tests, NULL, NULL, NULL, NULL, NULL); + /** * @brief Test Asserts * * This test verifies various assert macros provided by ztest. * */ -static void test_assert(void) +ZTEST(framework_tests, test_assert) { zassert_true(1, "1 was false"); zassert_false(0, "0 was true"); @@ -21,12 +24,3 @@ static void test_assert(void) zassert_equal(1, 1, "1 was not equal to 1"); zassert_equal_ptr(NULL, NULL, "NULL was not equal to NULL"); } - -void test_main(void) -{ - ztest_test_suite(framework_tests, - ztest_unit_test(test_assert) - ); - - ztest_run_test_suite(framework_tests); -} diff --git a/samples/subsys/testsuite/pytest/prj.conf b/samples/subsys/testsuite/pytest/prj.conf index c3e81438ced..989b0c1b2cf 100644 --- a/samples/subsys/testsuite/pytest/prj.conf +++ b/samples/subsys/testsuite/pytest/prj.conf @@ -1,2 +1,3 @@ CONFIG_ZTEST=y +CONFIG_ZTEST_NEW_API=y CONFIG_IDLE_STACK_SIZE=4096 diff --git a/samples/subsys/testsuite/pytest/src/main.c b/samples/subsys/testsuite/pytest/src/main.c index dfb6085e9c6..4d4f396ff02 100644 --- a/samples/subsys/testsuite/pytest/src/main.c +++ b/samples/subsys/testsuite/pytest/src/main.c @@ -4,16 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ #include -void test_pytest(void) + +ZTEST_SUITE(test_pytest, NULL, NULL, NULL, NULL, NULL); + +ZTEST(test_pytest, test_pytest) { TC_PRINT("Hello world\n"); } - -void test_main(void) -{ - ztest_test_suite(test_pytest, - ztest_unit_test(test_pytest) - ); - - ztest_run_test_suite(test_pytest); -} diff --git a/subsys/testsuite/include/ztest.ld b/subsys/testsuite/include/ztest.ld index 7012c8ee064..14de5a897fc 100644 --- a/subsys/testsuite/include/ztest.ld +++ b/subsys/testsuite/include/ztest.ld @@ -12,5 +12,17 @@ SECTIONS 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; diff --git a/subsys/testsuite/ztest/CMakeLists.txt b/subsys/testsuite/ztest/CMakeLists.txt index 4b6db949686..44ab071fc18 100644 --- a/subsys/testsuite/ztest/CMakeLists.txt +++ b/subsys/testsuite/ztest/CMakeLists.txt @@ -6,7 +6,9 @@ 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) -zephyr_library_sources_ifdef(CONFIG_ZTRESS src/ztress.c) +zephyr_library_sources_ifndef(CONFIG_ZTEST_NEW_API src/ztest.c) +zephyr_library_sources_ifdef(CONFIG_ZTEST_NEW_API src/ztest_new.c) +zephyr_library_sources( src/ztest_error_hook.c) +zephyr_library_sources_ifdef(CONFIG_ZTEST_NEW_API src/ztest_rules.c) +zephyr_library_sources_ifdef(CONFIG_ZTEST_MOCKING src/ztest_mock.c) +zephyr_library_sources_ifdef(CONFIG_ZTRESS src/ztress.c) diff --git a/subsys/testsuite/ztest/Kconfig b/subsys/testsuite/ztest/Kconfig index 79e6c940d70..e6b546a5493 100644 --- a/subsys/testsuite/ztest/Kconfig +++ b/subsys/testsuite/ztest/Kconfig @@ -10,6 +10,12 @@ config ZTEST if ZTEST +config ZTEST_NEW_API + bool "Use the new Ztest API" + help + Enables the new Ztest APIs for creating suites and unit tests in + separate compilational units as well as the new 'rules' API. + config ZTEST_STACKSIZE int "Test function thread stack size" default 2048 if COVERAGE_GCOV @@ -73,6 +79,20 @@ config ZTEST_ASSERT_HOOK error test case. Remember to add ignore_fault tag in yaml file when using twister to run testing. +if ZTEST_NEW_API + +menu "ztest provided rules" + +config ZTEST_RULE_1CPU + bool "Run all the tests on a single CPU" + help + This rule will call z_test_1cpu_start before each unit test and + ztest_1cpu_stop after each test. + +endmenu + +endif # ZTEST_NEW_API + endif # ZTEST config ZTEST_MOCKING diff --git a/subsys/testsuite/ztest/include/ztest_test.h b/subsys/testsuite/ztest/include/ztest_test.h index 8e3e8b10d04..90953425ae9 100644 --- a/subsys/testsuite/ztest/include/ztest_test.h +++ b/subsys/testsuite/ztest/include/ztest_test.h @@ -1,302 +1,32 @@ /* * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2021 Google LLC * * SPDX-License-Identifier: Apache-2.0 */ -/** - * @file - * - * @brief Zephyr testing framework _test. - */ +#ifndef ZEPHYR_TESTSUITE_INCLUDE_ZTEST_TEST_H_ +#define ZEPHYR_TESTSUITE_INCLUDE_ZTEST_TEST_H_ -#ifndef ZEPHYR_TESTSUITE_ZTEST_TEST_H_ -#define ZEPHYR_TESTSUITE_ZTEST_TEST_H_ - -#include -#include -#include +#ifdef CONFIG_ZTEST_NEW_API +#include +#else +#include +#endif /* !CONFIG_ZTEST_NEW_API */ #ifdef __cplusplus extern "C" { #endif -struct unit_test { - const char *name; - void (*test)(void); - void (*setup)(void); - void (*teardown)(void); - uint32_t thread_options; -}; - -/** - * Stats about a ztest suite - */ -struct ztest_suite_stats { - /** The number of times that the suite ran */ - uint32_t run_count; - /** The number of times that the suite was skipped */ - uint32_t skip_count; - /** The number of times that the suite failed */ - uint32_t fail_count; -}; - -/** - * A single node of test suite. Each node should be added to a single linker section which will - * allow ztest_run_registered_test_suites() to iterate over the various nodes. - */ -struct ztest_suite_node { - /** The name of the test suite. */ - const char *name; - /** Pointer to the test suite. */ - struct unit_test *suite; - /** - * An optional predicate function to determine if the test should run. If NULL, then the - * test will only run once on the first attempt. - * - * @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); - /** Stats */ - struct ztest_suite_stats stats; -}; - -extern struct ztest_suite_node _ztest_suite_node_list_start[]; -extern struct ztest_suite_node _ztest_suite_node_list_end[]; - -/** - * Create and register a ztest suite. Using this macro creates a new test suite (using - * ztest_test_suite). It then creates a struct ztest_suite_node in a specific linker section. - * - * Tests can then be run by calling ztest_run_registered_test_suites(const void *state) by passing - * in the current state. See the documentation for ztest_run_registered_test_suites for more info. - * - * @param SUITE_NAME The name of the suite (see ztest_test_suite for more info) - * @param PREDICATE A function to test against the state and determine if the test should run. - * @param args Varargs placeholder for the remaining arguments passed for the unit tests. - */ -#define ztest_register_test_suite(SUITE_NAME, PREDICATE, args...) \ - ztest_test_suite(SUITE_NAME, ##args); \ - static STRUCT_SECTION_ITERABLE(ztest_suite_node, z_ztest_test_node_##SUITE_NAME) = { \ - .name = #SUITE_NAME, \ - .suite = _##SUITE_NAME, \ - .predicate = PREDICATE, \ - }; - -/** - * 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_registered_test_suites(const void *state); - -/** - * @brief Fails the test if any of the registered tests did not run. - * - * When registering test suites, a pragma function can be provided to determine WHEN the test should - * run. It is possible that a test suite could be registered but the pragma always prevents it from - * running. In cases where a test should make sure that ALL suites ran at least once, this function - * may be called at the end of test_main(). It will cause the test to fail if any suite was - * registered but never ran. - */ -void ztest_verify_all_registered_test_suites_ran(void); - -/** - * @brief Run a test suite. - * - * Internal implementation. Do not call directly. This will run the full test suite along with some - * checks for fast failures and initialization. - * - * @param name The name of the suite to run. - * @param suite Pointer to the first unit test. - * @return Negative value if the test suite never ran; otherwise, return the number of failures. - */ -int z_ztest_run_test_suite(const char *name, struct unit_test *suite); - -/** - * @defgroup ztest_test Ztest testing macros - * @ingroup ztest - * - * This module eases the testing process by providing helpful macros and other - * testing structures. - * - * @{ - */ - -/** - * @brief Fail the currently running test. - * - * This is the function called from failed assertions and the like. You - * probably don't need to call it yourself. - */ -void ztest_test_fail(void); - -/** - * @brief Pass the currently running test. - * - * Normally a test passes just by returning without an assertion failure. - * However, if the success case for your test involves a fatal fault, - * you can call this function from k_sys_fatal_error_handler to indicate that - * the test passed before aborting the thread. - */ -void ztest_test_pass(void); - -/** - * @brief Skip the current test. - * - */ -void ztest_test_skip(void); - -/** - * @brief Do nothing, successfully. - * - * Unit test / setup function / teardown function that does - * nothing, successfully. Can be used as a parameter to - * ztest_unit_test_setup_teardown(). - */ -static inline void unit_test_noop(void) -{ -} - -/** - * @brief Define a test with setup and teardown functions - * - * This should be called as an argument to ztest_test_suite. The test will - * be run in the following order: @a setup, @a fn, @a teardown. - * - * @param fn Main test function - * @param setup Setup function - * @param teardown Teardown function - */ - -#define ztest_unit_test_setup_teardown(fn, setup, teardown) { \ - STRINGIFY(fn), fn, setup, teardown, 0 \ -} - -/** - * @brief Define a user mode test with setup and teardown functions - * - * This should be called as an argument to ztest_test_suite. The test will - * be run in the following order: @a setup, @a fn, @a teardown. ALL - * test functions will be run in user mode, and only if CONFIG_USERSPACE - * is enabled, otherwise this is the same as ztest_unit_test_setup_teardown(). - * - * @param fn Main test function - * @param setup Setup function - * @param teardown Teardown function - */ - -#define ztest_user_unit_test_setup_teardown(fn, setup, teardown) { \ - STRINGIFY(fn), fn, setup, teardown, K_USER \ -} - -/** - * @brief Define a test function - * - * This should be called as an argument to ztest_test_suite. - * - * @param fn Test function - */ - -#define ztest_unit_test(fn) \ - ztest_unit_test_setup_teardown(fn, unit_test_noop, unit_test_noop) - -/** - * @brief Define a test function that should run as a user thread - * - * This should be called as an argument to ztest_test_suite. - * If CONFIG_USERSPACE is not enabled, this is functionally identical to - * ztest_unit_test(). - * - * @param fn Test function - */ - -#define ztest_user_unit_test(fn) \ - ztest_user_unit_test_setup_teardown(fn, unit_test_noop, unit_test_noop) - __syscall void z_test_1cpu_start(void); __syscall void z_test_1cpu_stop(void); -/** - * @brief Define a SMP-unsafe test function - * - * As ztest_unit_test(), but ensures all test code runs on only - * one CPU when in SMP. - * - * @param fn Test function - */ -#ifdef CONFIG_SMP -#define ztest_1cpu_unit_test(fn) \ - ztest_unit_test_setup_teardown(fn, z_test_1cpu_start, z_test_1cpu_stop) -#else -#define ztest_1cpu_unit_test(fn) ztest_unit_test(fn) -#endif - -/** - * @brief Define a SMP-unsafe test function that should run as a user thread - * - * As ztest_user_unit_test(), but ensures all test code runs on only - * one CPU when in SMP. - * - * @param fn Test function - */ -#ifdef CONFIG_SMP -#define ztest_1cpu_user_unit_test(fn) \ - ztest_user_unit_test_setup_teardown(fn, z_test_1cpu_start, z_test_1cpu_stop) -#else -#define ztest_1cpu_user_unit_test(fn) ztest_user_unit_test(fn) -#endif - -/* definitions for use with testing application shared memory */ -#ifdef CONFIG_USERSPACE -#define ZTEST_DMEM K_APP_DMEM(ztest_mem_partition) -#define ZTEST_BMEM K_APP_BMEM(ztest_mem_partition) -#define ZTEST_SECTION K_APP_DMEM_SECTION(ztest_mem_partition) -extern struct k_mem_partition ztest_mem_partition; -#else -#define ZTEST_DMEM -#define ZTEST_BMEM -#define ZTEST_SECTION .data -#endif - -/** - * @brief Define a test suite - * - * This function should be called in the following fashion: - * ```{.c} - * ztest_test_suite(test_suite_name, - * ztest_unit_test(test_function), - * ztest_unit_test(test_other_function) - * ); - * - * ztest_run_test_suite(test_suite_name); - * ``` - * - * @param suite Name of the testing suite - */ -#define ztest_test_suite(suite, ...) \ - static ZTEST_DMEM struct unit_test _##suite[] = { \ - __VA_ARGS__, { 0 } \ - } -/** - * @brief Run the specified test suite. - * - * @param suite Test suite to run. - */ -#define ztest_run_test_suite(suite) \ - z_ztest_run_test_suite(#suite, _##suite) - -/** - * @} - */ -#ifndef ZTEST_UNITTEST -#include -#endif - #ifdef __cplusplus } #endif -#endif /* ZEPHYR_TESTSUITE_ZTEST_TEST_H_ */ +#ifndef ZTEST_UNITTEST +#include +#endif + +#endif /* ZEPHYR_TESTSUITE_INCLUDE_ZTEST_TEST_H_ */ diff --git a/subsys/testsuite/ztest/include/ztest_test_deprecated.h b/subsys/testsuite/ztest/include/ztest_test_deprecated.h new file mode 100644 index 00000000000..14a9ec17a64 --- /dev/null +++ b/subsys/testsuite/ztest/include/ztest_test_deprecated.h @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * @brief Zephyr testing framework _test_deprecated. + */ + +#ifndef ZEPHYR_TESTSUITE_ZTEST_TEST_H_ +#define ZEPHYR_TESTSUITE_ZTEST_TEST_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct unit_test { + const char *name; + void (*test)(void); + void (*setup)(void); + void (*teardown)(void); + uint32_t thread_options; +}; + +/** + * Stats about a ztest suite + */ +struct ztest_suite_stats { + /** The number of times that the suite ran */ + uint32_t run_count; + /** The number of times that the suite was skipped */ + uint32_t skip_count; + /** The number of times that the suite failed */ + uint32_t fail_count; +}; + +/** + * A single node of test suite. Each node should be added to a single linker section which will + * allow ztest_run_registered_test_suites() to iterate over the various nodes. + */ +struct ztest_suite_node { + /** The name of the test suite. */ + const char *name; + /** Pointer to the test suite. */ + struct unit_test *suite; + /** + * An optional predicate function to determine if the test should run. If NULL, then the + * test will only run once on the first attempt. + * + * @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); + /** Stats */ + struct ztest_suite_stats stats; +}; + +extern struct ztest_suite_node _ztest_suite_node_list_start[]; +extern struct ztest_suite_node _ztest_suite_node_list_end[]; + +/** + * Create and register a ztest suite. Using this macro creates a new test suite (using + * ztest_test_suite). It then creates a struct ztest_suite_node in a specific linker section. + * + * Tests can then be run by calling ztest_run_registered_test_suites(const void *state) by passing + * in the current state. See the documentation for ztest_run_registered_test_suites for more info. + * + * @param SUITE_NAME The name of the suite (see ztest_test_suite for more info) + * @param PREDICATE A function to test against the state and determine if the test should run. + * @param args Varargs placeholder for the remaining arguments passed for the unit tests. + */ +#define ztest_register_test_suite(SUITE_NAME, PREDICATE, args...) \ + ztest_test_suite(SUITE_NAME, ##args); \ + static STRUCT_SECTION_ITERABLE(ztest_suite_node, z_ztest_test_node_##SUITE_NAME) = { \ + .name = #SUITE_NAME, \ + .suite = _##SUITE_NAME, \ + .predicate = PREDICATE, \ + }; + +/** + * 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_registered_test_suites(const void *state); + +/** + * @brief Fails the test if any of the registered tests did not run. + * + * When registering test suites, a pragma function can be provided to determine WHEN the test should + * run. It is possible that a test suite could be registered but the pragma always prevents it from + * running. In cases where a test should make sure that ALL suites ran at least once, this function + * may be called at the end of test_main(). It will cause the test to fail if any suite was + * registered but never ran. + */ +void ztest_verify_all_registered_test_suites_ran(void); + +/** + * @brief Run a test suite. + * + * Internal implementation. Do not call directly. This will run the full test suite along with some + * checks for fast failures and initialization. + * + * @param name The name of the suite to run. + * @param suite Pointer to the first unit test. + * @return Negative value if the test suite never ran; otherwise, return the number of failures. + */ +int z_ztest_run_test_suite(const char *name, struct unit_test *suite); + +/** + * @defgroup ztest_test_deprecated Ztest testing macros + * @ingroup ztest + * + * This module eases the testing process by providing helpful macros and other + * testing structures. + * + * @{ + */ + +/** + * @brief Fail the currently running test. + * + * This is the function called from failed assertions and the like. You + * probably don't need to call it yourself. + */ +void ztest_test_fail(void); + +/** + * @brief Pass the currently running test. + * + * Normally a test passes just by returning without an assertion failure. + * However, if the success case for your test involves a fatal fault, + * you can call this function from k_sys_fatal_error_handler to indicate that + * the test passed before aborting the thread. + */ +void ztest_test_pass(void); + +/** + * @brief Skip the current test. + */ +void ztest_test_skip(void); + +/** + * @brief Do nothing, successfully. + * + * Unit test / setup function / teardown function that does + * nothing, successfully. Can be used as a parameter to + * ztest_unit_test_setup_teardown(). + */ +static inline void unit_test_noop(void) +{ +} + +/** + * @brief Define a test with setup and teardown functions + * + * This should be called as an argument to ztest_test_suite. The test will + * be run in the following order: @a setup, @a fn, @a teardown. + * + * @param fn Main test function + * @param setup Setup function + * @param teardown Teardown function + */ +#define ztest_unit_test_setup_teardown(fn, setup, teardown) \ + { \ + STRINGIFY(fn), fn, setup, teardown, 0 \ + } + +/** + * @brief Define a user mode test with setup and teardown functions + * + * This should be called as an argument to ztest_test_suite. The test will + * be run in the following order: @a setup, @a fn, @a teardown. ALL + * test functions will be run in user mode, and only if CONFIG_USERSPACE + * is enabled, otherwise this is the same as ztest_unit_test_setup_teardown(). + * + * @param fn Main test function + * @param setup Setup function + * @param teardown Teardown function + */ +#define ztest_user_unit_test_setup_teardown(fn, setup, teardown) \ + { \ + STRINGIFY(fn), fn, setup, teardown, K_USER \ + } + +/** + * @brief Define a test function + * + * This should be called as an argument to ztest_test_suite. + * + * @param fn Test function + */ +#define ztest_unit_test(fn) \ + ztest_unit_test_setup_teardown(fn, unit_test_noop, unit_test_noop) + +/** + * @brief Define a test function that should run as a user thread + * + * This should be called as an argument to ztest_test_suite. + * If CONFIG_USERSPACE is not enabled, this is functionally identical to + * ztest_unit_test(). + * + * @param fn Test function + */ +#define ztest_user_unit_test(fn) \ + ztest_user_unit_test_setup_teardown(fn, unit_test_noop, unit_test_noop) + +/** + * @brief Define a SMP-unsafe test function + * + * As ztest_unit_test(), but ensures all test code runs on only + * one CPU when in SMP. + * + * @param fn Test function + */ +#ifdef CONFIG_SMP +#define ztest_1cpu_unit_test(fn) \ + ztest_unit_test_setup_teardown(fn, z_test_1cpu_start, z_test_1cpu_stop) +#else +#define ztest_1cpu_unit_test(fn) ztest_unit_test(fn) +#endif + +/** + * @brief Define a SMP-unsafe test function that should run as a user thread + * + * As ztest_user_unit_test(), but ensures all test code runs on only + * one CPU when in SMP. + * + * @param fn Test function + */ +#ifdef CONFIG_SMP +#define ztest_1cpu_user_unit_test(fn) \ + ztest_user_unit_test_setup_teardown(fn, z_test_1cpu_start, z_test_1cpu_stop) +#else +#define ztest_1cpu_user_unit_test(fn) ztest_user_unit_test(fn) +#endif + +/* definitions for use with testing application shared memory */ +#ifdef CONFIG_USERSPACE +#define ZTEST_DMEM K_APP_DMEM(ztest_mem_partition) +#define ZTEST_BMEM K_APP_BMEM(ztest_mem_partition) +#define ZTEST_SECTION K_APP_DMEM_SECTION(ztest_mem_partition) +extern struct k_mem_partition ztest_mem_partition; +#else +#define ZTEST_DMEM +#define ZTEST_BMEM +#define ZTEST_SECTION .data +#endif + +/** + * @brief Define a test suite + * + * This function should be called in the following fashion: + * ```{.c} + * ztest_test_suite(test_suite_name, + * ztest_unit_test(test_function), + * ztest_unit_test(test_other_function) + * ); + * + * ztest_run_test_suite(test_suite_name); + * ``` + * + * @param suite Name of the testing suite + */ +#define ztest_test_suite(suite, ...) \ + static ZTEST_DMEM struct unit_test _##suite[] = { __VA_ARGS__, { 0 } } +/** + * @brief Run the specified test suite. + * + * @param suite Test suite to run. + */ +#define ztest_run_test_suite(suite) \ + z_ztest_run_test_suite(#suite, _##suite) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_TESTSUITE_ZTEST_TEST_H_ */ diff --git a/subsys/testsuite/ztest/include/ztest_test_new.h b/subsys/testsuite/ztest/include/ztest_test_new.h new file mode 100644 index 00000000000..5f1fe7adc2e --- /dev/null +++ b/subsys/testsuite/ztest/include/ztest_test_new.h @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * @brief Zephyr testing framework _test. + */ + +#ifndef ZEPHYR_TESTSUITE_ZTEST_TEST_H_ +#define ZEPHYR_TESTSUITE_ZTEST_TEST_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ztest_unit_test { + const char *test_suite_name; + const char *name; + void (*test)(void *data); + uint32_t thread_options; +}; + +extern struct ztest_unit_test _ztest_unit_test_list_start[]; +extern struct ztest_unit_test _ztest_unit_test_list_end[]; + +/** + * Stats about a ztest suite + */ +struct ztest_suite_stats { + /** The number of times that the suite ran */ + uint32_t run_count; + /** The number of times that the suite was skipped */ + uint32_t skip_count; + /** The number of times that the suite failed */ + uint32_t fail_count; +}; + +/** + * A single node of test suite. Each node should be added to a single linker section which will + * allow ztest_run_test_suites() to iterate over the various nodes. + */ +struct ztest_suite_node { + /** The name of the test suite. */ + const char *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); + /** + * Function to run before each test in this suite + * + * @param data The test suite's data returned from setup() + */ + void (*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); + /** + * Teardown function to run after running this suite + * + * @param data The test suite's data returned from setup() + */ + void (*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. + * + * @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); + /** Stats */ + struct ztest_suite_stats stats; +}; + +extern struct ztest_suite_node _ztest_suite_node_list_start[]; +extern struct ztest_suite_node _ztest_suite_node_list_end[]; + +/** + * Create and register a ztest suite. Using this macro creates a new test suite (using + * ztest_test_suite). It then creates a struct ztest_suite_node in a specific linker section. + * + * Tests can then be run by calling ztest_run_test_suites(const void *state) by passing + * in the current state. See the documentation for ztest_run_test_suites for more info. + * + * @param SUITE_NAME The name of the suite (see ztest_test_suite for more info) + * @param PREDICATE A function to test against the state and determine if the test should run. + * @param setup_fn The setup function to call before running this test suite + * @param before_fn The function to call before each unit test in this suite + * @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, z_ztest_test_node_##SUITE_NAME) = { \ + .name = #SUITE_NAME, \ + .setup = (setup_fn), \ + .before = (before_fn), \ + .after = (after_fn), \ + .teardown = (teardown_fn), \ + .predicate = PREDICATE, \ + } + +/** + * 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); + +/** + * @brief Fails the test if any of the registered tests did not run. + * + * When registering test suites, a pragma function can be provided to determine WHEN the test should + * run. It is possible that a test suite could be registered but the pragma always prevents it from + * running. In cases where a test should make sure that ALL suites ran at least once, this function + * may be called at the end of test_main(). It will cause the test to fail if any suite was + * registered but never ran. + */ +void ztest_verify_all_test_suites_ran(void); + +/** + * @brief Run a test suite. + * + * Internal implementation. Do not call directly. This will run the full test suite along with some + * checks for fast failures and initialization. + * + * @param name The name of the suite to run. + * @return Negative value if the test suite never ran; otherwise, return the number of failures. + */ +int z_ztest_run_test_suite(const char *name); + +/** + * @defgroup ztest_test Ztest testing macros + * @ingroup ztest + * + * This module eases the testing process by providing helpful macros and other + * testing structures. + * + * @{ + */ + +/** + * @brief Fail the currently running test. + * + * This is the function called from failed assertions and the like. You + * probably don't need to call it yourself. + */ +void ztest_test_fail(void); + +/** + * @brief Pass the currently running test. + * + * Normally a test passes just by returning without an assertion failure. + * However, if the success case for your test involves a fatal fault, + * you can call this function from k_sys_fatal_error_handler to indicate that + * the test passed before aborting the thread. + */ +void ztest_test_pass(void); + +/** + * @brief Skip the current test. + * + */ +void ztest_test_skip(void); + +/** + * @brief Do nothing, successfully. + * + * Unit test / setup function / teardown function that does + * nothing, successfully. Can be used as a parameter to + * ztest_unit_test_setup_teardown(). + */ +static inline void unit_test_noop(void) +{ +} + +#define Z_TEST(suite, fn, t_options, use_fixture) \ + static void _##suite##_##fn##_wrapper(void *data); \ + static void suite##_##fn( \ + COND_CODE_1(use_fixture, (struct suite##_fixture *this), (void))); \ + static STRUCT_SECTION_ITERABLE(ztest_unit_test, z_ztest_unit_test_##suite##_##fn) = { \ + .test_suite_name = STRINGIFY(suite), \ + .name = STRINGIFY(fn), \ + .test = (_##suite##_##fn##_wrapper), \ + .thread_options = t_options, \ + }; \ + static void _##suite##_##fn##_wrapper(void *data) \ + { \ + COND_CODE_1(use_fixture, (suite##_##fn((struct suite##_fixture *)data);), \ + (ARG_UNUSED(data); suite##_##fn();)) \ + } \ + static inline void suite##_##fn( \ + COND_CODE_1(use_fixture, (struct suite##_fixture *this), (void))) + +#define Z_ZTEST(suite, fn, t_options) Z_TEST(suite, fn, t_options, 0) +#define Z_ZTEST_F(suite, fn, t_options) Z_TEST(suite, fn, t_options, 1) + +/** + * @brief Create and register a new unit test. + * + * Calling this macro will create a new unit test and attach it to the declared `suite`. The `suite` + * does not need to be defined in the same compilational unit. + * + * @param suite The name of the test suite to attach this test + * @param fn The test function to call. + */ +#define ZTEST(suite, fn) Z_ZTEST(suite, fn, 0) + +/** + * @brief Define a test function that should run as a user thread + * + * This macro behaves exactly the same as ZTEST, but calls the test function in user space if + * `CONFIG_USERSPACE` was enabled. + * + * @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))) + +/** + * @brief Define a test function + * + * This macro behaves exactly the same as ZTEST(), but the function takes an argument for the + * fixture of type `struct suite##_fixture*` named `this`. + * + * @param suite The name of the test suite to attach this test + * @param fn The test function to call. + */ +#define ZTEST_F(suite, fn) Z_ZTEST_F(suite, fn, 0) + +/** + * @brief Define a test function that should run as a user thread + * + * If CONFIG_USERSPACE is not enabled, this is functionally identical to ZTEST_F(). The test + * function takes a single fixture argument of type `struct suite##_fixture*` named `this`. + * + * @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))) + +typedef void (*ztest_rule_cb)(const struct ztest_unit_test *test, void *data); + +struct ztest_test_rule { + ztest_rule_cb before_each; + ztest_rule_cb after_each; +}; + +#define ZTEST_RULE(name, before_each_fn, after_each_fn) \ + static STRUCT_SECTION_ITERABLE(ztest_test_rule, z_ztest_test_rule_##name) = { \ + .before_each = (before_each_fn), \ + .after_each = (after_each_fn), \ + } + +extern struct ztest_test_rule _ztest_test_rule_list_start[]; +extern struct ztest_test_rule _ztest_test_rule_list_end[]; + +/** + * @brief A 'before' function to use in test suites that just need to start 1cpu + * + * Ignores data, and calls z_test_1cpu_start() + * + * @param data The test suite's data + */ +void ztest_simple_1cpu_before(void *data); + +/** + * @brief A 'after' function to use in test suites that just need to stop 1cpu + * + * Ignores data, and calls z_test_1cpu_stop() + * + * @param data The test suite's data + */ +void ztest_simple_1cpu_after(void *data); + +/* definitions for use with testing application shared memory */ +#ifdef CONFIG_USERSPACE +#define ZTEST_DMEM K_APP_DMEM(ztest_mem_partition) +#define ZTEST_BMEM K_APP_BMEM(ztest_mem_partition) +#define ZTEST_SECTION K_APP_DMEM_SECTION(ztest_mem_partition) +extern struct k_mem_partition ztest_mem_partition; +#else +#define ZTEST_DMEM +#define ZTEST_BMEM +#define ZTEST_SECTION .data +#endif + +/** + * @brief Run the specified test suite. + * + * @param suite Test suite to run. + */ +#define ztest_run_test_suite(suite) z_ztest_run_test_suite(STRINGIFY(suite)) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_TESTSUITE_ZTEST_TEST_H_ */ diff --git a/subsys/testsuite/ztest/src/ztest_new.c b/subsys/testsuite/ztest/src/ztest_new.c new file mode 100644 index 00000000000..43ee17aaa11 --- /dev/null +++ b/subsys/testsuite/ztest/src/ztest_new.c @@ -0,0 +1,627 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#ifdef CONFIG_USERSPACE +#include +#endif +#include +#include + +#ifdef KERNEL +static struct k_thread ztest_thread; +#endif + +#ifdef CONFIG_ARCH_POSIX +#include +#endif + +/* ZTEST_DMEM and ZTEST_BMEM are used for the application shared memory test */ + +ZTEST_DMEM enum { + TEST_PHASE_SETUP, + TEST_PHASE_BEFORE, + TEST_PHASE_TEST, + TEST_PHASE_AFTER, + TEST_PHASE_TEARDOWN, + TEST_PHASE_FRAMEWORK +} phase = TEST_PHASE_FRAMEWORK; + +static ZTEST_BMEM int test_status; + +/** + * @brief Try to shorten a filename by removing the current directory + * + * This helps to reduce the very long filenames in assertion failures. It + * removes the current directory from the filename and returns the rest. + * This makes assertions a lot more readable, and sometimes they fit on one + * line. + * + * @param file Filename to check + * @returns Shortened filename, or @file if it could not be shortened + */ +const char *ztest_relative_filename(const char *file) +{ +#ifdef CONFIG_ARCH_POSIX + const char *cwd; + char buf[200]; + + cwd = getcwd(buf, sizeof(buf)); + if (cwd && strlen(file) > strlen(cwd) && + !strncmp(file, cwd, strlen(cwd))) + return file + strlen(cwd) + 1; /* move past the trailing '/' */ +#endif + return file; +} + +static int cleanup_test(struct ztest_unit_test *test) +{ + int ret = TC_PASS; + int mock_status; + + mock_status = z_cleanup_mock(); + +#ifdef KERNEL + /* we need to remove the ztest_thread information from the timeout_q. + * Because we reuse the same k_thread structure this would + * causes some problems. + */ + if (IS_ENABLED(CONFIG_MULTITHREADING)) { + k_thread_abort(&ztest_thread); + } +#endif + + 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; + } else { + ; + } + + return ret; +} + +#ifdef KERNEL +#ifdef CONFIG_SMP +#define NUM_CPUHOLD (CONFIG_MP_NUM_CPUS - 1) +#else +#define NUM_CPUHOLD 0 +#endif +#define CPUHOLD_STACK_SZ (512 + CONFIG_TEST_EXTRA_STACKSIZE) + +static struct k_thread cpuhold_threads[NUM_CPUHOLD]; +K_KERNEL_STACK_ARRAY_DEFINE(cpuhold_stacks, NUM_CPUHOLD, CPUHOLD_STACK_SZ); +static struct k_sem cpuhold_sem; +volatile int cpuhold_active; + +/* "Holds" a CPU for use with the "1cpu" test cases. Note that we + * can't use tools like the cpumask feature because we have tests that + * may need to control that configuration themselves. We do this at + * the lowest level, but locking interrupts directly and spinning. + */ +static void cpu_hold(void *arg1, void *arg2, void *arg3) +{ + ARG_UNUSED(arg1); + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + unsigned int key = arch_irq_lock(); + uint32_t dt, start_ms = k_uptime_get_32(); + + k_sem_give(&cpuhold_sem); + +#if defined(CONFIG_ARM64) && defined(CONFIG_FPU_SHARING) + /* + * We'll be spinning with IRQs disabled. The flush-your-FPU request + * IPI will never be serviced during that time. Therefore we flush + * the FPU preemptively here to prevent any other CPU waiting after + * this CPU forever and deadlock the system. + */ + extern void z_arm64_flush_local_fpu(void); + z_arm64_flush_local_fpu(); +#endif + + while (cpuhold_active) { + k_busy_wait(1000); + } + + /* Holding the CPU via spinning is expensive, and abusing this + * for long-running test cases tends to overload the CI system + * (qemu runs separate CPUs in different threads, but the CI + * logic views it as one "job") and cause other test failures. + */ + dt = k_uptime_get_32() - start_ms; + zassert_true(dt < 3000, + "1cpu test took too long (%d ms)", dt); + arch_irq_unlock(key); +} + +void z_impl_z_test_1cpu_start(void) +{ + cpuhold_active = 1; + char tname[CONFIG_THREAD_MAX_NAME_LEN]; + + k_sem_init(&cpuhold_sem, 0, 999); + + /* Spawn N-1 threads to "hold" the other CPUs, waiting for + * each to signal us that it's locked and spinning. + * + * Note that NUM_CPUHOLD can be a value that causes coverity + * to flag the following loop as DEADCODE so suppress the warning. + */ + /* coverity[DEADCODE] */ + for (int i = 0; i < NUM_CPUHOLD; i++) { + k_thread_create(&cpuhold_threads[i], + cpuhold_stacks[i], CPUHOLD_STACK_SZ, + (k_thread_entry_t) cpu_hold, NULL, NULL, NULL, + K_HIGHEST_THREAD_PRIO, 0, K_NO_WAIT); + if (IS_ENABLED(CONFIG_THREAD_NAME)) { + snprintk(tname, CONFIG_THREAD_MAX_NAME_LEN, "cpuhold%02d", i); + k_thread_name_set(&cpuhold_threads[i], tname); + } + k_sem_take(&cpuhold_sem, K_FOREVER); + } +} + +void z_impl_z_test_1cpu_stop(void) +{ + cpuhold_active = 0; + + /* Note that NUM_CPUHOLD can be a value that causes coverity + * to flag the following loop as DEADCODE so suppress the warning. + */ + /* coverity[DEADCODE] */ + for (int i = 0; i < NUM_CPUHOLD; i++) { + k_thread_abort(&cpuhold_threads[i]); + } +} + +#ifdef CONFIG_USERSPACE +void z_vrfy_z_test_1cpu_start(void) +{ + z_impl_z_test_1cpu_start(); +} +#include + +void z_vrfy_z_test_1cpu_stop(void) +{ + z_impl_z_test_1cpu_stop(); +} +#include +#endif /* CONFIG_USERSPACE */ +#endif + +static void run_test_rules(bool is_before, struct ztest_unit_test *test, void *data) +{ + for (struct ztest_test_rule *rule = _ztest_test_rule_list_start; + rule < _ztest_test_rule_list_end; ++rule) { + if (is_before && rule->before_each) { + rule->before_each(test, data); + } else if (!is_before && rule->after_each) { + rule->after_each(test, data); + } + } +} + +static void run_test_functions(struct ztest_suite_node *suite, struct ztest_unit_test *test, + void *data) +{ + phase = TEST_PHASE_TEST; + test->test(data); +} + +#ifndef KERNEL + +/* Static code analysis tool can raise a violation that the standard header + * shall not be used. + * + * setjmp is using in a test code, not in a runtime code, it is acceptable. + * It is a deliberate deviation. + */ +#include /* parasoft-suppress MISRAC2012-RULE_21_4-a MISRAC2012-RULE_21_4-b*/ +#include +#include +#include + +#define FAIL_FAST 0 + +static jmp_buf test_fail; +static jmp_buf test_pass; +static jmp_buf stack_fail; + +void ztest_test_fail(void) +{ + raise(SIGABRT); +} + +void ztest_test_pass(void) +{ + longjmp(test_pass, 1); +} + +static void handle_signal(int sig) +{ + static const char *const phase_str[] = { + "setup", + "unit test", + "teardown", + }; + + PRINT(" %s", strsignal(sig)); + switch (phase) { + case TEST_PHASE_SETUP: + case TEST_PHASE_BEFORE: + case TEST_PHASE_TEST: + case TEST_PHASE_AFTER: + 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 ztest_suite_node *suite, struct ztest_unit_test *test, void *data) +{ + 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; + } + + run_test_functions(suite, test, data); +out: + ret |= cleanup_test(test); + Z_TC_END_RESULT(ret, test->name); + + return ret; +} + +#else /* KERNEL */ + +/* Zephyr's probably going to cause all tests to fail if one test fails, so + * skip the rest of tests if one of them fails + */ +#ifdef CONFIG_ZTEST_FAIL_FAST +#define FAIL_FAST 1 +#else +#define FAIL_FAST 0 +#endif + +K_THREAD_STACK_DEFINE(ztest_thread_stack, CONFIG_ZTEST_STACKSIZE + CONFIG_TEST_EXTRA_STACKSIZE); +static ZTEST_BMEM int test_result; + +static void test_finalize(void) +{ + if (IS_ENABLED(CONFIG_MULTITHREADING)) { + k_thread_abort(&ztest_thread); + k_thread_abort(k_current_get()); + } +} + +void ztest_test_fail(void) +{ + test_result = -1; + test_finalize(); +} + +void ztest_test_pass(void) +{ + test_result = 0; + test_finalize(); +} + +void ztest_test_skip(void) +{ + test_result = -2; + test_finalize(); +} + +void ztest_simple_1cpu_before(void *data) +{ + ARG_UNUSED(data); + z_test_1cpu_start(); +} + +void ztest_simple_1cpu_after(void *data) +{ + ARG_UNUSED(data); + z_test_1cpu_stop(); +} + +static void init_testing(void) +{ + k_object_access_all_grant(&ztest_thread); +} + +static void test_cb(void *a, void *b, void *c) +{ + struct ztest_suite_node *suite = a; + struct ztest_unit_test *test = b; + + test_result = 1; + run_test_functions(suite, test, c); + test_result = 0; +} + +static int run_test(struct ztest_suite_node *suite, struct ztest_unit_test *test, void *data) +{ + int ret = TC_PASS; + + TC_START(test->name); + + phase = TEST_PHASE_BEFORE; + if (suite->before) { + suite->before(data); + } + run_test_rules(/*is_before=*/true, test, data); + + if (IS_ENABLED(CONFIG_MULTITHREADING)) { + k_thread_create(&ztest_thread, ztest_thread_stack, + K_THREAD_STACK_SIZEOF(ztest_thread_stack), + (k_thread_entry_t)test_cb, suite, test, data, + CONFIG_ZTEST_THREAD_PRIORITY, + test->thread_options | K_INHERIT_PERMS, K_FOREVER); + + if (test->name != NULL) { + k_thread_name_set(&ztest_thread, test->name); + } + k_thread_start(&ztest_thread); + k_thread_join(&ztest_thread, K_FOREVER); + } else { + test_result = 1; + run_test_functions(suite, test, data); + } + + phase = TEST_PHASE_AFTER; + if (suite->after != NULL) { + suite->after(data); + } + run_test_rules(/*is_before=*/false, test, data); + phase = TEST_PHASE_FRAMEWORK; + + /* Flush all logs in case deferred mode and default logging thread are used. */ + while (IS_ENABLED(CONFIG_TEST_LOGGING_FLUSH_AFTER_TEST) && + IS_ENABLED(CONFIG_LOG_PROCESS_THREAD) && + log_data_pending()) { + k_msleep(100); + } + + if (test_result == -1) { + ret = TC_FAIL; + } + + if (!test_result || !FAIL_FAST) { + ret |= cleanup_test(test); + } + + if (test_result == -2) { + Z_TC_END_RESULT(TC_SKIP, test->name); + } else { + Z_TC_END_RESULT(ret, test->name); + } + + return ret; +} + +#endif /* !KERNEL */ + +static struct ztest_suite_node *ztest_find_test_suite(const char *name) +{ + struct ztest_suite_node *node; + + for (node = _ztest_suite_node_list_start; node < _ztest_suite_node_list_end; ++node) { + if (strcmp(name, node->name) == 0) { + return node; + } + } + + return NULL; +} + +struct ztest_unit_test *ztest_get_next_test(const char *suite, struct ztest_unit_test *prev) +{ + struct ztest_unit_test *test = (prev == NULL) ? _ztest_unit_test_list_start : prev + 1; + + for (; test < _ztest_unit_test_list_end; ++test) { + if (strcmp(suite, test->test_suite_name) == 0) { + return test; + } + } + return NULL; +} + +static int z_ztest_run_test_suite_ptr(struct ztest_suite_node *suite) +{ + struct ztest_unit_test *test = NULL; + void *data = NULL; + int fail = 0; + + if (test_status < 0) { + return test_status; + } + + if (suite == NULL) { + test_status = 1; + return -1; + } + + init_testing(); + + TC_SUITE_START(suite->name); + phase = TEST_PHASE_SETUP; + if (suite->setup != NULL) { + data = suite->setup(); + } + while ((test = ztest_get_next_test(suite->name, test)) != NULL) { + fail += run_test(suite, test, data); + + if (fail && FAIL_FAST) { + break; + } + } + TC_SUITE_END(suite->name, (fail > 0 ? TC_FAIL : TC_PASS)); + phase = TEST_PHASE_TEARDOWN; + if (suite->teardown != NULL) { + suite->teardown(data); + } + + test_status = (test_status || fail) ? 1 : 0; + + return fail; +} + +int z_ztest_run_test_suite(const char *name) +{ + return z_ztest_run_test_suite_ptr(ztest_find_test_suite(name)); +} + +void end_report(void) +{ + if (test_status) { + TC_END_REPORT(TC_FAIL); + } else { + TC_END_REPORT(TC_PASS); + } +} + +#ifdef CONFIG_USERSPACE +K_APPMEM_PARTITION_DEFINE(ztest_mem_partition); +#endif + +int ztest_run_test_suites(const void *state) +{ + struct ztest_suite_node *ptr; + int count = 0; + + 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; + } + + 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; +} + +void ztest_verify_all_test_suites_ran(void) +{ + bool all_tests_run = true; + 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) { + PRINT("ERROR: Test '%s' did not run.\n", ptr->name); + all_tests_run = false; + } + } + + if (!all_tests_run) { + test_status = 1; + } +} + +void __weak test_main(void) +{ + ztest_run_test_suites(NULL); + ztest_verify_all_test_suites_ran(); +} + +#ifndef KERNEL +int main(void) +{ + z_init_mock(); + test_main(); + end_report(); + + return test_status; +} +#else +void main(void) +{ +#ifdef CONFIG_USERSPACE + /* Partition containing globals tagged with ZTEST_DMEM and ZTEST_BMEM + * macros. Any variables that user code may reference need to be + * placed in this partition if no other memory domain configuration + * is made. + */ + k_mem_domain_add_partition(&k_mem_domain_default, + &ztest_mem_partition); +#ifdef Z_MALLOC_PARTITION_EXISTS + /* Allow access to malloc() memory */ + k_mem_domain_add_partition(&k_mem_domain_default, + &z_malloc_partition); +#endif +#endif /* CONFIG_USERSPACE */ + + z_init_mock(); + test_main(); + end_report(); + if (IS_ENABLED(CONFIG_ZTEST_RETEST_IF_PASSED)) { + static __noinit struct { + uint32_t magic; + uint32_t boots; + } state; + const uint32_t magic = 0x152ac523; + + if (state.magic != magic) { + state.magic = magic; + state.boots = 0; + } + state.boots += 1; + if (test_status == 0) { + PRINT("Reset board #%u to test again\n", + state.boots); + k_msleep(10); + sys_reboot(SYS_REBOOT_COLD); + } else { + PRINT("Failed after %u attempts\n", state.boots); + state.boots = 0; + } + } +} +#endif diff --git a/subsys/testsuite/ztest/src/ztest_rules.c b/subsys/testsuite/ztest/src/ztest_rules.c new file mode 100644 index 00000000000..90c5531e1e7 --- /dev/null +++ b/subsys/testsuite/ztest/src/ztest_rules.c @@ -0,0 +1,23 @@ +/* + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#ifdef CONFIG_ZTEST_RULE_1CPU +static void one_cpu_rule_before_each(const struct ztest_unit_test *test, void *data) +{ + ARG_UNUSED(test); + ARG_UNUSED(data); + z_test_1cpu_start(); +} +static void one_cpu_rule_after_each(const struct ztest_unit_test *test, void *data) +{ + ARG_UNUSED(test); + ARG_UNUSED(data); + z_test_1cpu_stop(); +} +ZTEST_RULE(one_cpu, one_cpu_rule_before_each, one_cpu_rule_after_each); +#endif /* CONFIG_ZTEST_RULE_1CPU */ diff --git a/tests/subsys/logging/log_api/prj.conf b/tests/subsys/logging/log_api/prj.conf index dfa69fc1fc1..dff0c49b090 100644 --- a/tests/subsys/logging/log_api/prj.conf +++ b/tests/subsys/logging/log_api/prj.conf @@ -1,5 +1,6 @@ CONFIG_MAIN_THREAD_PRIORITY=5 CONFIG_ZTEST=y +CONFIG_ZTEST_NEW_API=y CONFIG_TEST_USERSPACE=y CONFIG_TEST_LOGGING_DEFAULTS=n CONFIG_LOG=y diff --git a/tests/subsys/logging/log_api/src/main.c b/tests/subsys/logging/log_api/src/main.c index 0720b8cba83..c50c6fcd072 100644 --- a/tests/subsys/logging/log_api/src/main.c +++ b/tests/subsys/logging/log_api/src/main.c @@ -9,12 +9,3 @@ #include #include "test.inc" - -void test_cxx(void); -void test_cc(void); - -void test_main(void) -{ - test_cc(); - test_cxx(); -} diff --git a/tests/subsys/logging/log_api/src/test.inc b/tests/subsys/logging/log_api/src/test.inc index e39c69a5142..89c79c4219a 100644 --- a/tests/subsys/logging/log_api/src/test.inc +++ b/tests/subsys/logging/log_api/src/test.inc @@ -627,20 +627,15 @@ static void test_log_panic(void) mock_log_backend_validate(&backend1, true); } -/* Disable backends because same suite may be excuted again but compiled by C++ */ -static void log_api_suite_teardown(void) +/* Disable backends because same suite may be executed again but compiled by C++ */ +static void log_api_suite_teardown(void *data) { + ARG_UNUSED(data); log_backend_disable(&backend1); log_backend_disable(&backend2); } -/*test case main entry*/ -#if __cplusplus -extern "C" void test_cxx(void); -void test_cxx(void) -#else -void test_cc(void) -#endif +static void *log_api_suite_setup(void) { PRINT("Configuration:\n"); PRINT("\t Mode: %s\n", @@ -654,17 +649,35 @@ void test_cc(void) #if __cplusplus PRINT("\t C++: yes\n"); #endif - - ztest_test_suite(test_log_api, - ztest_unit_test(test_log_various_messages), - ztest_unit_test(test_log_backend_runtime_filtering), - ztest_unit_test(test_log_overflow), - ztest_unit_test(test_log_arguments), - ztest_unit_test(test_log_from_declared_module), - ztest_unit_test(test_log_msg_dropped_notification), - ztest_unit_test(test_log_panic) - ); - ztest_run_test_suite(test_log_api); - - log_api_suite_teardown(); + return NULL; } + +static void log_api_suite_before(void *data) +{ + ARG_UNUSED(data); + while(LOG_PROCESS()) { + } +} + +#define WRAP_TEST(test_name, suffix) \ + ZTEST(test_log_api_##suffix, test_name##_##suffix) \ + { \ + test_name(); \ + } + +#if __cplusplus +#define TEST_SUFFIX cxx +#else +#define TEST_SUFFIX cc +#endif +#define TEST_SUITE_NAME test_log_api_ ## TEST_SUFFIX + +ZTEST_SUITE(TEST_SUITE_NAME, NULL, log_api_suite_setup, + log_api_suite_before, NULL, log_api_suite_teardown); +WRAP_TEST(test_log_various_messages, TEST_SUFFIX) +WRAP_TEST(test_log_backend_runtime_filtering, TEST_SUFFIX) +WRAP_TEST(test_log_overflow, TEST_SUFFIX) +WRAP_TEST(test_log_arguments, TEST_SUFFIX) +WRAP_TEST(test_log_from_declared_module, TEST_SUFFIX) +WRAP_TEST(test_log_msg_dropped_notification, TEST_SUFFIX) +WRAP_TEST(test_log_panic, TEST_SUFFIX) diff --git a/tests/ztest/base/CMakeLists.txt b/tests/ztest/base/CMakeLists.txt index 517b4f0d97f..e6da41a3086 100644 --- a/tests/ztest/base/CMakeLists.txt +++ b/tests/ztest/base/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.20.0) if(BOARD STREQUAL unit_testing) - list(APPEND SOURCES src/main.c) + list(APPEND SOURCES src/main_deprecated.c) find_package(ZephyrUnittest REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(base) @@ -10,6 +10,5 @@ else() find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(base) - FILE(GLOB app_sources src/*.c) - target_sources(app PRIVATE ${app_sources}) + target_sources(app PRIVATE src/main.c) endif() diff --git a/tests/ztest/base/prj_verbose_0.conf b/tests/ztest/base/prj_verbose_0.conf index c47f69824da..b4903cd10cb 100644 --- a/tests/ztest/base/prj_verbose_0.conf +++ b/tests/ztest/base/prj_verbose_0.conf @@ -1,2 +1,3 @@ CONFIG_ZTEST=y +CONFIG_ZTEST_NEW_API=y CONFIG_ZTEST_ASSERT_VERBOSE=0 diff --git a/tests/ztest/base/prj_verbose_1.conf b/tests/ztest/base/prj_verbose_1.conf index 79028c27963..ef6cb613538 100644 --- a/tests/ztest/base/prj_verbose_1.conf +++ b/tests/ztest/base/prj_verbose_1.conf @@ -1,2 +1,3 @@ CONFIG_ZTEST=y +CONFIG_ZTEST_NEW_API=y CONFIG_ZTEST_ASSERT_VERBOSE=1 diff --git a/tests/ztest/base/prj_verbose_2.conf b/tests/ztest/base/prj_verbose_2.conf index 0bda78a5b4f..c995cf19ee0 100644 --- a/tests/ztest/base/prj_verbose_2.conf +++ b/tests/ztest/base/prj_verbose_2.conf @@ -1,2 +1,3 @@ CONFIG_ZTEST=y +CONFIG_ZTEST_NEW_API=y CONFIG_ZTEST_ASSERT_VERBOSE=2 diff --git a/tests/ztest/base/src/main.c b/tests/ztest/base/src/main.c index 7c2482051bd..07fc9eb4747 100644 --- a/tests/ztest/base/src/main.c +++ b/tests/ztest/base/src/main.c @@ -6,11 +6,13 @@ #include -static void test_empty_test(void) +ZTEST_SUITE(framework_tests, NULL, NULL, NULL, NULL, NULL); + +ZTEST(framework_tests, test_empty_test) { } -static void test_assert_tests(void) +ZTEST(framework_tests, test_assert_tests) { zassert_true(1, NULL); zassert_false(0, NULL); @@ -20,7 +22,7 @@ static void test_assert_tests(void) zassert_equal_ptr(NULL, NULL, NULL); } -static void test_assert_mem_equal(void) +ZTEST(framework_tests, test_assert_mem_equal) { static const uint32_t expected[4] = { 0x1234, @@ -33,13 +35,94 @@ static void test_assert_mem_equal(void) zassert_mem_equal(actual, expected, sizeof(expected), NULL); } -void test_main(void) -{ - ztest_test_suite(framework_tests, - ztest_unit_test(test_empty_test), - ztest_unit_test(test_assert_tests), - ztest_unit_test(test_assert_mem_equal) - ); +/*************************************************************************************************** + * Sample fixture tests + **************************************************************************************************/ - ztest_run_test_suite(framework_tests); +struct fixture_tests_fixture { +}; + +static struct fixture_tests_fixture test_fixture; + +static void *fixture_tests_setup(void) +{ + return &test_fixture; +} + +ZTEST_SUITE(fixture_tests, NULL, fixture_tests_setup, NULL, NULL, NULL); + +ZTEST_F(fixture_tests, test_fixture_pointer) +{ + zassert_equal_ptr(&test_fixture, this, "Test fixture should be at 0x%x but was at 0x%x", + &test_fixture, this); +} + +/*************************************************************************************************** + * Sample rule tests + **************************************************************************************************/ + +enum rule_state { + RULE_STATE_SETUP = 0, + RULE_STATE_BEFORE_EACH, + RULE_STATE_TEST, + RULE_STATE_AFTER_EACH, +}; + +struct rules_tests_fixture { + enum rule_state state; +}; + +static struct rules_tests_fixture rule_tests_fixture; + +static void rule_before_each(const struct ztest_unit_test *test, void *data) +{ + if (strcmp(test->test_suite_name, "rules_tests") == 0 && + strcmp(test->name, "test_rules_before_after") == 0) { + struct rules_tests_fixture *fixture = data; + + zassert_equal_ptr(&rule_tests_fixture, data, + "Data expected to point to rule_state"); + zassert_equal(fixture->state, RULE_STATE_SETUP, "Unexpected state"); + fixture->state = RULE_STATE_BEFORE_EACH; + } +} + +static void rule_after_each(const struct ztest_unit_test *test, void *data) +{ + if (strcmp(test->test_suite_name, "rules_tests") == 0 && + strcmp(test->name, "test_rules_before_after") == 0) { + struct rules_tests_fixture *fixture = data; + + zassert_equal_ptr(&rule_tests_fixture, data, + "Data expected to point to rule_state"); + zassert_equal(fixture->state, RULE_STATE_TEST, "Unexpected state"); + fixture->state = RULE_STATE_AFTER_EACH; + } +} + +static void *rule_test_setup(void) +{ + rule_tests_fixture.state = RULE_STATE_SETUP; + return &rule_tests_fixture; +} + +static void rule_test_teardown(void *data) +{ + struct rules_tests_fixture *fixture = data; + + /* + * Normally, we wouldn't assert here, but it's the only way to test that the rule's + * after_each function was called. + */ + zassert_equal(fixture->state, RULE_STATE_AFTER_EACH, "Unexpected state"); +} + +ZTEST_RULE(verify_before_after_rule, rule_before_each, rule_after_each); + +ZTEST_SUITE(rules_tests, NULL, rule_test_setup, NULL, NULL, rule_test_teardown); + +ZTEST_F(rules_tests, test_rules_before_after) +{ + zassert_equal(this->state, RULE_STATE_BEFORE_EACH, "Unexpected state"); + this->state = RULE_STATE_TEST; } diff --git a/tests/ztest/base/src/main_deprecated.c b/tests/ztest/base/src/main_deprecated.c new file mode 100644 index 00000000000..6ae589f5880 --- /dev/null +++ b/tests/ztest/base/src/main_deprecated.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +static void test_empty_test(void) +{ +} + +static void test_assert_tests(void) +{ + zassert_true(1, NULL); + zassert_false(0, NULL); + zassert_is_null(NULL, NULL); + zassert_not_null("foo", NULL); + zassert_equal(1, 1, NULL); + zassert_equal_ptr(NULL, NULL, NULL); +} + +static void test_assert_mem_equal(void) +{ + static const uint32_t expected[4] = { + 0x1234, + 0x5678, + 0x9ABC, + 0xDEF0 + }; + uint32_t actual[4] = {0}; + + memcpy(actual, expected, sizeof(actual)); + zassert_mem_equal(actual, expected, sizeof(expected), NULL); +} + +void test_main(void) +{ + ztest_test_suite(framework_tests, + ztest_unit_test(test_empty_test), + ztest_unit_test(test_assert_tests), + ztest_unit_test(test_assert_mem_equal) + ); + + ztest_run_test_suite(framework_tests); +}