mirror of
https://git.rtems.org/rtems-docs/
synced 2025-07-24 04:44:18 +08:00
eng: Update test framework chapter
Document the dynamic text fixtures, utility functions, and the interrupt test support. Reorder some sections and reword some paragraphs based on review comments. Update #3199.
This commit is contained in:
parent
31b1d88aac
commit
21fa28c7ad
@ -11,8 +11,8 @@ RTEMS Software Engineering (|version|)
|
|||||||
|
|
||||||
.. topic:: Copyrights and License
|
.. topic:: Copyrights and License
|
||||||
|
|
||||||
| |copy| 2018, 2019 embedded brains GmbH
|
| |copy| 2018, 2020 embedded brains GmbH
|
||||||
| |copy| 2018, 2019 Sebastian Huber
|
| |copy| 2018, 2020 Sebastian Huber
|
||||||
| |copy| 1988, 2015 On-Line Applications Research Corporation (OAR)
|
| |copy| 1988, 2015 On-Line Applications Research Corporation (OAR)
|
||||||
|
|
||||||
.. include:: ../common/license.rst
|
.. include:: ../common/license.rst
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
.. SPDX-License-Identifier: CC-BY-SA-4.0
|
.. SPDX-License-Identifier: CC-BY-SA-4.0
|
||||||
|
|
||||||
.. Copyright (C) 2018, 2019 embedded brains GmbH
|
.. Copyright (C) 2018, 2020 embedded brains GmbH
|
||||||
.. Copyright (C) 2018, 2019 Sebastian Huber
|
.. Copyright (C) 2018, 2020 Sebastian Huber
|
||||||
|
|
||||||
Software Test Framework
|
Software Test Framework
|
||||||
***********************
|
***********************
|
||||||
@ -16,6 +16,8 @@ features:
|
|||||||
|
|
||||||
* Implemented in standard C11
|
* Implemented in standard C11
|
||||||
|
|
||||||
|
* Tests can be written in C or C++
|
||||||
|
|
||||||
* Runs on at least FreeBSD, MSYS2, Linux and RTEMS
|
* Runs on at least FreeBSD, MSYS2, Linux and RTEMS
|
||||||
|
|
||||||
* Test runner and test case code can be in separate translation units
|
* Test runner and test case code can be in separate translation units
|
||||||
@ -67,11 +69,11 @@ A `test suite` is a collection of test cases. A `test case` consists of
|
|||||||
individual test actions and checks. A `test check` determines if the outcome
|
individual test actions and checks. A `test check` determines if the outcome
|
||||||
of a test action meets its expectation. A `test action` is a program sequence
|
of a test action meets its expectation. A `test action` is a program sequence
|
||||||
with an observable outcome, for example a function invocation with a return
|
with an observable outcome, for example a function invocation with a return
|
||||||
status. If the test action outcome is all right, then the test check passes,
|
status. If a test action produces the expected outcome as determined by the
|
||||||
otherwise the test check fails. The test check failures of a test case are
|
corresponding test check, then this test check passes, otherwise this test
|
||||||
summed up. A test case passes, if the failure count of this test case is zero,
|
check fails. The test check failures of a test case are summed up. A test
|
||||||
otherwise the test case fails. The test suite passes if all test cases pass,
|
case passes, if the failure count of this test case is zero, otherwise the test
|
||||||
otherwise it fails.
|
case fails. The test suite passes if all test cases pass, otherwise it fails.
|
||||||
|
|
||||||
Test Cases
|
Test Cases
|
||||||
----------
|
----------
|
||||||
@ -89,7 +91,7 @@ body:
|
|||||||
The test case `name` must be a valid C designator. The test case names must be
|
The test case `name` must be a valid C designator. The test case names must be
|
||||||
unique within the test suite. Just link modules with test cases to the test
|
unique within the test suite. Just link modules with test cases to the test
|
||||||
runner to form a test suite. The test cases are automatically registered via
|
runner to form a test suite. The test cases are automatically registered via
|
||||||
static constructors.
|
static C constructors.
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
:caption: Test Case Example
|
:caption: Test Case Example
|
||||||
@ -144,13 +146,43 @@ macro followed by a function body:
|
|||||||
|
|
||||||
The test case `name` must be a valid C designator. The test case names must be
|
The test case `name` must be a valid C designator. The test case names must be
|
||||||
unique within the test suite. The `fixture` must point to a statically
|
unique within the test suite. The `fixture` must point to a statically
|
||||||
initialized read-only object of type `T_fixture`. The test fixture
|
initialized read-only object of type `T_fixture`.
|
||||||
provides methods to setup, stop and tear down a test case. A context is passed
|
|
||||||
to the methods. The initial context is defined by the read-only fixture
|
.. code-block:: c
|
||||||
object. The context can be obtained by the `T_fixture_context()`
|
|
||||||
function. It can be set within the scope of one test case by the
|
typedef struct T_fixture {
|
||||||
`T_set_fixture_context()` function. This can be used for example to
|
void (*setup)(void *context);
|
||||||
dynamically allocate a test environment in the setup method.
|
void (*stop)(void *context);
|
||||||
|
void (*teardown)(void *context);
|
||||||
|
void (*scope)(void *context, char *buffer, size_t size);
|
||||||
|
void *initial_context;
|
||||||
|
} T_fixture;
|
||||||
|
|
||||||
|
The test fixture provides methods to setup, stop, and teardown a test case as
|
||||||
|
well as the scope for log messages. A context is passed to each of the
|
||||||
|
methods. The initial context is defined by the read-only fixture object. The
|
||||||
|
context can be obtained by the `T_fixture_context()` function. The context can
|
||||||
|
be changed within the scope of one test case by the `T_set_fixture_context()`
|
||||||
|
function. The next test case execution using the same fixture will start again
|
||||||
|
with the initial context defined by the read-only fixture object. Setting the
|
||||||
|
context can be used for example to dynamically allocate a test environment in
|
||||||
|
the setup method.
|
||||||
|
|
||||||
|
The test case fixtures of a test case are organized as a stack. Fixtures can
|
||||||
|
be dynamically added to a test case and removed from a test case via the
|
||||||
|
`T_push_fixture()` and `T_pop_fixture()` functions.
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
void *T_push_fixture(T_fixture_node *node, const T_fixture *fixture);
|
||||||
|
|
||||||
|
void T_pop_fixture(void);
|
||||||
|
|
||||||
|
The `T_push_fixture()` function needs an uninitialized fixture node which must
|
||||||
|
exist until `T_pop_fixture()` is called. It returns the initial context of the
|
||||||
|
fixture. At the end of a test case all pushed fixtures are popped
|
||||||
|
automatically. A call of `T_pop_fixture()` invokes the teardown method of the
|
||||||
|
fixture and must correspond to a previous call to `T_push_fixture()`.
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
:caption: Test Fixture Example
|
:caption: Test Fixture Example
|
||||||
@ -237,9 +269,9 @@ dynamically allocate a test environment in the setup method.
|
|||||||
Test Case Planning
|
Test Case Planning
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
Each non-quiet test check fetches and increments the test step counter
|
A non-quiet test check fetches and increments the test step counter atomically.
|
||||||
atomically. For each test case execution the planned steps can be specified
|
For each test case execution the planned steps can be specified with the
|
||||||
with the `T_plan()` function.
|
`T_plan()` function.
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
|
|
||||||
@ -318,7 +350,7 @@ execution follows exactly the planned steps.
|
|||||||
Test Case Resource Accounting
|
Test Case Resource Accounting
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
The framework can check if various resources are leaked during a test case
|
The framework can check if various resources have leaked during a test case
|
||||||
execution. The resource checkers are specified by the test run configuration.
|
execution. The resource checkers are specified by the test run configuration.
|
||||||
On RTEMS, checks for the following resources are available
|
On RTEMS, checks for the following resources are available
|
||||||
|
|
||||||
@ -452,13 +484,14 @@ test case execution. You can provide an optional destroy function to
|
|||||||
Test Case Destructors
|
Test Case Destructors
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
You can add test case destructors with `T_add_destructor()`. They are called
|
You can add test case destructors with `T_add_destructor()`. The destructors
|
||||||
automatically at the test case end before the resource accounting takes place.
|
are called automatically at the test case end before the resource accounting
|
||||||
Optionally, a registered destructor can be removed before the test case end
|
takes place. Optionally, a registered destructor can be removed before the
|
||||||
with `T_remove_destructor()`. The `T_destructor` structure of a destructor
|
test case end with `T_remove_destructor()`. The `T_destructor` structure of a
|
||||||
must exist after the return from the test case body. Do not use stack memory
|
destructor must exist after the return from the test case body. It is
|
||||||
or dynamic memory obtained via `T_malloc()`, `T_calloc()` or `T_zalloc()` for
|
recommended to use statically allocated memory. Do not use stack memory or
|
||||||
the `T_destructor` structure.
|
dynamic memory obtained via `T_malloc()`, `T_calloc()` or `T_zalloc()` for the
|
||||||
|
`T_destructor` structure.
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
|
|
||||||
@ -497,16 +530,31 @@ the `T_destructor` structure.
|
|||||||
Test Checks
|
Test Checks
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
A `test check` determines if the actual value presented to the test check meets
|
A `test check` determines if the actual value presented to the test check has
|
||||||
its expectation. The actual value should represent the outcome of a test
|
the expected properties. The actual value should represent the outcome of a
|
||||||
action. If the actual value is all right, then the test check passes,
|
test action. If a test action produces the expected outcome as determined by
|
||||||
otherwise the test check fails. A failed test check does not stop the test
|
the corresponding test check, then this test check passes, otherwise this test
|
||||||
case execution immediately unless the `T_assert_*()` test variant is used.
|
check fails. A failed test check does not stop the test case execution
|
||||||
Each test check increments the test step counter unless the `T_quiet_*()` test
|
immediately unless the `T_assert_*()` test variant is used. Each test check
|
||||||
variant is used. The test step counter is initialized to zero before the test
|
increments the test step counter unless the `T_quiet_*()` test variant is used.
|
||||||
case begins to execute. The `T_step_*(step, ...)` test check variants verify
|
The test step counter is initialized to zero before the test case begins to
|
||||||
that the test step counter is equal to the planned test step value, otherwise
|
execute. The `T_step_*(step, ...)` test check variants verify that the test
|
||||||
the test check fails.
|
step counter is equal to the planned test step value, otherwise the test check
|
||||||
|
fails.
|
||||||
|
|
||||||
|
Test Check Variant Conventions
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The `T_quiet_*()` test check variants do not increment the test step counter
|
||||||
|
and only print a message if the test check fails. This is helpful in case a
|
||||||
|
test check appears in a tight loop.
|
||||||
|
|
||||||
|
The `T_step_*(step, ...)` test check variants check in addition that the test
|
||||||
|
step counter is equal to the specified test step value, otherwise the test
|
||||||
|
check fails.
|
||||||
|
|
||||||
|
The `T_assert_*()` and `T_step_assert_*(step, ...)` test check variants stop
|
||||||
|
the current test case execution if the test check fails.
|
||||||
|
|
||||||
Test Check Parameter Conventions
|
Test Check Parameter Conventions
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -557,21 +605,10 @@ lt
|
|||||||
If the actual value satisfies the test check condition, then the test check
|
If the actual value satisfies the test check condition, then the test check
|
||||||
passes, otherwise it fails.
|
passes, otherwise it fails.
|
||||||
|
|
||||||
Test Check Variant Conventions
|
Test Check Type Conventions
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The `T_quiet_*()` test check variants do not increment the test step counter
|
The following names for test check types are used:
|
||||||
and only print a message if the test check fails. This is helpful in case a
|
|
||||||
test check appears in a tight loop.
|
|
||||||
|
|
||||||
The `T_step_*(step, ...)` test check variants check in addition that the test
|
|
||||||
step counter is equal to the specified test step value, otherwise the test
|
|
||||||
check fails.
|
|
||||||
|
|
||||||
The `T_assert_*()` and `T_step_assert_*(step, ...)` test check variants stop
|
|
||||||
the current test case execution if the test check fails.
|
|
||||||
|
|
||||||
The following names for test check type variants are used:
|
|
||||||
|
|
||||||
ptr
|
ptr
|
||||||
The test value must be a pointer (`void *`).
|
The test value must be a pointer (`void *`).
|
||||||
@ -654,6 +691,57 @@ ssz
|
|||||||
sz
|
sz
|
||||||
The test value must be of type `size_t`.
|
The test value must be of type `size_t`.
|
||||||
|
|
||||||
|
Integers
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
Let `xyz` be the type variant which shall be one of `schar`, `uchar`, `short`,
|
||||||
|
`ushort`, `int`, `uint`, `long`, `ulong`, `ll`, `ull`, `i8`, `u8`, `i16`,
|
||||||
|
`u16`, `i32`, `u32`, `i64`, `u64`, `iptr`, `uptr`, `ssz`, and `sz`.
|
||||||
|
|
||||||
|
Let `I` be the type name which shall be compatible to the type variant.
|
||||||
|
|
||||||
|
The following test checks for integers are available:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
void T_eq_xyz(I a, I e);
|
||||||
|
void T_assert_eq_xyz(I a, I e);
|
||||||
|
void T_quiet_eq_xyz(I a, I e);
|
||||||
|
void T_step_eq_xyz(unsigned int step, I a, I e);
|
||||||
|
void T_step_assert_eq_xyz(unsigned int step, I a, I e);
|
||||||
|
|
||||||
|
void T_ne_xyz(I a, I e);
|
||||||
|
void T_assert_ne_xyz(I a, I e);
|
||||||
|
void T_quiet_ne_xyz(I a, I e);
|
||||||
|
void T_step_ne_xyz(unsigned int step, I a, I e);
|
||||||
|
void T_step_assert_ne_xyz(unsigned int step, I a, I e);
|
||||||
|
|
||||||
|
void T_ge_xyz(I a, I e);
|
||||||
|
void T_assert_ge_xyz(I a, I e);
|
||||||
|
void T_quiet_ge_xyz(I a, I e);
|
||||||
|
void T_step_ge_xyz(unsigned int step, I a, I e);
|
||||||
|
void T_step_assert_ge_xyz(unsigned int step, I a, I e);
|
||||||
|
|
||||||
|
void T_gt_xyz(I a, I e);
|
||||||
|
void T_assert_gt_xyz(I a, I e);
|
||||||
|
void T_quiet_gt_xyz(I a, I e);
|
||||||
|
void T_step_gt_xyz(unsigned int step, I a, I e);
|
||||||
|
void T_step_assert_gt_xyz(unsigned int step, I a, I e);
|
||||||
|
|
||||||
|
void T_le_xyz(I a, I e);
|
||||||
|
void T_assert_le_xyz(I a, I e);
|
||||||
|
void T_quiet_le_xyz(I a, I e);
|
||||||
|
void T_step_le_xyz(unsigned int step, I a, I e);
|
||||||
|
void T_step_assert_le_xyz(unsigned int step, I a, I e);
|
||||||
|
|
||||||
|
void T_lt_xyz(I a, I e);
|
||||||
|
void T_assert_lt_xyz(I a, I e);
|
||||||
|
void T_quiet_lt_xyz(I a, I e);
|
||||||
|
void T_step_lt_xyz(unsigned int step, I a, I e);
|
||||||
|
void T_step_assert_lt_xyz(unsigned int step, I a, I e);
|
||||||
|
|
||||||
|
An automatically generated message is printed in case the test check fails.
|
||||||
|
|
||||||
Boolean Expressions
|
Boolean Expressions
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@ -838,57 +926,6 @@ The following test checks for characters (`char`) are available:
|
|||||||
|
|
||||||
An automatically generated message is printed in case the test check fails.
|
An automatically generated message is printed in case the test check fails.
|
||||||
|
|
||||||
Integers
|
|
||||||
~~~~~~~~
|
|
||||||
|
|
||||||
The following test checks for integers are available:
|
|
||||||
|
|
||||||
.. code-block:: c
|
|
||||||
|
|
||||||
void T_eq_xyz(I a, I e);
|
|
||||||
void T_assert_eq_xyz(I a, I e);
|
|
||||||
void T_quiet_eq_xyz(I a, I e);
|
|
||||||
void T_step_eq_xyz(unsigned int step, I a, I e);
|
|
||||||
void T_step_assert_eq_xyz(unsigned int step, I a, I e);
|
|
||||||
|
|
||||||
void T_ne_xyz(I a, I e);
|
|
||||||
void T_assert_ne_xyz(I a, I e);
|
|
||||||
void T_quiet_ne_xyz(I a, I e);
|
|
||||||
void T_step_ne_xyz(unsigned int step, I a, I e);
|
|
||||||
void T_step_assert_ne_xyz(unsigned int step, I a, I e);
|
|
||||||
|
|
||||||
void T_ge_xyz(I a, I e);
|
|
||||||
void T_assert_ge_xyz(I a, I e);
|
|
||||||
void T_quiet_ge_xyz(I a, I e);
|
|
||||||
void T_step_ge_xyz(unsigned int step, I a, I e);
|
|
||||||
void T_step_assert_ge_xyz(unsigned int step, I a, I e);
|
|
||||||
|
|
||||||
void T_gt_xyz(I a, I e);
|
|
||||||
void T_assert_gt_xyz(I a, I e);
|
|
||||||
void T_quiet_gt_xyz(I a, I e);
|
|
||||||
void T_step_gt_xyz(unsigned int step, I a, I e);
|
|
||||||
void T_step_assert_gt_xyz(unsigned int step, I a, I e);
|
|
||||||
|
|
||||||
void T_le_xyz(I a, I e);
|
|
||||||
void T_assert_le_xyz(I a, I e);
|
|
||||||
void T_quiet_le_xyz(I a, I e);
|
|
||||||
void T_step_le_xyz(unsigned int step, I a, I e);
|
|
||||||
void T_step_assert_le_xyz(unsigned int step, I a, I e);
|
|
||||||
|
|
||||||
void T_lt_xyz(I a, I e);
|
|
||||||
void T_assert_lt_xyz(I a, I e);
|
|
||||||
void T_quiet_lt_xyz(I a, I e);
|
|
||||||
void T_step_lt_xyz(unsigned int step, I a, I e);
|
|
||||||
void T_step_assert_lt_xyz(unsigned int step, I a, I e);
|
|
||||||
|
|
||||||
The type variant `xyz` must be `schar`, `uchar`, `short`, `ushort`, `int`,
|
|
||||||
`uint`, `long`, `ulong`, `ll`, `ull`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`,
|
|
||||||
`i64`, `u64`, `iptr`, `uptr`, `ssz`, or `sz`.
|
|
||||||
|
|
||||||
The type name `I` must be compatible to the type variant.
|
|
||||||
|
|
||||||
An automatically generated message is printed in case the test check fails.
|
|
||||||
|
|
||||||
RTEMS Status Codes
|
RTEMS Status Codes
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@ -1028,6 +1065,35 @@ RTEMS, floating-point operations are only supported in special tasks and may be
|
|||||||
forbidden in interrupt context. The formatted output functions provided by the
|
forbidden in interrupt context. The formatted output functions provided by the
|
||||||
test framework work in every context.
|
test framework work in every context.
|
||||||
|
|
||||||
|
Utility
|
||||||
|
-------
|
||||||
|
|
||||||
|
You can stop a test case via the ``T_stop()`` function. This function does not
|
||||||
|
return. You can indicate unreachable code paths with the ``T_unreachable()``
|
||||||
|
function. If this function is called, then the test case stops.
|
||||||
|
|
||||||
|
You can busy wait with the ``T_busy()`` function:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
void T_busy(uint_fast32_t count);
|
||||||
|
|
||||||
|
It performs a busy loop with the specified iteration count. This function is
|
||||||
|
optimized to not perform memory accesses and should have a small jitter. The
|
||||||
|
loop iterations have a processor-specific duration.
|
||||||
|
|
||||||
|
You can get an iteration count for the ``T_busy()`` function which corresponds
|
||||||
|
roughly to one clock tick interval with the ``T_get_one_clock_tick_busy()``
|
||||||
|
function:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
uint_fast32_t T_get_one_clock_tick_busy(void);
|
||||||
|
|
||||||
|
This function requires a clock driver. It must be called from thread context
|
||||||
|
with interrupts enabled. It may return a different value each time it is
|
||||||
|
called.
|
||||||
|
|
||||||
Time Services
|
Time Services
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
@ -1353,6 +1419,150 @@ reported.
|
|||||||
M:E:Empty:D:0.015188063
|
M:E:Empty:D:0.015188063
|
||||||
E:measure_empty:N:1:F:0:D:14.284869
|
E:measure_empty:N:1:F:0:D:14.284869
|
||||||
|
|
||||||
|
Interrupt Tests
|
||||||
|
---------------
|
||||||
|
|
||||||
|
In the operating system implementation you may have two kinds of critical
|
||||||
|
sections. Firstly, there are low-level critical sections protected by
|
||||||
|
interrupts disabled and maybe also some SMP spin lock. Secondly, there are
|
||||||
|
high-level critical sections which are protected by disabled thread
|
||||||
|
dispatching. The high-level critical sections may contain several low-level
|
||||||
|
critical sections. Between these low-level critical sections interrupts may
|
||||||
|
happen which could alter the code path taken in the high-level critical
|
||||||
|
section.
|
||||||
|
|
||||||
|
The test framework provides support to write test cases for high-level critical
|
||||||
|
sections though the `T_interrupt_test()` function:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
T_INTERRUPT_TEST_INITIAL,
|
||||||
|
T_INTERRUPT_TEST_ACTION,
|
||||||
|
T_INTERRUPT_TEST_BLOCKED,
|
||||||
|
T_INTERRUPT_TEST_CONTINUE,
|
||||||
|
T_INTERRUPT_TEST_DONE,
|
||||||
|
T_INTERRUPT_TEST_EARLY,
|
||||||
|
T_INTERRUPT_TEST_INTERRUPT,
|
||||||
|
T_INTERRUPT_TEST_LATE,
|
||||||
|
T_INTERRUPT_TEST_TIMEOUT
|
||||||
|
} T_interrupt_test_state;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void (*prepare)(void *arg);
|
||||||
|
void (*action)(void *arg);
|
||||||
|
T_interrupt_test_state (*interrupt)(void *arg);
|
||||||
|
void (*blocked)(void *arg);
|
||||||
|
uint32_t max_iteration_count;
|
||||||
|
} T_interrupt_test_config;
|
||||||
|
|
||||||
|
T_interrupt_test_state T_interrupt_test(
|
||||||
|
const T_interrupt_test_config *config,
|
||||||
|
void *arg
|
||||||
|
);
|
||||||
|
|
||||||
|
This function returns ``T_INTERRUPT_TEST_DONE`` if the test condition was
|
||||||
|
satisfied within the maximum iteration count, otherwise it returns
|
||||||
|
``T_INTERRUPT_TEST_TIMEOUT``. The interrupt test run uses the specified
|
||||||
|
configuration and passes the specified argument to all configured handlers.
|
||||||
|
The function shall be called from thread context with interrupts enabled.
|
||||||
|
|
||||||
|
.. image:: ../images/eng/interrupt-test.*
|
||||||
|
:scale: 60
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
The interrupt test uses an *adaptive bisection algorithm* to try to hit the
|
||||||
|
code section under test by an interrupt. In each test iteration, it waits for
|
||||||
|
a time point one quarter of the clock tick interval after a clock tick using
|
||||||
|
the monotonic clock. Then it performs a busy wait using ``T_busy()`` with a
|
||||||
|
busy count controlled by the adaptive bisection algorithm. The test maintains
|
||||||
|
a sample set of upper and lower bound busy wait count values. Initially, the
|
||||||
|
lower bound values are zero and the upper bound values are set to a value
|
||||||
|
returned by ``T_get_one_clock_tick_busy()``. The busy wait count for an
|
||||||
|
iteration is set to the middle point between the arithmetic mean of the lower
|
||||||
|
and upper bound sample values. After the action handler returns, the set of
|
||||||
|
lower and upper bound sample values is updated based on the test state. If the
|
||||||
|
test state is ``T_INTERRUPT_TEST_EARLY``, then the oldest upper bound sample
|
||||||
|
value is replaced by the busy wait count used to delay the action and the
|
||||||
|
latest lower bound sample value is slightly decreased. Reducing the lower
|
||||||
|
bound helps to avoid a zero length interval between the upper and lower bounds.
|
||||||
|
If the test state is ``T_INTERRUPT_TEST_LATE``, then the oldest lower bound
|
||||||
|
sample value is replaced by the busy wait count used to delay the action and
|
||||||
|
the latest upper bound sample value is slightly increased. In all other test
|
||||||
|
states the timing values remain as is. Using the arithmetic mean of a sample
|
||||||
|
set dampens the effect of each test iteration and is an heuristic to mitigate
|
||||||
|
the influence of jitters in the action code execution.
|
||||||
|
|
||||||
|
The optional *prepare* handler should prepare the system so that the *action*
|
||||||
|
handler can be called. It is called in a tight loop, so all the time consuming
|
||||||
|
setup should be done before ``T_interrupt_test()`` is called. During the
|
||||||
|
preparation the test state is ``T_INTERRUPT_TEST_INITIAL``. The preparation
|
||||||
|
handler shall not change the test state.
|
||||||
|
|
||||||
|
The *action* handler should call the function which executes the code section
|
||||||
|
under test. The execution path up to the code section under test should have a
|
||||||
|
low jitter. Otherwise, the adaptive bisection algorithm may not find the right
|
||||||
|
spot.
|
||||||
|
|
||||||
|
The *interrupt* handler should check if the test condition is satisfied or a
|
||||||
|
new iteration is necessary. This handler is called in interrupt context. It
|
||||||
|
shall return ``T_INTERRUPT_TEST_DONE`` if the test condition is satisfied and
|
||||||
|
the test run is done. It shall return ``T_INTERRUPT_TEST_EARLY`` if the
|
||||||
|
interrupt happened too early to satisfy the test condition. It shall return
|
||||||
|
``T_INTERRUPT_TEST_LATE`` if the interrupt happened too late to satisfy the
|
||||||
|
test condition. It shall return ``T_INTERRUPT_TEST_CONTINUE`` if the test
|
||||||
|
should continue with the current timing settings. Other states shall not be
|
||||||
|
returned. It is critical to return the early and late states if the test
|
||||||
|
condition was not satisfied, otherwise the adaptive bisection algorithm may not
|
||||||
|
work. The returned state is used to try to change the test state from
|
||||||
|
``T_INTERRUPT_TEST_ACTION`` to the returned state.
|
||||||
|
|
||||||
|
The optional *blocked* handler is invoked if the executing thread blocks during
|
||||||
|
the action processing. It should remove the blocking condition of the thread
|
||||||
|
so that the next iteration can start. It can use
|
||||||
|
``T_interrupt_change_state()`` to change the interrupt test state.
|
||||||
|
|
||||||
|
The *max iteration count* configuration member defines the maximum iteration
|
||||||
|
count of the test loop. If the maximum iteration count is reached before the
|
||||||
|
test condition is satisfied, then ``T_interrupt_test()`` returns
|
||||||
|
``T_INTERRUPT_TEST_TIMEOUT``.
|
||||||
|
|
||||||
|
The *interrupt* and *blocked* handlers may be called in arbitrary test states.
|
||||||
|
|
||||||
|
The *action*, *interrupt*, and *blocked* handlers can use
|
||||||
|
``T_interrupt_test_get_state()`` to get the current test state:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
T_interrupt_test_state T_interrupt_test_get_state(void);
|
||||||
|
|
||||||
|
The *action*, *interrupt*, and *blocked* handlers can use
|
||||||
|
``T_interrupt_test_change_state()`` to try to change the test state from an
|
||||||
|
expected state to a desired state:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
T_interrupt_test_state T_interrupt_test_change_state(
|
||||||
|
T_interrupt_test_state expected_state,
|
||||||
|
T_interrupt_test_state desired_state
|
||||||
|
);
|
||||||
|
|
||||||
|
The function returns the previous state. If it **differs from the expected
|
||||||
|
state**, then the requested state **change to the desired state did not take
|
||||||
|
place**. In an SMP configuration, do not call this function in a tight loop.
|
||||||
|
It could lock up the test run. To busy wait for a state change, use
|
||||||
|
``T_interrupt_test_get_state()``.
|
||||||
|
|
||||||
|
The *action* handler can use ``T_interrupt_test_busy_wait_for_interrupt()`` to
|
||||||
|
busy wait for the interrupt:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
void T_interrupt_test_busy_wait_for_interrupt(void);
|
||||||
|
|
||||||
|
This is useful if the action code does not block to wait for the interrupt. If
|
||||||
|
the action handler just returns the test code immediately prepares the next
|
||||||
|
iteration and may miss an interrupt which happens too late.
|
||||||
|
|
||||||
Test Runner
|
Test Runner
|
||||||
-----------
|
-----------
|
||||||
|
BIN
images/eng/interrupt-test.odg
Normal file
BIN
images/eng/interrupt-test.odg
Normal file
Binary file not shown.
BIN
images/eng/interrupt-test.pdf
Normal file
BIN
images/eng/interrupt-test.pdf
Normal file
Binary file not shown.
BIN
images/eng/interrupt-test.png
Normal file
BIN
images/eng/interrupt-test.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 74 KiB |
Loading…
x
Reference in New Issue
Block a user