docs: ztest: Update documentation for new API
Documentation now includes: - How to create a test suite - How to use predicates - How to use setup/before/after/teardown - How to use test rules - Direct people to use FFF instead of ztest mock Fixes #47420 Signed-off-by: Yuval Peress <peress@google.com>
This commit is contained in:
parent
2f49bec7b7
commit
b513e621b0
4 changed files with 524 additions and 293 deletions
|
@ -10,3 +10,4 @@ Testing
|
|||
twister
|
||||
coverage
|
||||
sparse
|
||||
ztest_deprecated
|
||||
|
|
|
@ -10,6 +10,171 @@ test structure.
|
|||
The framework can be used in two ways, either as a generic framework for
|
||||
integration testing, or for unit testing specific modules.
|
||||
|
||||
To enable the latest APIs of Ztest simply set :kconfig:option:`CONFIG_ZTEST_NEW_API=y`. The legacy
|
||||
APIs will soon be deprecated and eventually removed.
|
||||
|
||||
Creating a test suite
|
||||
*********************
|
||||
|
||||
Using Ztest to create a test suite is as easy as calling the :c:macro:`ZTEST_SUITE`. The macro
|
||||
accepts the following arguments:
|
||||
|
||||
* ``suite_name`` - The name of the suite. This name must be unique within a single binary.
|
||||
* :c:type:`ztest_suite_predicate_t` - An optional predicate function to allow choosing when the
|
||||
test will run. The predicate will get a pointer to the global state passed in through
|
||||
:c:func:`ztest_run_all` and should return a boolean to decide if the suite should run.
|
||||
* :c:type:`ztest_suite_setup_t` - An optional setup function which returns a test fixture. This
|
||||
will be called and run once per test suite run.
|
||||
* :c:type:`ztest_suite_before_t` - An optional before function which will run before every single
|
||||
test in this suite.
|
||||
* :c:type:`ztest_suite_after_t` - An optional after function which will run after every single
|
||||
test in this suite.
|
||||
* :c:type:`ztest_suite_teardown_t` - An optional teardown function which will run at the end of
|
||||
all the tests in the suite.
|
||||
|
||||
Below is an example of a test suite using a predicate::
|
||||
|
||||
#include <zephyr/ztest.h>
|
||||
#include "test_state.h"
|
||||
|
||||
static bool predicate(const void *global_state)
|
||||
{
|
||||
return ((const struct test_state*)global_state)->x == 5;
|
||||
}
|
||||
|
||||
ZTEST_SUITE(alternating_suite, predicate, NULL, NULL, NULL, NULL);
|
||||
|
||||
Adding tests to a suite
|
||||
***********************
|
||||
|
||||
There are 4 macros used to add a test to a suite, they are:
|
||||
|
||||
* :c:macro:`ZTEST` ``(suite_name, test_name)`` - Which can be used to add a test by ``test_name`` to a
|
||||
given suite by ``suite_name``.
|
||||
* :c:macro:`ZTEST_USER` ``(suite_name, test_name)`` - Which behaves the same as :c:macro:`ZTEST`, only
|
||||
that when :kconfig:option:`CONFIG_USERSPACE` is enabled, then the test will be run in a userspace
|
||||
thread.
|
||||
* :c:macro:`ZTEST_F` ``(suite_name, test_name)`` - Which behaves the same as :c:macro:`ZTEST`, only
|
||||
that the test function will already include a variable named ``fixture`` with the type
|
||||
``<suite_name>_fixture``.
|
||||
* :c:macro:`ZTEST_USER_F` ``(suite_name, test_name)`` - Which combines the fixture feature of
|
||||
:c:macro:`ZTEST_F` with the userspace threading for the test.
|
||||
|
||||
Test fixtures
|
||||
=============
|
||||
|
||||
Test fixtures can be used to help simplify repeated test setup operations. In many cases, tests in
|
||||
the same suite will require some initial setup followed by some form of reset between each test.
|
||||
This is achieved via fixtures in the following way::
|
||||
|
||||
#include <zephyr/ztest.h>
|
||||
|
||||
struct my_suite_fixture {
|
||||
size_t max_size;
|
||||
size_t size;
|
||||
uint8_t buff[1];
|
||||
};
|
||||
|
||||
static void *my_suite_setup(void)
|
||||
{
|
||||
/* Allocate the fixture with 256 byte buffer */
|
||||
struct my_suite_fixture *fixture = k_malloc(sizeof(struct my_suite_fixture) + 255);
|
||||
|
||||
zassume_not_null(fixture, NULL);
|
||||
fixture->max_size = 256;
|
||||
|
||||
return fixture;
|
||||
}
|
||||
|
||||
static void my_suite_before(void *f)
|
||||
{
|
||||
struct my_suite_fixture *fixture = (struct my_suite_fixture *)f;
|
||||
memset(fixture->buff, 0, fixture->max_size);
|
||||
fixture->size = 0;
|
||||
}
|
||||
|
||||
static void my_suite_teardown(void *f)
|
||||
{
|
||||
k_free(f);
|
||||
}
|
||||
|
||||
ZTEST_SUITE(my_suite, NULL, my_suite_setup, my_suite_before, NULL, my_suite_teardown);
|
||||
|
||||
ZTEST_F(my_suite, test_feature_x)
|
||||
{
|
||||
zassert_equal(0, fixture->size);
|
||||
zassert_equal(256, fixture->max_size);
|
||||
}
|
||||
|
||||
|
||||
Advanced features
|
||||
*****************
|
||||
|
||||
Test rules
|
||||
==========
|
||||
|
||||
Test rules are a way to run the same logic for every test and every suite. There are a lot of cases
|
||||
where you might want to reset some state for every test in the binary (regardless of which suite is
|
||||
currently running). As an example, this could be to reset mocks, reset emulators, flush the UART,
|
||||
etc.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
#include <zephyr/fff.h>
|
||||
#include <zephyr/ztest.h>
|
||||
|
||||
#include "test_mocks.h"
|
||||
|
||||
DEFINE_FFF_GLOBALS;
|
||||
|
||||
DEFINE_FAKE_VOID_FUN(my_weak_func);
|
||||
|
||||
static void fff_reset_rule_before(const struct ztest_unit_test *test, void *fixture)
|
||||
{
|
||||
ARG_UNUSED(test);
|
||||
ARG_UNUSED(fixture);
|
||||
|
||||
RESET_FAKE(my_weak_func);
|
||||
}
|
||||
|
||||
ZTEST_RULE(fff_reset_rule, fff_reset_rule_before, NULL);
|
||||
|
||||
A custom ``test_main``
|
||||
======================
|
||||
|
||||
While the Ztest framework provides a default :c:func:`test_main` function, it's possible that some
|
||||
applications will want to provide custom behavior. This is particularly true if there's some global
|
||||
state that the tests depend on and that state either cannot be replicated or is difficult to
|
||||
replicate without starting the process over. For example, one such state could be a power sequence.
|
||||
Assuming there's a board with several steps in the power-on sequence a test suite can be written
|
||||
using the ``predicate`` to control when it would run. In that case, the :c:func:`test_main`
|
||||
function can be written as following::
|
||||
|
||||
#include <zephyr/ztest.h>
|
||||
|
||||
#include "my_test.h"
|
||||
|
||||
void test_main(void)
|
||||
{
|
||||
struct power_sequence_state state;
|
||||
|
||||
/* Only suites that use a predicate checking for phase == PWR_PHASE_0 will run. */
|
||||
state.phase = PWR_PHASE_0;
|
||||
ztest_run_test_suites(&state);
|
||||
|
||||
/* Only suites that use a predicate checking for phase == PWR_PHASE_1 will run. */
|
||||
state.phase = PWR_PHASE_1;
|
||||
ztest_run_test_suites(&state);
|
||||
|
||||
/* Only suites that use a predicate checking for phase == PWR_PHASE_2 will run. */
|
||||
state.phase = PWR_PHASE_2;
|
||||
ztest_run_test_suites(&state);
|
||||
|
||||
/* Check that all the suites in this binary ran at least once. */
|
||||
ztest_verify_all_test_suites_ran();
|
||||
}
|
||||
|
||||
|
||||
Quick start - Integration testing
|
||||
*********************************
|
||||
|
||||
|
@ -79,17 +244,12 @@ An example can be seen below::
|
|||
*
|
||||
* This test verifies the zassert_true macro.
|
||||
*/
|
||||
static void test_assert(void)
|
||||
ZTEST(my_suite, test_assert)
|
||||
{
|
||||
zassert_true(1, "1 was false");
|
||||
}
|
||||
|
||||
|
||||
The above test is then enabled as part of the testsuite using::
|
||||
|
||||
ztest_unit_test(test_assert)
|
||||
|
||||
|
||||
Listing Tests
|
||||
=============
|
||||
|
||||
|
@ -104,6 +264,7 @@ can list all kernel test cases, for example, by entering::
|
|||
|
||||
twister --list-tests -T tests/kernel
|
||||
|
||||
|
||||
Skipping Tests
|
||||
==============
|
||||
|
||||
|
@ -113,43 +274,28 @@ report them as being skipped. Because the test inventory and
|
|||
the list of tests is extracted from the code, adding
|
||||
conditionals inside the test suite is sub-optimal. Tests that need
|
||||
to be skipped for a certain platform or feature need to explicitly
|
||||
report a skip using :c:func:`ztest_test_skip`. If the test runs,
|
||||
report a skip using :c:func:`ztest_test_skip` or :c:macro:`Z_TEST_SKIP_IFDEF`. If the test runs,
|
||||
it needs to report either a pass or fail. For example::
|
||||
|
||||
#ifdef CONFIG_TEST1
|
||||
void test_test1(void)
|
||||
#ifdef CONFIG_TEST1
|
||||
ZTEST(common, test_test1)
|
||||
{
|
||||
zassert_true(1, "true");
|
||||
zassert_true(1, "true");
|
||||
}
|
||||
#else
|
||||
void test_test1(void)
|
||||
ZTEST(common, test_test1)
|
||||
{
|
||||
ztest_test_skip();
|
||||
}
|
||||
#endif
|
||||
|
||||
ZTEST(common, test_test2)
|
||||
{
|
||||
Z_TEST_SKIP_IFDEF(CONFIG_BUGxxxxx);
|
||||
zassert_equal(1, 0, NULL);
|
||||
}
|
||||
|
||||
void test_main(void)
|
||||
{
|
||||
ztest_test_suite(common,
|
||||
ztest_unit_test(test_test1),
|
||||
ztest_unit_test(test_test2)
|
||||
);
|
||||
ztest_run_test_suite(common);
|
||||
}
|
||||
|
||||
Use the following macro at the start of your test to skip it with a KConfig
|
||||
option.
|
||||
|
||||
#define Z_TEST_SKIP_IFDEF(config)
|
||||
|
||||
For example::
|
||||
|
||||
void test_test1(void)
|
||||
{
|
||||
Z_TEST_SKIP_IFDEF(CONFIG_BUGxxxxx);
|
||||
zassert_equal(1, 0, NULL);
|
||||
}
|
||||
ZTEST_SUITE(common, NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
Quick start - Unit testing
|
||||
**************************
|
||||
|
@ -169,8 +315,6 @@ and are used to decide whether a test failed or passed by verifying whether an
|
|||
interaction with an object occurred, and if required, to assert the order of
|
||||
that interaction.
|
||||
|
||||
.. _main_c_bp:
|
||||
|
||||
Best practices for declaring the test suite
|
||||
===========================================
|
||||
|
||||
|
@ -189,193 +333,6 @@ subcases that a Zephyr *ztest* test image will expose.
|
|||
high-level test project level, particularly when tests do too
|
||||
many things, is too vague.
|
||||
|
||||
There exist two alternatives to writing tests. The first, and more verbose,
|
||||
approach is to directly declare and run the test suites.
|
||||
Here is a generic template for a test showing the expected use of
|
||||
:c:func:`ztest_test_suite`:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
#include <zephyr/ztest.h>
|
||||
|
||||
extern void test_sometest1(void);
|
||||
extern void test_sometest2(void);
|
||||
#ifndef CONFIG_WHATEVER /* Conditionally skip test_sometest3 */
|
||||
void test_sometest3(void)
|
||||
{
|
||||
ztest_test_skip();
|
||||
}
|
||||
#else
|
||||
extern void test_sometest3(void);
|
||||
#endif
|
||||
extern void test_sometest4(void);
|
||||
...
|
||||
|
||||
void test_main(void)
|
||||
{
|
||||
ztest_test_suite(common,
|
||||
ztest_unit_test(test_sometest1),
|
||||
ztest_unit_test(test_sometest2),
|
||||
ztest_unit_test(test_sometest3),
|
||||
ztest_unit_test(test_sometest4)
|
||||
);
|
||||
ztest_run_test_suite(common);
|
||||
}
|
||||
|
||||
Alternatively, it is possible to split tests across multiple files using
|
||||
:c:func:`ztest_register_test_suite` which bypasses the need for ``extern``:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
#include <zephyr/ztest.h>
|
||||
|
||||
void test_sometest1(void) {
|
||||
zassert_true(1, "true");
|
||||
}
|
||||
|
||||
ztest_register_test_suite(common, NULL,
|
||||
ztest_unit_test(test_sometest1)
|
||||
);
|
||||
|
||||
The above sample simple registers the test suite and uses a ``NULL`` pragma
|
||||
function (more on that later). It is important to note that the test suite isn't
|
||||
directly run in this file. Instead two alternatives exist for running the suite.
|
||||
First, if to do nothing. A default ``test_main`` function is provided by
|
||||
ztest. This is the preferred approach if the test doesn't involve a state and
|
||||
doesn't require use of the pragma.
|
||||
|
||||
In cases of an integration test it is possible that some general state needs to
|
||||
be set between test suites. This can be thought of as a state diagram in which
|
||||
``test_main`` simply goes through various actions that modify the board's
|
||||
state and different test suites need to run. This is achieved in the following:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
#include <zephyr/ztest.h>
|
||||
|
||||
struct state {
|
||||
bool is_hibernating;
|
||||
bool is_usb_connected;
|
||||
}
|
||||
|
||||
static bool pragma_always(const void *state)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pragma_not_hibernating_not_connected(const void *s)
|
||||
{
|
||||
struct state *state = s;
|
||||
return !state->is_hibernating && !state->is_usb_connected;
|
||||
}
|
||||
|
||||
static bool pragma_usb_connected(const void *s)
|
||||
{
|
||||
return ((struct state *)s)->is_usb_connected;
|
||||
}
|
||||
|
||||
ztest_register_test_suite(baseline, pragma_always,
|
||||
ztest_unit_test(test_case0));
|
||||
ztest_register_test_suite(before_usb, pragma_not_hibernating_not_connected,
|
||||
ztest_unit_test(test_case1),
|
||||
ztest_unit_test(test_case2));
|
||||
ztest_register_test_suite(with_usb, pragma_usb_connected,,
|
||||
ztest_unit_test(test_case3),
|
||||
ztest_unit_test(test_case4));
|
||||
|
||||
void test_main(void)
|
||||
{
|
||||
struct state state;
|
||||
|
||||
/* Should run `baseline` test suite only. */
|
||||
ztest_run_registered_test_suites(&state);
|
||||
|
||||
/* Simulate power on and update state. */
|
||||
emulate_power_on();
|
||||
/* Should run `baseline` and `before_usb` test suites. */
|
||||
ztest_run_registered_test_suites(&state);
|
||||
|
||||
/* Simulate plugging in a USB device. */
|
||||
emulate_plugging_in_usb();
|
||||
/* Should run `baseline` and `with_usb` test suites. */
|
||||
ztest_run_registered_test_suites(&state);
|
||||
|
||||
/* Verify that all the registered test suites actually ran. */
|
||||
ztest_verify_all_registered_test_suites_ran();
|
||||
}
|
||||
|
||||
For *twister* to parse source files and create a list of subcases,
|
||||
the declarations of :c:func:`ztest_test_suite` and
|
||||
:c:func:`ztest_register_test_suite` must follow a few rules:
|
||||
|
||||
- one declaration per line
|
||||
|
||||
- conditional execution by using :c:func:`ztest_test_skip`
|
||||
|
||||
What to avoid:
|
||||
|
||||
- packing multiple testcases in one source file
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void test_main(void)
|
||||
{
|
||||
#ifdef TEST_feature1
|
||||
ztest_test_suite(feature1,
|
||||
ztest_unit_test(test_1a),
|
||||
ztest_unit_test(test_1b),
|
||||
ztest_unit_test(test_1c)
|
||||
);
|
||||
ztest_run_test_suite(feature1);
|
||||
#endif
|
||||
|
||||
#ifdef TEST_feature2
|
||||
ztest_test_suite(feature2,
|
||||
ztest_unit_test(test_2a),
|
||||
ztest_unit_test(test_2b)
|
||||
);
|
||||
ztest_run_test_suite(feature2);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
- Do not use ``#if``
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
ztest_test_suite(common,
|
||||
ztest_unit_test(test_sometest1),
|
||||
ztest_unit_test(test_sometest2),
|
||||
#ifdef CONFIG_WHATEVER
|
||||
ztest_unit_test(test_sometest3),
|
||||
#endif
|
||||
ztest_unit_test(test_sometest4),
|
||||
...
|
||||
|
||||
- Do not add comments on lines with a call to :c:func:`ztest_unit_test`:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
ztest_test_suite(common,
|
||||
ztest_unit_test(test_sometest1),
|
||||
ztest_unit_test(test_sometest2) /* will fail */,
|
||||
/* will fail! */ ztest_unit_test(test_sometest3),
|
||||
ztest_unit_test(test_sometest4),
|
||||
...
|
||||
|
||||
- Do not define multiple definitions of unit / user unit test case per
|
||||
line
|
||||
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
ztest_test_suite(common,
|
||||
ztest_unit_test(test_sometest1), ztest_unit_test(test_sometest2),
|
||||
ztest_unit_test(test_sometest3),
|
||||
ztest_unit_test(test_sometest4),
|
||||
...
|
||||
|
||||
|
||||
Other questions:
|
||||
|
||||
- Why not pre-scan with CPP and then parse? or post scan the ELF file?
|
||||
|
@ -492,24 +449,17 @@ Example output for a failed macro from
|
|||
|
||||
.. doxygengroup:: ztest_assume
|
||||
|
||||
Mocking
|
||||
=======
|
||||
|
||||
These functions allow abstracting callbacks and related functions and
|
||||
controlling them from specific tests. You can enable the mocking framework by
|
||||
setting :kconfig:option:`CONFIG_ZTEST_MOCKING` to "y" in the configuration file of the
|
||||
test. The amount of concurrent return values and expected parameters is
|
||||
limited by :kconfig:option:`CONFIG_ZTEST_PARAMETER_COUNT`.
|
||||
.. _mocking-fff:
|
||||
|
||||
Here is an example for configuring the function ``expect_two_parameters`` to
|
||||
expect the values ``a=2`` and ``b=3``, and telling ``returns_int`` to return
|
||||
``5``:
|
||||
Mocking via FFF
|
||||
===============
|
||||
|
||||
.. literalinclude:: mocking.c
|
||||
:language: c
|
||||
:linenos:
|
||||
Zephyr has integrated with FFF for mocking. See `FFF`_ for documentation. To use it, use the
|
||||
following in your source::
|
||||
|
||||
#include <zephyr/fff.h>
|
||||
|
||||
.. doxygengroup:: ztest_mock
|
||||
|
||||
Customizing Test Output
|
||||
***********************
|
||||
|
@ -558,3 +508,5 @@ For example
|
|||
$ zephyr.exe -list
|
||||
$ zephyr.exe -test="fixture_tests::test_fixture_pointer,framework_tests::test_assert_mem_equal"
|
||||
$ zephyr.exe -test="framework_tests::*"
|
||||
|
||||
.. _FFF: https://github.com/meekrosoft/fff
|
||||
|
|
266
doc/develop/test/ztest_deprecated.rst
Normal file
266
doc/develop/test/ztest_deprecated.rst
Normal file
|
@ -0,0 +1,266 @@
|
|||
.. _test-framework-deprecated:
|
||||
|
||||
ZTest Deprecated APIs
|
||||
#####################
|
||||
|
||||
Ztest is currently being migrated to a new API, this documentation provides information about the
|
||||
deprecated APIs which will eventually be removed. See :ref:`Test Framework <test-framework>` for the new API.
|
||||
Similarly, ZTest's mocking framework is also deprecated (see :ref:`Mocking via FFF <mocking-fff>`).
|
||||
|
||||
Quick start - Unit testing
|
||||
**************************
|
||||
|
||||
Ztest can be used for unit testing. This means that rather than including the
|
||||
entire Zephyr OS for testing a single function, you can focus the testing
|
||||
efforts into the specific module in question. This will speed up testing since
|
||||
only the module will have to be compiled in, and the tested functions will be
|
||||
called directly.
|
||||
|
||||
Since you won't be including basic kernel data structures that most code
|
||||
depends on, you have to provide function stubs in the test. Ztest provides
|
||||
some helpers for mocking functions, as demonstrated below.
|
||||
|
||||
In a unit test, mock objects can simulate the behavior of complex real objects
|
||||
and are used to decide whether a test failed or passed by verifying whether an
|
||||
interaction with an object occurred, and if required, to assert the order of
|
||||
that interaction.
|
||||
|
||||
.. _main_c_bp:
|
||||
|
||||
Best practices for declaring the test suite
|
||||
===========================================
|
||||
|
||||
*twister* and other validation tools need to obtain the list of
|
||||
subcases that a Zephyr *ztest* test image will expose.
|
||||
|
||||
.. admonition:: Rationale
|
||||
|
||||
This all is for the purpose of traceability. It's not enough to
|
||||
have only a semaphore test project. We also need to show that we
|
||||
have testpoints for all APIs and functionality, and we trace back
|
||||
to documentation of the API, and functional requirements.
|
||||
|
||||
The idea is that test reports show results for every sub-testcase
|
||||
as passed, failed, blocked, or skipped. Reporting on only the
|
||||
high-level test project level, particularly when tests do too
|
||||
many things, is too vague.
|
||||
|
||||
There exist two alternatives to writing tests. The first, and more verbose,
|
||||
approach is to directly declare and run the test suites.
|
||||
Here is a generic template for a test showing the expected use of
|
||||
:c:func:`ztest_test_suite`:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
#include <zephyr/ztest.h>
|
||||
|
||||
extern void test_sometest1(void);
|
||||
extern void test_sometest2(void);
|
||||
#ifndef CONFIG_WHATEVER /* Conditionally skip test_sometest3 */
|
||||
void test_sometest3(void)
|
||||
{
|
||||
ztest_test_skip();
|
||||
}
|
||||
#else
|
||||
extern void test_sometest3(void);
|
||||
#endif
|
||||
extern void test_sometest4(void);
|
||||
...
|
||||
|
||||
void test_main(void)
|
||||
{
|
||||
ztest_test_suite(common,
|
||||
ztest_unit_test(test_sometest1),
|
||||
ztest_unit_test(test_sometest2),
|
||||
ztest_unit_test(test_sometest3),
|
||||
ztest_unit_test(test_sometest4)
|
||||
);
|
||||
ztest_run_test_suite(common);
|
||||
}
|
||||
|
||||
Alternatively, it is possible to split tests across multiple files using
|
||||
:c:func:`ztest_register_test_suite` which bypasses the need for ``extern``:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
#include <zephyr/ztest.h>
|
||||
|
||||
void test_sometest1(void) {
|
||||
zassert_true(1, "true");
|
||||
}
|
||||
|
||||
ztest_register_test_suite(common, NULL,
|
||||
ztest_unit_test(test_sometest1)
|
||||
);
|
||||
|
||||
The above sample simple registers the test suite and uses a ``NULL`` pragma
|
||||
function (more on that later). It is important to note that the test suite isn't
|
||||
directly run in this file. Instead two alternatives exist for running the suite.
|
||||
First, if to do nothing. A default ``test_main`` function is provided by
|
||||
ztest. This is the preferred approach if the test doesn't involve a state and
|
||||
doesn't require use of the pragma.
|
||||
|
||||
In cases of an integration test it is possible that some general state needs to
|
||||
be set between test suites. This can be thought of as a state diagram in which
|
||||
``test_main`` simply goes through various actions that modify the board's
|
||||
state and different test suites need to run. This is achieved in the following:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
#include <zephyr/ztest.h>
|
||||
|
||||
struct state {
|
||||
bool is_hibernating;
|
||||
bool is_usb_connected;
|
||||
}
|
||||
|
||||
static bool pragma_always(const void *state)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pragma_not_hibernating_not_connected(const void *s)
|
||||
{
|
||||
struct state *state = s;
|
||||
return !state->is_hibernating && !state->is_usb_connected;
|
||||
}
|
||||
|
||||
static bool pragma_usb_connected(const void *s)
|
||||
{
|
||||
return ((struct state *)s)->is_usb_connected;
|
||||
}
|
||||
|
||||
ztest_register_test_suite(baseline, pragma_always,
|
||||
ztest_unit_test(test_case0));
|
||||
ztest_register_test_suite(before_usb, pragma_not_hibernating_not_connected,
|
||||
ztest_unit_test(test_case1),
|
||||
ztest_unit_test(test_case2));
|
||||
ztest_register_test_suite(with_usb, pragma_usb_connected,,
|
||||
ztest_unit_test(test_case3),
|
||||
ztest_unit_test(test_case4));
|
||||
|
||||
void test_main(void)
|
||||
{
|
||||
struct state state;
|
||||
|
||||
/* Should run `baseline` test suite only. */
|
||||
ztest_run_registered_test_suites(&state);
|
||||
|
||||
/* Simulate power on and update state. */
|
||||
emulate_power_on();
|
||||
/* Should run `baseline` and `before_usb` test suites. */
|
||||
ztest_run_registered_test_suites(&state);
|
||||
|
||||
/* Simulate plugging in a USB device. */
|
||||
emulate_plugging_in_usb();
|
||||
/* Should run `baseline` and `with_usb` test suites. */
|
||||
ztest_run_registered_test_suites(&state);
|
||||
|
||||
/* Verify that all the registered test suites actually ran. */
|
||||
ztest_verify_all_registered_test_suites_ran();
|
||||
}
|
||||
|
||||
For *twister* to parse source files and create a list of subcases,
|
||||
the declarations of :c:func:`ztest_test_suite` and
|
||||
:c:func:`ztest_register_test_suite` must follow a few rules:
|
||||
|
||||
- one declaration per line
|
||||
|
||||
- conditional execution by using :c:func:`ztest_test_skip`
|
||||
|
||||
What to avoid:
|
||||
|
||||
- packing multiple testcases in one source file
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void test_main(void)
|
||||
{
|
||||
#ifdef TEST_feature1
|
||||
ztest_test_suite(feature1,
|
||||
ztest_unit_test(test_1a),
|
||||
ztest_unit_test(test_1b),
|
||||
ztest_unit_test(test_1c)
|
||||
);
|
||||
ztest_run_test_suite(feature1);
|
||||
#endif
|
||||
|
||||
#ifdef TEST_feature2
|
||||
ztest_test_suite(feature2,
|
||||
ztest_unit_test(test_2a),
|
||||
ztest_unit_test(test_2b)
|
||||
);
|
||||
ztest_run_test_suite(feature2);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
- Do not use ``#if``
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
ztest_test_suite(common,
|
||||
ztest_unit_test(test_sometest1),
|
||||
ztest_unit_test(test_sometest2),
|
||||
#ifdef CONFIG_WHATEVER
|
||||
ztest_unit_test(test_sometest3),
|
||||
#endif
|
||||
ztest_unit_test(test_sometest4),
|
||||
...
|
||||
|
||||
- Do not add comments on lines with a call to :c:func:`ztest_unit_test`:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
ztest_test_suite(common,
|
||||
ztest_unit_test(test_sometest1),
|
||||
ztest_unit_test(test_sometest2) /* will fail */,
|
||||
/* will fail! */ ztest_unit_test(test_sometest3),
|
||||
ztest_unit_test(test_sometest4),
|
||||
...
|
||||
|
||||
- Do not define multiple definitions of unit / user unit test case per
|
||||
line
|
||||
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
ztest_test_suite(common,
|
||||
ztest_unit_test(test_sometest1), ztest_unit_test(test_sometest2),
|
||||
ztest_unit_test(test_sometest3),
|
||||
ztest_unit_test(test_sometest4),
|
||||
...
|
||||
|
||||
|
||||
Other questions:
|
||||
|
||||
- Why not pre-scan with CPP and then parse? or post scan the ELF file?
|
||||
|
||||
If C pre-processing or building fails because of any issue, then we
|
||||
won't be able to tell the subcases.
|
||||
|
||||
- Why not declare them in the YAML testcase description?
|
||||
|
||||
A separate testcase description file would be harder to maintain
|
||||
than just keeping the information in the test source files
|
||||
themselves -- only one file to update when changes are made
|
||||
eliminates duplication.
|
||||
|
||||
Mocking
|
||||
*******
|
||||
|
||||
These functions allow abstracting callbacks and related functions and
|
||||
controlling them from specific tests. You can enable the mocking framework by
|
||||
setting :kconfig:option:`CONFIG_ZTEST_MOCKING` to "y" in the configuration file of the
|
||||
test. The amount of concurrent return values and expected parameters is
|
||||
limited by :kconfig:option:`CONFIG_ZTEST_PARAMETER_COUNT`.
|
||||
|
||||
Here is an example for configuring the function ``expect_two_parameters`` to
|
||||
expect the values ``a=2`` and ``b=3``, and telling ``returns_int`` to return
|
||||
``5``:
|
||||
|
||||
.. literalinclude:: mocking.c
|
||||
:language: c
|
||||
:linenos:
|
||||
|
||||
.. doxygengroup:: ztest_mock
|
|
@ -50,6 +50,43 @@ struct ztest_suite_stats {
|
|||
uint32_t fail_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup function to run before running this suite
|
||||
*
|
||||
* @return Pointer to the data structure that will be used throughout this test suite
|
||||
*/
|
||||
typedef void *(*ztest_suite_setup_t)(void);
|
||||
|
||||
/**
|
||||
* Function to run before each test in this suite
|
||||
*
|
||||
* @param fixture The test suite's fixture returned from setup()
|
||||
*/
|
||||
typedef void (*ztest_suite_before_t)(void *fixture);
|
||||
|
||||
/**
|
||||
* Function to run after each test in this suite
|
||||
*
|
||||
* @param fixture The test suite's fixture returned from setup()
|
||||
*/
|
||||
typedef void (*ztest_suite_after_t)(void *fixture);
|
||||
|
||||
/**
|
||||
* Teardown function to run after running this suite
|
||||
*
|
||||
* @param fixture The test suite's data returned from setup()
|
||||
*/
|
||||
typedef void (*ztest_suite_teardown_t)(void *fixture);
|
||||
|
||||
/**
|
||||
* 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 global_state The current state of the test application.
|
||||
* @return True if the suite should be run; false to skip.
|
||||
*/
|
||||
typedef bool (*ztest_suite_predicate_t)(const void *global_state);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -57,38 +94,22 @@ struct ztest_suite_stats {
|
|||
struct ztest_suite_node {
|
||||
/** The name of the test suite. */
|
||||
const char *const name;
|
||||
/**
|
||||
* Setup function to run before running this suite
|
||||
*
|
||||
* @return Pointer to the data structure that will be used throughout this test suite
|
||||
*/
|
||||
void *(*const setup)(void);
|
||||
/**
|
||||
* Function to run before each test in this suite
|
||||
*
|
||||
* @param data The test suite's data returned from setup()
|
||||
*/
|
||||
void (*const before)(void *data);
|
||||
/**
|
||||
* Function to run after each test in this suite
|
||||
*
|
||||
* @param data The test suite's data returned from setup()
|
||||
*/
|
||||
void (*const after)(void *data);
|
||||
/**
|
||||
* Teardown function to run after running this suite
|
||||
*
|
||||
* @param data The test suite's data returned from setup()
|
||||
*/
|
||||
void (*const 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 (*const predicate)(const void *state);
|
||||
|
||||
/** Setup function */
|
||||
const ztest_suite_setup_t setup;
|
||||
|
||||
/** Before function */
|
||||
const ztest_suite_before_t before;
|
||||
|
||||
/** After function */
|
||||
const ztest_suite_after_t after;
|
||||
|
||||
/** Teardown function */
|
||||
const ztest_suite_teardown_t teardown;
|
||||
|
||||
/** Optional predicate filter */
|
||||
const ztest_suite_predicate_t predicate;
|
||||
|
||||
/** Stats */
|
||||
struct ztest_suite_stats *const stats;
|
||||
};
|
||||
|
@ -131,7 +152,7 @@ extern struct ztest_suite_node _ztest_suite_node_list_end[];
|
|||
void ztest_run_all(const void *state);
|
||||
|
||||
/**
|
||||
* Run the registered unit tests which return true from their pragma function.
|
||||
* Run the registered unit tests which return true from their predicate function.
|
||||
*
|
||||
* @param state The current state of the machine as it relates to the test executable.
|
||||
* @return The number of tests that ran.
|
||||
|
@ -181,6 +202,18 @@ int z_ztest_run_test_suite(const char *name);
|
|||
*/
|
||||
struct ztest_unit_test *z_ztest_get_next_test(const char *suite, struct ztest_unit_test *prev);
|
||||
|
||||
/* 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
|
||||
|
||||
/**
|
||||
* @defgroup ztest_test Ztest testing macros
|
||||
* @ingroup ztest
|
||||
|
@ -215,17 +248,6 @@ void ztest_test_pass(void);
|
|||
*/
|
||||
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( \
|
||||
|
@ -323,6 +345,7 @@ static inline void unit_test_noop(void)
|
|||
*/
|
||||
typedef void (*ztest_rule_cb)(const struct ztest_unit_test *test, void *data);
|
||||
|
||||
/** @private */
|
||||
struct ztest_test_rule {
|
||||
ztest_rule_cb before_each;
|
||||
ztest_rule_cb after_each;
|
||||
|
@ -343,8 +366,9 @@ struct ztest_test_rule {
|
|||
* - Test rule's `after` function is not guaranteed to run in any particular order.
|
||||
*
|
||||
* @param name The name for the test rule (must be unique within the compilation unit)
|
||||
* @param before_each_fn The callback function to call before each test (may be NULL)
|
||||
* @param after_each_fn The callback function to call after each test (may be NULL)
|
||||
* @param before_each_fn The callback function (ztest_rule_cb) to call before each test
|
||||
* (may be NULL)
|
||||
* @param after_each_fn The callback function (ztest_rule_cb) to call after each test (may be NULL)
|
||||
*/
|
||||
#define ZTEST_RULE(name, before_each_fn, after_each_fn) \
|
||||
static STRUCT_SECTION_ITERABLE(ztest_test_rule, z_ztest_test_rule_##name) = { \
|
||||
|
@ -373,18 +397,6 @@ void ztest_simple_1cpu_before(void *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.
|
||||
*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue