Testing

Creating a test suite

ZTEST_SUITE

/**
 * 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)             \
	struct ztest_suite_stats UTIL_CAT(z_ztest_suite_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_suite_node_stats_, SUITE_NAME),             \
	}

.setup: 每个test suite run都会调用一次。
.before: 每个single test in the test suite run前都会调用一次。
.after: 每个single test in the test suite run后都会调用一次。
.teardown: 所有tests跑完后会调用一次。
.predicate: return bool类型的函数,可以在里面判断全局变量状态,来决定是否要进行test。

Adding tests to a suite

ZTEST(suite_name, test_name): suite_name为上面通过ZTEST_SUITE创建的SUITE_NAME, test_name为测试名称。
ZTEST_USER(suite_name, test_name): 和ZTEST类似,不过在CONFIG_USERSPACE打开后,测试会跑在userspace线程。
ZTEST_F(suite_name, test_name): 和ZTEST类似,不过会自带一个类型为<suite_name>_fixturefixture变量。
ZTEST_USER_F:结合2,3两条。

Quick start - Integration testing 集成测试

./scripts/twister -s tests/foo/bar/test-identifier 运行定义在testcase.yaml中的test-identifier。

./scripts/twister -T tests/foo/bar/ 运行所有foo/bar/目录下的测试。

几条规则:

  • 定义的测试名需要有test_前缀。
  • 测试需要用doxygen说明。
  • 测试名不能重复。

e.g.

/**
 * @brief Test Asserts
 *
 * This test verifies the zassert_true macro.
 */
ZTEST(my_suite, test_assert)
{
        zassert_true(1, "1 was false");
}

Listing Tests

twister --list-tests -T tests/kernel: 列出tests/kernel下的所有测试。

Skipping Tests

利用 ztest_test_skip() or Z_TEST_SKIP_IFDEF


#ifdef CONFIG_TEST1
ZTEST(common, test_test1)
{
     zassert_true(1, "true");
}
#else
ZTEST(common, test_test1)
{
     ztest_test_skip();
}
#endif

ZTEST(common, test_test2)
{
     Z_TEST_SKIP_IFDEF(CONFIG_BUGxxxxx);
     zassert_equal(1, 0, NULL);
}

Quick start - Unit testing 单元测试

examples:tests/unit

CMakeLists需要指明unit test:

cmake_minimum_required(VERSION 3.20.0)

project(app)
find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE})
target_sources(testbinary PRIVATE main.c)

Stress test framework

//TODO:

Test Runner (Twister)