.. SPDX-License-Identifier: CC-BY-SA-4.0

.. Copyright (C) 2018, 2019 embedded brains GmbH
.. Copyright (C) 2018, 2019 Sebastian Huber

Software Test Framework
***********************

.. _RTEMSTestFramework:

The RTEMS Test Framework
========================

The `RTEMS Test Framework` helps you to write test suites.  It has the following
features:

* Implemented in standard C11

* Runs on at least FreeBSD, MSYS2, Linux and RTEMS

* Test runner and test case code can be in separate translation units

* Test cases are automatically registered at link-time

* Test cases may have a test fixture

* Test checks for various standard types

* Supports test case planning

* Test case scoped dynamic memory

* Test case destructors

* Test case resource accounting to show that no resources are leaked
  during the test case execution

* Supports early test case exit, e.g. in case a malloc() fails

* Individual test case and overall test suite duration is reported

* Procedures for code runtime measurements in RTEMS

* Easy to parse test report to generate for example human readable test reports

* Low overhead time measurement of short time sequences (using cycle counter
  hardware if a available)

* Configurable time service provider for a monotonic clock

* Low global memory overhead for test cases and test checks

* Supports multi-threaded execution and interrupts in test cases

* A simple (polled) put character function is sufficient to produce the test report

* Only text, global data and a stack pointer must be set up to run a test suite

* No dynamic memory is used by the framework itself

* No memory is aggregated throughout the test case execution

Nomenclature
------------

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
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
status.  If the test action outcome is all right, then the test check passes,
otherwise the test check fails.  The test check failures of a test case are
summed up.  A test case passes, if the failure count of this test case is zero,
otherwise the test case fails.  The test suite passes if all test cases pass,
otherwise it fails.

Test Cases
----------

You can write a test case with the `T_TEST_CASE()` macro followed by a function
body:

.. code-block:: c

   T_TEST_CASE(name)
   {
      /* Your test case code */
   }

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
runner to form a test suite.  The test cases are automatically registered via
static constructors.

.. code-block:: c
    :caption: Test Case Example

    #include <t.h>

    static int add(int a, int b)
    {
        return a + b;
    }

    T_TEST_CASE(a_test_case)
    {
        int actual_value;

        actual_value = add(1, 1);
        T_eq_int(actual_value, 2);
        T_true(false, "a test failure message");
    }

.. code-block:: none
    :caption: Test Case Report

    B:a_test_case
    P:0:8:UI1:test-simple.c:13
    F:1:8:UI1:test-simple.c:14:a test failure message
    E:a_test_case:N:2:F:1:D:0.001657

The `B` line indicates the begin of test case `a_test_case`.  The `P` line
shows that the test check in file `test-simple.c` at line 13 executed by task
`UI1` on processor 0 as the test step 0 passed.  The invocation of `add()` in
line 12 is the test action of test step 0.  The `F` lines shows that the test
check in file `test-simple.c` at line 14 executed by task `UI1` on processor 0
as the test step 1 failed with a message of `"a test failure message"`.  The
`E` line indicates the end of test case `a_test_case` resulting in a total of
two test steps (`N`) and one test failure (`F`).  The test case execution
duration (`D`) was 0.001657 seconds.  For test report details see:
:ref:`Test Reporting <RTEMSTestFrameworkTestReporting>`.

Test Fixture
------------

You can write a test case with a test fixture with the `T_TEST_CASE_FIXTURE()`
macro followed by a function body:

.. code-block:: c

   T_TEST_CASE_FIXTURE(name, fixture)
   {
      /* Your test case code */
   }

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
initialized read-only object of type `T_fixture`.  The test 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
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
`T_set_fixture_context()` function.  This can be used for example to
dynamically allocate a test environment in the setup method.

.. code-block:: c
    :caption: Test Fixture Example

    #include <t.h>

    static int initial_value = 3;

    static int counter;

    static void
    setup(void *ctx)
    {
        int *c;

        T_log(T_QUIET, "setup begin");
        T_eq_ptr(ctx, &initial_value);
        T_eq_ptr(ctx, T_fixture_context());
        c = ctx;
        counter = *c;
        T_set_fixture_context(&counter);
        T_eq_ptr(&counter, T_fixture_context());
        T_log(T_QUIET, "setup end");
    }

    static void
    stop(void *ctx)
    {
        int *c;

        T_log(T_QUIET, "stop begin");
        T_eq_ptr(ctx, &counter);
        c = ctx;
        ++(*c);
        T_log(T_QUIET, "stop end");
    }

    static void
    teardown(void *ctx)
    {
        int *c;

        T_log(T_QUIET, "teardown begin");
        T_eq_ptr(ctx, &counter);
        c = ctx;
        T_eq_int(*c, 4);
        T_log(T_QUIET, "teardown end");
    }

    static const T_fixture fixture = {
        .setup = setup,
        .stop = stop,
        .teardown = teardown,
        .initial_context = &initial_value
    };

    T_TEST_CASE_FIXTURE(fixture, &fixture)
    {
        T_assert_true(true, "all right");
        T_assert_true(false, "test fails and we stop the test case");
        T_log(T_QUIET, "not reached");
    }

.. code-block:: none
    :caption: Test Fixture Report

    B:fixture
    L:setup begin
    P:0:0:UI1:test-fixture.c:13
    P:1:0:UI1:test-fixture.c:14
    P:2:0:UI1:test-fixture.c:18
    L:setup end
    P:3:0:UI1:test-fixture.c:55
    F:4:0:UI1:test-fixture.c:56:test fails and we stop the test case
    L:stop begin
    P:5:0:UI1:test-fixture.c:28
    L:stop end
    L:teardown begin
    P:6:0:UI1:test-fixture.c:40
    P:7:0:UI1:test-fixture.c:42
    L:teardown end
    E:fixture:N:8:F:1

Test Case Planning
------------------

Each non-quiet test check fetches and increments the test step counter
atomically.  For each test case execution the planned steps can be specified
with the `T_plan()` function.

.. code-block:: c

    void T_plan(unsigned int planned_steps);

This function must be invoked at most once in each test case execution.  If the
planned test steps are set with this function, then the final test steps after
the test case execution must be equal to the planned steps, otherwise the test
case fails.

Use the `T_step_*(step, ...)` test check variants to ensure that the test case
execution follows exactly the planned steps.

.. code-block:: c
    :caption: Test Planning Example

    #include <t.h>

    T_TEST_CASE(wrong_step)
    {
        T_plan(2);
        T_step_true(0, true, "all right");
        T_step_true(2, true, "wrong step");
    }

    T_TEST_CASE(plan_ok)
    {
        T_plan(1);
        T_step_true(0, true, "all right");
    }

    T_TEST_CASE(plan_failed)
    {
        T_plan(2);
        T_step_true(0, true, "not enough steps");
        T_quiet_true(true, "quiet test do not count");
    }

    T_TEST_CASE(double_plan)
    {
        T_plan(99);
        T_plan(2);
    }

    T_TEST_CASE(steps)
    {
        T_step(0, "a");
        T_plan(3);
        T_step(1, "b");
        T_step(2, "c");
    }

.. code-block:: none
    :caption: Test Planning Report

    B:wrong_step
    P:0:0:UI1:test-plan.c:6
    F:1:0:UI1:test-plan.c:7:planned step (2)
    E:wrong_step:N:2:F:1
    B:plan_ok
    P:0:0:UI1:test-plan.c:13
    E:plan_ok:N:1:F:0
    B:plan_failed
    P:0:0:UI1:test-plan.c:19
    F:*:0:UI1:*:*:actual steps (1), planned steps (2)
    E:plan_failed:N:1:F:1
    B:double_plan
    F:*:0:UI1:*:*:planned steps (99) already set
    E:double_plan:N:0:F:1
    B:steps
    P:0:0:UI1:test-plan.c:31
    P:1:0:UI1:test-plan.c:33
    P:2:0:UI1:test-plan.c:34
    E:steps:N:3:F:0

Test Case Resource Accounting
-----------------------------

The framework can check if various resources are leaked during a test case
execution.  The resource checkers are specified by the test run configuration.
On RTEMS, checks for the following resources are available

* workspace and heap memory,
* file descriptors,
* POSIX keys and key value pairs,
* RTEMS barriers,
* RTEMS user extensions,
* RTEMS message queues,
* RTEMS partitions,
* RTEMS periods,
* RTEMS regions,
* RTEMS semaphores,
* RTEMS tasks, and
* RTEMS timers.

.. code-block:: c
    :caption: Resource Accounting Example

    #include <t.h>

    #include <stdlib.h>

    #include <rtems.h>

    T_TEST_CASE(missing_sema_delete)
    {
        rtems_status_code sc;
        rtems_id id;

        sc = rtems_semaphore_create(rtems_build_name('S', 'E', 'M', 'A'), 0,
            RTEMS_COUNTING_SEMAPHORE, 0, &id);
        T_rsc_success(sc);
    }

    T_TEST_CASE(missing_free)
    {
        void *p;

        p = malloc(1);
        T_not_null(p);
    }

.. code-block:: none
    :caption: Resource Accounting Report

    B:missing_sema_delete
    P:0:0:UI1:test-leak.c:14
    F:*:0:UI1:*:*:RTEMS semaphore leak (1)
    E:missing_sema_delete:N:1:F:1:D:0.004013
    B:missing_free
    P:0:0:UI1:test-leak.c:22
    F:*:0:UI1:*:*:memory leak in workspace or heap
    E:missing_free:N:1:F:1:D:0.003944

Test Case Scoped Dynamic Memory
-------------------------------

You can allocate dynamic memory which is automatically freed after the current
test case execution.  You can provide an optional destroy function to
`T_zalloc()` which is called right before the memory is freed.  The
`T_zalloc()` function initializes the memory to zero.

.. code-block:: c

   void *T_malloc(size_t size);

   void *T_calloc(size_t nelem, size_t elsize);

   void *T_zalloc(size_t size, void (*destroy)(void *));

   void T_free(void *ptr);

.. code-block:: c
    :caption: Test Case Scoped Dynamic Memory Example

    #include <t.h>

    T_TEST_CASE(malloc_free)
    {
        void *p;

        p = T_malloc(1);
        T_assert_not_null(p);
        T_free(p);
    }

    T_TEST_CASE(malloc_auto)
    {
        void *p;

        p = T_malloc(1);
        T_assert_not_null(p);
    }

    static void
    destroy(void *p)
    {
        int *i;

        i = p;
        T_step_eq_int(2, *i, 1);
    }

    T_TEST_CASE(zalloc_auto)
    {
        int *i;

        T_plan(3);
        i = T_zalloc(sizeof(*i), destroy);
        T_step_assert_not_null(0, i);
        T_step_eq_int(1, *i, 0);
        *i = 1;
    }

.. code-block:: none
    :caption: Test Case Scoped Dynamic Memory Report

    B:malloc_free
    P:0:0:UI1:test-malloc.c:8
    E:malloc_free:N:1:F:0:D:0.005200
    B:malloc_auto
    P:0:0:UI1:test-malloc.c:17
    E:malloc_auto:N:1:F:0:D:0.004790
    B:zalloc_auto
    P:0:0:UI1:test-malloc.c:35
    P:1:0:UI1:test-malloc.c:36
    P:2:0:UI1:test-malloc.c:26
    E:zalloc_auto:N:3:F:0:D:0.006583

Test Case Destructors
---------------------

You can add test case destructors with `T_add_destructor()`.  They are called
automatically at the test case end before the resource accounting takes place.
Optionally, a registered destructor can be removed before the test case end
with `T_remove_destructor()`.  The `T_destructor` structure of a destructor
must exist after the return from the test case body.  Do not use stack memory
or dynamic memory obtained via `T_malloc()`, `T_calloc()` or `T_zalloc()` for
the `T_destructor` structure.

.. code-block:: c

    void T_add_destructor(T_destructor *destructor,
       void (*destroy)(T_destructor *));

    void T_remove_destructor(T_destructor *destructor);

.. code-block:: c
    :caption: Test Case Destructor Example

    #include <t.h>

    static void
    destroy(T_destructor *dtor)
    {
        (void)dtor;
        T_step(0, "destroy");
    }

    T_TEST_CASE(destructor)
    {
        static T_destructor dtor;

        T_plan(1);
        T_add_destructor(&dtor, destroy);
    }

.. code-block:: none
    :caption: Test Case Destructor Report

    B:destructor
    P:0:0:UI1:test-destructor.c:7
    E:destructor:N:1:F:0:D:0.003714

Test Checks
-----------

A `test check` determines if the actual value presented to the test check meets
its expectation.  The actual value should represent the outcome of a test
action.  If the actual value is all right, then the test check passes,
otherwise the test check fails.  A failed test check does not stop the test
case execution immediately unless the `T_assert_*()` test variant is used.
Each test check increments the test step counter unless the `T_quiet_*()` test
variant is used.  The test step counter is initialized to zero before the test
case begins to execute.  The `T_step_*(step, ...)` test check variants verify
that the test step counter is equal to the planned test step value, otherwise
the test check fails.

Test Check Parameter Conventions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The following names for test check parameters are used throughout the test
checks:

step
    The planned test step for this test check.

a
    The actual value to check against an expected value.  It is usually the
    first parameter in all test checks, except in the `T_step_*(step, ...)`
    test check variants, here it is the second parameter.

e
    The expected value of a test check.  This parameter is optional.  Some test
    checks have an implicit expected value.  If present, then this parameter is
    directly after the actual value parameter of the test check.

fmt
    A printf()-like format string.  Floating-point and exotic formats may be
    not supported.

Test Check Condition Conventions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The following names for test check conditions are used:

eq
    The actual value must equal the expected value.

ne
    The actual value must not equal the value of the second parameter.

ge
    The actual value must be greater than or equal to the expected value.

gt
    The actual value must be greater than the expected value.

le
    The actual value must be less than or equal to the expected value.

lt
    The actual value must be less than the expected value.

If the actual value satisfies the test check condition, then the test check
passes, otherwise it 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.

The following names for test check type variants are used:

ptr
    The test value must be a pointer (`void *`).

mem
    The test value must be a memory area with a specified length.

str
    The test value must be a null byte terminated string.

nstr
    The length of the test value string is limited to a specified maximum.

char
    The test value must be a character (`char`).

schar
    The test value must be a signed character (`signed char`).

uchar
    The test value must be an unsigned character (`unsigned char`).

short
    The test value must be a short integer (`short`).

ushort
    The test value must be an unsigned short integer (`unsigned short`).

int
    The test value must be an integer (`int`).

uint
    The test value must be an unsigned integer (`unsigned int`).

long
    The test value must be a long integer (`long`).

ulong
    The test value must be an unsigned long integer (`unsigned long`).

ll
    The test value must be a long long integer (`long long`).

ull
    The test value must be an unsigned long long integer (`unsigned long long`).

i8
    The test value must be a signed 8-bit integer (`int8_t`).

u8
    The test value must be an unsigned 8-bit integer (`uint8_t`).

i16
    The test value must be a signed 16-bit integer (`int16_t`).

u16
    The test value must be an unsigned 16-bit integer (`uint16_t`).

i32
    The test value must be a signed 32-bit integer (`int32_t`).

u32
    The test value must be an unsigned 32-bit integer (`uint32_t`).

i64
    The test value must be a signed 64-bit integer (`int64_t`).

u64
    The test value must be an unsigned 64-bit integer (`uint64_t`).

iptr
    The test value must be of type `intptr_t`.

uptr
    The test value must be of type `uintptr_t`.

ssz
    The test value must be of type `ssize_t`.

sz
    The test value must be of type `size_t`.

Boolean Expressions
~~~~~~~~~~~~~~~~~~~

The following test checks for boolean expressions are available:

.. code-block:: c

    void T_true(bool a, const char *fmt, ...);
    void T_assert_true(bool a, const char *fmt, ...);
    void T_quiet_true(bool a, const char *fmt, ...);
    void T_step_true(unsigned int step, bool a, const char *fmt, ...);
    void T_step_assert_true(unsigned int step, bool a, const char *fmt, ...);

    void T_false(bool a, const char *fmt, ...);
    void T_assert_false(bool a, const char *fmt, ...);
    void T_quiet_true(bool a, const char *fmt, ...);
    void T_step_true(unsigned int step, bool a, const char *fmt, ...);
    void T_step_assert_true(unsigned int step, bool a, const char *fmt, ...);

The message is only printed in case the test check fails.  The format parameter
is mandatory.

.. code-block:: c
    :caption: Boolean Test Checks Example

    #include <t.h>

    T_TEST_CASE(example)
    {
        T_true(true, "test passes, no message output");
        T_true(false, "test fails");
        T_quiet_true(true, "quiet test passes, no output at all");
        T_quiet_true(false, "quiet test fails");
        T_step_true(2, true, "step test passes, no message output");
        T_step_true(3, false, "step test fails");
        T_assert_false(true, "this is a format %s", "string");
    }

.. code-block:: none
    :caption: Boolean Test Checks Report

    B:example
    P:0:0:UI1:test-example.c:5
    F:1:0:UI1:test-example.c:6:test fails
    F:*:0:UI1:test-example.c:8:quiet test fails
    P:2:0:UI1:test-example.c:9
    F:3:0:UI1:test-example.c:10:step test fails
    F:4:0:UI1:test-example.c:11:this is a format string
    E:example:N:5:F:4

Generic Types
~~~~~~~~~~~~~

The following test checks for data types with an equality (`==`) or inequality
(`!=`) operator are available:

.. code-block:: c

    void T_eq(T a, T e, const char *fmt, ...);
    void T_assert_eq(T a, T e, const char *fmt, ...);
    void T_quiet_eq(T a, T e, const char *fmt, ...);
    void T_step_eq(unsigned int step, T a, T e, const char *fmt, ...);
    void T_step_assert_eq(unsigned int step, T a, T e, const char *fmt, ...);

    void T_ne(T a, T e, const char *fmt, ...);
    void T_assert_ne(T a, T e, const char *fmt, ...);
    void T_quiet_ne(T a, T e, const char *fmt, ...);
    void T_step_ne(unsigned int step, T a, T e, const char *fmt, ...);
    void T_step_assert_ne(unsigned int step, T a, T e, const char *fmt, ...);

The type name `T` specifies an arbitrary type which must support the
corresponding operator.  The message is only printed in case the test check
fails.  The format parameter is mandatory.

Pointers
~~~~~~~~

The following test checks for pointers are available:

.. code-block:: c

    void T_eq_ptr(const void *a, const void *e);
    void T_assert_eq_ptr(const void *a, const void *e);
    void T_quiet_eq_ptr(const void *a, const void *e);
    void T_step_eq_ptr(unsigned int step, const void *a, const void *e);
    void T_step_assert_eq_ptr(unsigned int step, const void *a, const void *e);

    void T_ne_ptr(const void *a, const void *e);
    void T_assert_ne_ptr(const void *a, const void *e);
    void T_quiet_ne_ptr(const void *a, const void *e);
    void T_step_ne_ptr(unsigned int step, const void *a, const void *e);
    void T_step_assert_ne_ptr(unsigned int step, const void *a, const void *e);

    void T_null(const void *a);
    void T_assert_null(const void *a);
    void T_quiet_null(const void *a);
    void T_step_null(unsigned int step, const void *a);
    void T_step_assert_null(unsigned int step, const void *a);

    void T_not_null(const void *a);
    void T_assert_not_null(const void *a);
    void T_quiet_not_null(const void *a);
    void T_step_not_null(unsigned int step, const void *a);
    void T_step_assert_not_null(unsigned int step, const void *a);

An automatically generated message is printed in case the test check fails.

Memory Areas
~~~~~~~~~~~~

The following test checks for memory areas are available:

.. code-block:: c

    void T_eq_mem(const void *a, const void *e, size_t n);
    void T_assert_eq_mem(const void *a, const void *e, size_t n);
    void T_quiet_eq_mem(const void *a, const void *e, size_t n);
    void T_step_eq_mem(unsigned int step, const void *a, const void *e, size_t n);
    void T_step_assert_eq_mem(unsigned int step, const void *a, const void *e, size_t n);

    void T_ne_mem(const void *a, const void *e, size_t n);
    void T_assert_ne_mem(const void *a, const void *e, size_t n);
    void T_quiet_ne_mem(const void *a, const void *e, size_t n);
    void T_step_ne_mem(unsigned int step, const void *a, const void *e, size_t n);
    void T_step_assert_ne_mem(unsigned int step, const void *a, const void *e, size_t n);

The `memcmp()` function is used to compare the memory areas.  An automatically
generated message is printed in case the test check fails.

Strings
~~~~~~~

The following test checks for strings are available:

.. code-block:: c

    void T_eq_str(const char *a, const char *e);
    void T_assert_eq_str(const char *a, const char *e);
    void T_quiet_eq_str(const char *a, const char *e);
    void T_step_eq_str(unsigned int step, const char *a, const char *e);
    void T_step_assert_eq_str(unsigned int step, const char *a, const char *e);

    void T_ne_str(const char *a, const char *e);
    void T_assert_ne_str(const char *a, const char *e);
    void T_quiet_ne_str(const char *a, const char *e);
    void T_step_ne_str(unsigned int step, const char *a, const char *e);
    void T_step_assert_ne_str(unsigned int step, const char *a, const char *e);

    void T_eq_nstr(const char *a, const char *e, size_t n);
    void T_assert_eq_nstr(const char *a, const char *e, size_t n);
    void T_quiet_eq_nstr(const char *a, const char *e, size_t n);
    void T_step_eq_nstr(unsigned int step, const char *a, const char *e, size_t n);
    void T_step_assert_eq_nstr(unsigned int step, const char *a, const char *e, size_t n);

    void T_ne_nstr(const char *a, const char *e, size_t n);
    void T_assert_ne_nstr(const char *a, const char *e, size_t n);
    void T_quiet_ne_nstr(const char *a, const char *e, size_t n);
    void T_step_ne_nstr(unsigned int step, const char *a, const char *e, size_t n);
    void T_step_assert_ne_nstr(unsigned int step, const char *a, const char *e, size_t n);

The `strcmp()` and `strncmp()` functions are used to compare the strings.  An
automatically generated message is printed in case the test check fails.

Characters
~~~~~~~~~~

The following test checks for characters (`char`) are available:

.. code-block:: c

    void T_eq_char(char a, char e);
    void T_assert_eq_char(char a, char e);
    void T_quiet_eq_char(char a, char e);
    void T_step_eq_char(unsigned int step, char a, char e);
    void T_step_assert_eq_char(unsigned int step, char a, char e);

    void T_ne_char(char a, char e);
    void T_assert_ne_char(char a, char e);
    void T_quiet_ne_char(char a, char e);
    void T_step_ne_char(unsigned int step, char a, char e);
    void T_step_assert_ne_char(unsigned int step, char a, char e);

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
~~~~~~~~~~~~~~~~~~

The following test checks for RTEMS status codes are available:

.. code-block:: c

    void T_rsc(rtems_status_code a, rtems_status_code e);
    void T_assert_rsc(rtems_status_code a, rtems_status_code e);
    void T_quiet_rsc(rtems_status_code a, rtems_status_code e);
    void T_step_rsc(unsigned int step, rtems_status_code a, rtems_status_code e);
    void T_step_assert_rsc(unsigned int step, rtems_status_code a, rtems_status_code e);

    void T_rsc_success(rtems_status_code a);
    void T_assert_rsc_success(rtems_status_code a);
    void T_quiet_rsc_success(rtems_status_code a);
    void T_step_rsc_success(unsigned int step, rtems_status_code a);
    void T_step_assert_rsc_success(unsigned int step, rtems_status_code a);

An automatically generated message is printed in case the test check fails.

POSIX Error Numbers
~~~~~~~~~~~~~~~~~~~

The following test checks for POSIX error numbers are available:

.. code-block:: c

    void T_eno(int a, int e);
    void T_assert_eno(int a, int e);
    void T_quiet_eno(int a, int e);
    void T_step_eno(unsigned int step, int a, int e);
    void T_step_assert_eno(unsigned int step, int a, int e);

    void T_eno_success(int a);
    void T_assert_eno_success(int a);
    void T_quiet_eno_success(int a);
    void T_step_eno_success(unsigned int step, int a);
    void T_step_assert_eno_success(unsigned int step, int a);

The actual and expected value must be a POSIX error number, e.g. EINVAL,
ENOMEM, etc.  An automatically generated message is printed in case the test
check fails.

POSIX Status Codes
~~~~~~~~~~~~~~~~~~

The following test checks for POSIX status codes are available:

.. code-block:: c

    void T_psx_error(int a, int eno);
    void T_assert_psx_error(int a, int eno);
    void T_quiet_psx_error(int a, int eno);
    void T_step_psx_error(unsigned int step, int a, int eno);
    void T_step_assert_psx_error(unsigned int step, int a, int eno);

    void T_psx_success(int a);
    void T_assert_psx_success(int a);
    void T_quiet_psx_success(int a);
    void T_step_psx_success(unsigned int step, int a);
    void T_step_assert_psx_success(unsigned int step, int a);

The `eno` value must be a POSIX error number, e.g. EINVAL, ENOMEM, etc.  An
actual value of zero indicates success.  An actual value of minus one indicates
an error.  An automatically generated message is printed in case the test check
fails.

.. code-block:: c
    :caption: POSIX Status Code Example

    #include <t.h>

    #include <sys/stat.h>
    #include <errno.h>

    T_TEST_CASE(stat)
    {
        struct stat st;
        int status;

        errno = 0;
        status = stat("foobar", &st);
        T_psx_error(status, ENOENT);
    }

.. code-block:: none
    :caption: POSIX Status Code Report

    B:stat
    P:0:0:UI1:test-psx.c:13
    E:stat:N:1:F:0

Log Messages and Formatted Output
---------------------------------

You can print log messages with the `T_log()` function:

.. code-block:: c

    void T_log(T_verbosity verbosity, char const *fmt, ...);

A newline is automatically added to terminate the log message line.

.. code-block:: c
    :caption: Log Message Example

    #include <t.h>

    T_TEST_CASE(log)
    {
        T_log(T_NORMAL, "a log message %i, %i, %i", 1, 2, 3);
        T_set_verbosity(T_QUIET);
        T_log(T_NORMAL, "not verbose enough");
    }

.. code-block:: none
    :caption: Log Message Report

    B:log
    L:a log message 1, 2, 3
    E:log:N:0:F:0

You can use the following functions to print formatted output:

.. code-block:: c

    int T_printf(char const *, ...);

    int T_vprintf(char const *, va_list);

    int T_snprintf(char *, size_t, const char *, ...);

In contrast to the corresponding standard C library functions, floating-point
and exotic formats may not be supported.  On some architectures supported by
RTEMS, floating-point operations are only supported in special tasks and may be
forbidden in interrupt context.  The formatted output functions provided by the
test framework work in every context.

Time Services
-------------

The test framework provides two unsigned integer types for time values.  The
`T_ticks` unsigned integer type is used by the `T_tick()` function which
measures time using the highest frequency counter available on the platform.
It should only be used to measure small time intervals.  The `T_time` unsigned
integer type is used by the `T_now()` function which returns the current
monotonic clock value of the platform, e.g. `CLOCK_MONOTONIC`.

.. code-block:: c

   T_ticks T_tick(void);

   T_time T_now(void);

The reference time point for these two clocks is unspecified.  You can obtain
the test case begin time with the `T_case_begin_time()` function.

.. code-block:: c

   T_time T_case_begin_time(void);

You can convert time into ticks with the `T_time_to_ticks()` function and vice
versa with the `T_ticks_to_time()` function.

.. code-block:: c

    T_time T_ticks_to_time(T_ticks ticks);

    T_ticks T_time_to_ticks(T_time time);

You can convert seconds and nanoseconds values into a combined time value with
the `T_seconds_and_nanoseconds_to_time()` function.  You can convert a time
value into separate seconds and nanoseconds values with the
`T_time_to_seconds_and_nanoseconds()` function.

.. code-block:: c

    T_time T_seconds_and_nanoseconds_to_time(uint32_t s, uint32_t ns);

    void T_time_to_seconds_and_nanoseconds(T_time time, uint32_t *s, uint32_t *ns);

You can convert a time value into a string represention.  The time unit of the
string representation is seconds.  The precision of the string represention may
be nanoseconds, microseconds, milliseconds, or seconds.  You have to provide a
buffer for the string (`T_time_string`).

.. code-block:: c

    const char *T_time_to_string_ns(T_time time, T_time_string buffer);

    const char *T_time_to_string_us(T_time time, T_time_string buffer);

    const char *T_time_to_string_ms(T_time time, T_time_string buffer);

    const char *T_time_to_string_s(T_time time, T_time_string buffer);

.. code-block:: c
    :caption: Time String Example

    #include <t.h>

    T_TEST_CASE(time_to_string)
    {
        T_time_string ts;
        T_time t;
        uint32_t s;
        uint32_t ns;

        t = T_seconds_and_nanoseconds_to_time(0, 123456789);
        T_eq_str(T_time_to_string_ns(t, ts), "0.123456789");
        T_eq_str(T_time_to_string_us(t, ts), "0.123456");
        T_eq_str(T_time_to_string_ms(t, ts), "0.123");
        T_eq_str(T_time_to_string_s(t, ts), "0");

        T_time_to_seconds_and_nanoseconds(t, &s, &ns);
        T_eq_u32(s, 0);
        T_eq_u32(ns, 123456789);
    }

.. code-block:: none
    :caption: Time String Report

    B:time_to_string
    P:0:0:UI1:test-time.c:11
    P:1:0:UI1:test-time.c:12
    P:2:0:UI1:test-time.c:13
    P:3:0:UI1:test-time.c:14
    P:4:0:UI1:test-time.c:17
    P:5:0:UI1:test-time.c:18
    E:time_to_string:N:6:F:0:D:0.005250

You can convert a tick value into a string represention.  The time unit of the
string representation is seconds.  The precision of the string represention may
be nanoseconds, microseconds, milliseconds, or seconds.  You have to provide a
buffer for the string (`T_time_string`).

.. code-block:: c

    const char *T_ticks_to_string_ns(T_ticks ticks, T_time_string buffer);

    const char *T_ticks_to_string_us(T_ticks ticks, T_time_string buffer);

    const char *T_ticks_to_string_ms(T_ticks ticks, T_time_string buffer);

    const char *T_ticks_to_string_s(T_ticks ticks, T_time_string buffer);

Code Runtime Measurements
-------------------------

You can measure the runtime of code fragments in several execution environment
variants with the `T_measure_runtime()` function.  This function needs a
context which must be created with the `T_measure_runtime_create()` function.
The context is automatically destroyed after the test case execution.

.. code-block:: c

    typedef struct {
        size_t sample_count;
    } T_measure_runtime_config;

    typedef struct {
        const char *name;
        int flags;
        void (*setup)(void *arg);
        void (*body)(void *arg);
        bool (*teardown)(void *arg, T_ticks *delta, uint32_t tic, uint32_t toc,
            unsigned int retry);
        void *arg;
    } T_measure_runtime_request;

    T_measure_runtime_context *T_measure_runtime_create(
        const T_measure_runtime_config *config);

    void T_measure_runtime(T_measure_runtime_context *ctx,
        const T_measure_runtime_request *request);

The runtime measurement is performed for the `body` request handler of the
measurement request (`T_measure_runtime_request`).  The optional `setup`
request handler is called before each invocation of the `body` request handler.
The optional `teardown` request handler is called after each invocation of the
`body` request handler.  It has several parameters and a return status.  If it
returns true, then this measurement sample value is recorded, otherwise the
measurement is retried.  The `delta` parameter is the current measurement
sample value.  It can be altered by the `teardown` request handler.  The `tic`
and `toc` parameters are the system tick values before and after the request
body invocation.  The `retry` parameter is the current retry counter.  The
runtime of the operational `setup` and `teardown` request handlers is not
measured.

You can control some aspects of the measurement through the request flags (use
zero for the default):

T_MEASURE_RUNTIME_ALLOW_CLOCK_ISR
    Allow clock interrupts during the measurement.  By default, measurements
    during which a clock interrupt happened are discarded unless it happens two
    times in a row.

T_MEASURE_RUNTIME_REPORT_SAMPLES
    Report all measurement samples.

T_MEASURE_RUNTIME_DISABLE_VALID_CACHE
    Disable the `ValidCache` execution environment variant.

T_MEASURE_RUNTIME_DISABLE_HOT_CACHE
    Disable the `HotCache` execution environment variant.

T_MEASURE_RUNTIME_DISABLE_DIRTY_CACHE
    Disable the `DirtyCache` execution environment variant.

T_MEASURE_RUNTIME_DISABLE_MINOR_LOAD
    Disable the `Load` execution environment variants with a load worker count
    less than the processor count.

T_MEASURE_RUNTIME_DISABLE_MAX_LOAD
    Disable the `Load` execution environment variant with a load worker count
    equal to the processor count.

The execution environment variants (`M:V`) are:

ValidCache
    Before the `body` request handler is invoked a memory area with twice the
    size of the outer-most data cache is completely read.  This fills the data
    cache with valid cache lines which are unrelated to the `body` request
    handler.

    You can disable this variant with the
    `T_MEASURE_RUNTIME_DISABLE_VALID_CACHE` request flag.

HotCache
    Before the `body` request handler is invoked the `body` request handler is
    called without measuring the runtime.  The aim is to load all data used by
    the `body` request handler to the cache.

    You can disable this variant with the
    `T_MEASURE_RUNTIME_DISABLE_HOT_CACHE` request flag.

DirtyCache
    Before the `body` request handler is invoked a memory area with twice the
    size of the outer-most data cache is completely written with new data.
    This should produce a data cache with dirty cache lines which are unrelated
    to the `body` request handler.  In addition, the entire instruction cache
    is invalidated.

    You can disable this variant with the
    `T_MEASURE_RUNTIME_DISABLE_DIRTY_CACHE` request flag.

Load
    This variant tries to get close to worst-case conditions.  The cache is set
    up according to the `DirtyCache` variant.  In addition, other processors
    try to fully load the memory system.  The load is produced through writes
    to a memory area with twice the size of the outer-most data cache.  The
    load variant is performed multiple times with a different set of active
    load worker threads (`M:L`).  The active workers range from one up to the
    processor count.

    You can disable these variants with the
    `T_MEASURE_RUNTIME_DISABLE_MINOR_LOAD` and
    `T_MEASURE_RUNTIME_DISABLE_MAX_LOAD` request flags.

    On SPARC, the `body` request handler is called with a register window
    setting so that window overflow traps will occur in the next level function
    call.

Each execution in an environment variant produces a sample set of `body`
request handler runtime measurements.  The minimum (`M:MI`), first quartile
(`M:Q1`), median (`M:Q2`), third quartile (`M:Q3`), maximum (`M:MX`), median
absolute deviation (`M:MAD`), and the sum of the sample values (`M:D`) is
reported.

.. code-block:: c
    :caption: Code Runtime Measurement Example

    #include <t.h>

    static void
    empty(void *arg)
    {
        (void)arg;
    }

    T_TEST_CASE(measure_empty)
    {
        static const T_measure_runtime_config config = {
            .sample_count = 1024
        };
        T_measure_runtime_context *ctx;
        T_measure_runtime_request req;

        ctx = T_measure_runtime_create(&config);
        T_assert_not_null(ctx);

        memset(&req, 0, sizeof(req));
        req.name = "Empty";
        req.body = empty;
        T_measure_runtime(ctx, &req);
    }

.. code-block:: none
    :caption: Code Runtime Measurement Report

    B:measure_empty
    P:0:0:UI1:test-rtems-measure.c:18
    M:B:Empty
    M:V:ValidCache
    M:N:1024
    M:MI:0.000000000
    M:Q1:0.000000000
    M:Q2:0.000000000
    M:Q3:0.000000000
    M:MX:0.000000009
    M:MAD:0.000000000
    M:D:0.000000485
    M:E:Empty:D:0.208984183
    M:B:Empty
    M:V:HotCache
    M:N:1024
    M:MI:0.000000003
    M:Q1:0.000000003
    M:Q2:0.000000003
    M:Q3:0.000000003
    M:MX:0.000000006
    M:MAD:0.000000000
    M:D:0.000002626
    M:E:Empty:D:0.000017046
    M:B:Empty
    M:V:DirtyCache
    M:N:1024
    M:MI:0.000000007
    M:Q1:0.000000007
    M:Q2:0.000000007
    M:Q3:0.000000008
    M:MX:0.000000559
    M:MAD:0.000000000
    M:D:0.000033244
    M:E:Empty:D:1.887834875
    M:B:Empty
    M:V:Load
    M:L:1
    M:N:1024
    M:MI:0.000000000
    M:Q1:0.000000002
    M:Q2:0.000000002
    M:Q3:0.000000003
    M:MX:0.000000288
    M:MAD:0.000000000
    M:D:0.000002421
    M:E:Empty:D:0.001798809
    [... 22 more load variants ...]
    M:E:Empty:D:0.021252583
    M:B:Empty
    M:V:Load
    M:L:24
    M:N:1024
    M:MI:0.000000001
    M:Q1:0.000000002
    M:Q2:0.000000002
    M:Q3:0.000000003
    M:MX:0.000001183
    M:MAD:0.000000000
    M:D:0.000003406
    M:E:Empty:D:0.015188063
    E:measure_empty:N:1:F:0:D:14.284869


Test Runner
-----------

You can call the `T_main()` function to run all registered test cases.

.. code-block:: c

    int T_main(const T_config *config);

The `T_main()` function returns 0 if all test cases passed, otherwise it
returns 1.  Concurrent execution of the `T_main()` function is undefined
behaviour.

You can ask if you execute within the context of the test runner with the
`T_is_runner()` function:

.. code-block:: c

    bool T_is_runner(void);

It returns `true` if you execute within the context of the test runner (the
context which executes for example `T_main()`).  Otherwise it returns `false`,
for example if you execute in another task, in interrupt context, nobody
executes `T_main()`, or during system initialization on another processor.

On RTEMS, you have to register the test cases with the `T_register()` function
before you call `T_main()`.  This makes it possible to run low level tests, for
example without the operating system directly in `boot_card()` or during device
driver initialization.  On other platforms, the `T_register()` is a no
operation.

.. code-block:: c

    void T_register(void);

You can run test cases also individually.  Use `T_run_initialize()` to
initialize the test runner.  Call `T_run_all()` to run all or `T_run_by_name()`
to run specific registered test cases.  Call `T_case_begin()` to begin a
freestanding test case and call `T_case_end()` to finish it.  Finally,
call `T_run_finalize()`.

.. code-block:: c

    void T_run_initialize(const T_config *config);

    void T_run_all(void);

    void T_run_by_name(const char *name);

    void T_case_begin(const char *name, const T_fixture *fixture);

    void T_case_end(void);

    bool T_run_finalize(void);

The `T_run_finalize()` function returns `true` if all test cases passed,
otherwise it returns `false`.  Concurrent execution of the runner functions
(including `T_main()`) is undefined behaviour.  The test suite configuration
must be persistent throughout the test run.

.. code-block:: c

    typedef enum {
        T_EVENT_RUN_INITIALIZE,
        T_EVENT_CASE_EARLY,
        T_EVENT_CASE_BEGIN,
        T_EVENT_CASE_END,
        T_EVENT_CASE_LATE,
        T_EVENT_RUN_FINALIZE
    } T_event;

    typedef void (*T_action)(T_event, const char *);

    typedef void (*T_putchar)(int, void *);

    typedef struct {
        const char *name;
        char *buf;
        size_t buf_size;
        T_putchar putchar;
        void *putchar_arg;
        T_verbosity verbosity;
        T_time (*now)(void);
        size_t action_count;
        const T_action *actions;
    } T_config;

With the test suite configuration you can specifiy the test suite name, the put
character handler used the output the test report, the initial verbosity, the
monotonic time provider and an optional set of test suite actions.  Only the
test runner calls the put character handler, other tasks or interrupt handlers
write to a buffer which is emptied by the test runner on demand.  You have to
specifiy this buffer in the test configuration.  The test suite actions are
called with the test suite name for test suite run events
(`T_EVENT_RUN_INITIALIZE` and `T_EVENT_RUN_FINALIZE`) and the test case name
for the test case events (`T_EVENT_CASE_EARLY`, `T_EVENT_CASE_BEGIN`,
`T_EVENT_CASE_END` and `T_EVENT_CASE_LATE`).

Test Verbosity
--------------

Three test verbosity levels are defined:

T_QUIET
    Only the test suite begin, system, test case end, and test suite end lines
    are printed.

T_NORMAL
    Prints everything except passed test lines.

T_VERBOSE
    Prints everything.

The test verbosity level can be set within the scope of one test case with the
`T_set_verbosity()` function:

.. code-block:: c

    T_verbosity T_set_verbosity(T_verbosity new_verbosity);

The function returns the previous verbosity.  After the test case, the
configured verbosity is automatically restored.

An example with `T_QUIET` verbosity:

    .. code-block:: none

        A:xyz
        S:Platform:RTEMS
        [...]
        E:a:N:2:F:1
        E:b:N:0:F:1
        E:c:N:1:F:1
        E:d:N:6:F:0
        Z:xyz:C:4:N:9:F:3

The same example with `T_NORMAL` verbosity:

    .. code-block:: none

        A:xyz
        S:Platform:RTEMS
        [...]
        B:a
        F:1:0:UI1:test-verbosity.c:6:test fails
        E:a:N:2:F:1
        B:b
        F:*:0:UI1:test-verbosity.c:12:quiet test fails
        E:b:N:0:F:1
        B:c
        F:0:0:UI1:test-verbosity.c:17:this is a format string
        E:c:N:1:F:1
        B:d
        E:d:N:6:F:0
        Z:xyz:C:4:N:9:F:3

The same example with `T_VERBOSE` verbosity:

    .. code-block:: none

        A:xyz
        S:Platform:RTEMS
        [...]
        B:a
        P:0:0:UI1:test-verbosity.c:5
        F:1:0:UI1:test-verbosity.c:6:test fails
        E:a:N:2:F:1
        B:b
        F:*:0:UI1:test-verbosity.c:12:quiet test fails
        E:b:N:0:F:1
        B:c
        F:0:0:UI1:test-verbosity.c:17:this is a format string
        E:c:N:1:F:1
        B:d
        P:0:0:UI1:test-verbosity.c:22
        P:1:0:UI1:test-verbosity.c:23
        P:2:0:UI1:test-verbosity.c:24
        P:3:0:UI1:test-verbosity.c:25
        P:4:0:UI1:test-verbosity.c:26
        P:5:0:UI1:test-verbosity.c:27
        E:d:N:6:F:0
        Z:xyz:C:4:N:9:F:3

.. _RTEMSTestFrameworkTestReporting:

Test Reporting
--------------

The test reporting is line based which should be easy to parse with a simple
state machine. Each line consists of a set of fields separated by colon
characters (`:`).  The first character of the line determines the line format:

A
    A test suite begin line.  It has the format:

    **A:<TestSuite>**

    A description of the field follows:

    <TestSuite>
        The test suite name.  Must not contain colon characters (`:`).

S
    A test suite system line.  It has the format:

    **S:<Key>:<Value>**

    A description of the fields follows:

    <Key>
        A key string.  Must not contain colon characters (`:`).

    <Value>
        An arbitrary key value string.  May contain colon characters (`:`).

B
    A test case begin line.  It has the format:

    **B:<TestCase>**

    A description of the field follows:

    <TestCase>
        A test case name.  Must not contain colon characters (`:`).

P
    A test pass line.  It has the format:

    **P:<Step>:<Processor>:<Task>:<File>:<Line>**

    A description of the fields follows:

    <Step>
        Each non-quiet test has a unique test step counter value in each test case
        execution.  The test step counter is set to zero before the test case
        executes.  For quiet test checks, there is no associated test step and the
        character `*` instead of an integer is used to indicate this.

    <Processor>
        The processor index of the processor which executed at least one
        instruction of the corresponding test.

    <Task>
        The name of the task which executed the corresponding test if the test
        executed in task context.  The name `ISR` indicates that the test executed
        in interrupt context.  The name `?` indicates that the test executed in an
        arbitrary context with no valid executing task.

    <File>
        The name of the source file which contains the corresponding test.  A
        source file of `*` indicates that no test source file is associated
        with the test, e.g. it was produced by the test framework itself.

    <Line>
        The line of the test statement in the source file which contains the
        corresponding test.  A line number of `*` indicates that no test source
        file is associated with the test, e.g. it was produced by the test
        framework itself.

F
    A test failure line.  It has the format:

    **F:<Step>:<Processor>:<Task>:<File>:<Line>:<Message>**

    A description of the fields follows:

    <Step> <Processor> <Task> <File> <Line>
        See above **P** line.

    <Message>
        An arbitrary message string.  May contain colon characters (`:`).

L
    A log message line.  It has the format:

    **L:<Message>**

    A description of the field follows:

    <Message>
        An arbitrary message string.  May contain colon characters (`:`).

E
    A test case end line.  It has the format:

    **E:<TestCase>:N:<Steps>:F:<Failures>:D:<Duration>**

    A description of the fields follows:

    <TestCase>
        A test case name.  Must not contain colon characters (`:`).

    <Steps>
        The final test step counter of a test case.  Quiet test checks produce
        no test steps.

    <Failures>
        The count of failed test checks of a test case.

    <Duration>
        The test case duration in seconds.

Z
    A test suite end line. It has the format:

    **Z:<TestSuite>:C:<TestCases>:N:<OverallSteps>:F:<OverallFailures>:D:<Duration>**

    A description of the fields follows:

    <TestSuite>
        The test suite name.  Must not contain colon characters (`:`).

    <TestCases>
        The count of test cases in the test suite.

    <OverallSteps>
        The overall count of test steps in the test suite.

    <OverallFailures>
        The overall count of failed test cases in the test suite.

    <Duration>
        The test suite duration in seconds.

Y
    Auxiliary information line.  Issued after the test suite end. It has the format:

    **Y:ReportHash:SHA256:<Hash>**

    A description of the fields follows:

    <Hash>
        The SHA256 hash value of the test suite report from the begin to the
        end of the test suite.

M
    A code runtime measurement line.  It has the formats:

    **M:B:<Name>**

    **M:V:<Variant>**

    **M:L:<Load>**

    **M:N:<SampleCount>**

    **M:S:<Count>:<Value>**

    **M:MI:<Minimum>**

    **M:Q1:<FirstQuartile>**

    **M:Q2:<Median>**

    **M:Q3:<ThirdQuartile>**

    **M:MX:<Maximum>**

    **M:MAD:<MedianAbsoluteDeviation>**

    **M:D:<SumOfSampleValues>**

    **M:E:<Name>:D:<Duration>**

    A description of the fields follows:

    <Name>
        A code runtime measurement name.  Must not contain colon characters
        (`:`).

    <Variant>
        The execution variant which is one of **ValidCache**, **HotCache**,
        **DirtyCache**, or **Load**.

    <Load>
        The active load workers count which ranges from one to the processor
        count.

    <SampleCount>
        The sample count as defined by the runtime measurement configuration.

    <Count>
        The count of samples with the same value.

    <Value>
        A sample value in seconds.

    <Minimum>
        The minimum of the sample set in seconds.

    <FirstQuartile>
        The first quartile of the sample set in seconds.

    <Median>
        The median of the sample set in seconds.

    <ThirdQuartile>
        The third quartile of the sample set in seconds.

    <Maximum>
        The maximum of the sample set in seconds.

    <MedianAbsoluteDeviation>
        The median absolute deviation of the sample set in seconds.

    <SumOfSampleValues>
        The sum of all sample values of the sample set in seconds.

    <Duration>
        The runtime measurement duration in seconds.  It includes time to set
        up the execution environment variant.

.. code-block:: none
    :caption: Example Test Report

    A:xyz
    S:Platform:RTEMS
    S:Compiler:7.4.0 20181206 (RTEMS 5, RSB e0aec65182449a4e22b820e773087636edaf5b32, Newlib 1d35a003f)
    S:Version:5.0.0.820977c5af17c1ca2f79800d64bd87ce70a24c68
    S:BSP:erc32
    S:RTEMS_DEBUG:1
    S:RTEMS_MULTIPROCESSING:0
    S:RTEMS_POSIX_API:1
    S:RTEMS_PROFILING:0
    S:RTEMS_SMP:1
    B:timer
    P:0:0:UI1:test-rtems.c:26
    P:1:0:UI1:test-rtems.c:29
    P:2:0:UI1:test-rtems.c:33
    P:3:0:ISR:test-rtems.c:14
    P:4:0:ISR:test-rtems.c:15
    P:5:0:UI1:test-rtems.c:38
    P:6:0:UI1:test-rtems.c:39
    P:7:0:UI1:test-rtems.c:42
    E:timer:N:8:F:0:D:0.019373
    B:rsc_success
    P:0:0:UI1:test-rtems.c:59
    F:1:0:UI1:test-rtems.c:60:RTEMS_INVALID_NUMBER == RTEMS_SUCCESSFUL
    F:*:0:UI1:test-rtems.c:62:RTEMS_INVALID_NUMBER == RTEMS_SUCCESSFUL
    P:2:0:UI1:test-rtems.c:63
    F:3:0:UI1:test-rtems.c:64:RTEMS_INVALID_NUMBER == RTEMS_SUCCESSFUL
    E:rsc_success:N:4:F:3:D:0.011128
    B:rsc
    P:0:0:UI1:test-rtems.c:48
    F:1:0:UI1:test-rtems.c:49:RTEMS_INVALID_NUMBER == RTEMS_INVALID_ID
    F:*:0:UI1:test-rtems.c:51:RTEMS_INVALID_NUMBER == RTEMS_INVALID_ID
    P:2:0:UI1:test-rtems.c:52
    F:3:0:UI1:test-rtems.c:53:RTEMS_INVALID_NUMBER == RTEMS_INVALID_ID
    E:rsc:N:4:F:3:D:0.011083
    Z:xyz:C:3:N:16:F:6:D:0.047201
    Y:ReportHash:SHA256:e5857c520dd9c9b7c15d4a76d78c21ccc46619c30a869ecd11bbcd1885155e0b

Test Report Validation
----------------------

You can add the `T_report_hash_sha256()` test suite action to the test suite
configuration to generate and report the SHA256 hash value of the test suite
report.  The hash value covers everything reported by the test suite run from
the begin to the end.  This can be used to check that the report generated on
the target is identical to the report received on the report consumer side.
The hash value is reported after the end of test suite line (`Z`) as auxiliary
information in a `Y` line.  Consumers may have to reverse a `\\n` to `\\r\\n`
conversion before the hash is calculated.  Such a conversion could be performed
by a particular put character handler provided by the test suite configuration.

Supported Platforms
-------------------

The framework runs on FreeBSD, MSYS2, Linux and RTEMS.

Test Framework Requirements for RTEMS
=====================================

The requirements on a test framework suitable for RTEMS are:

License Requirements
--------------------

TF.License.Permissive
    The test framework shall have a permissive open source license such as
    BSD-2-Clause.

Portability Requirements
------------------------

TF.Portability
    The test framework shall be portable.

    TF.Portability.RTEMS
        The test framework shall run on RTEMS.

    TF.Portability.POSIX
        The test framework shall be portable to POSIX compatible operating
        systems.  This allows to run test cases of standard C/POSIX/etc. APIs
        on multiple platforms.

        TF.Portability.POSIX.Linux
            The test framework shall run on Linux.

        TF.Portability.POSIX.FreeBSD
            The test framework shall run on FreeBSD.

    TF.Portability.C11
        The test framework shall be written in C11.

    TF.Portability.Static
        Test framework shall not use dynamic memory for basic services.

    TF.Portability.Small
        The test framework shall be small enough to support low-end platforms
        (e.g. 64KiB of RAM/ROM should be sufficient to test the architecture
        port, e.g. no complex stuff such as file systems, etc.).

    TF.Portability.Small.LinkTimeConfiguration
        The test framework shall be configured at link-time.

    TF.Portability.Small.Modular
        The test framework shall be modular so that only necessary parts end up
        in the final executable.

    TF.Portability.Small.Memory
        The test framework shall not aggregate data during test case executions.

Reporting Requirements
----------------------

TF.Reporting
    Test results shall be reported.

    TF.Reporting.Verbosity
        The test report verbosity shall be configurable.  This allows different
        test run scenarios, e.g. regression test runs, full test runs with test
        report verification against the planned test output.

    TF.Reporting.Verification
        It shall be possible to use regular expressions to verify test reports
        line by line.

    TF.Reporting.Compact
        Test output shall be compact to avoid long test runs on platforms with
        a slow output device, e.g. 9600 Baud UART.

    TF.Reporting.PutChar
        A simple output one character function provided by the platform shall be
        sufficient to report the test results.

    TF.Reporting.NonBlocking
        The ouptut functions shall be non-blocking.

    TF.Reporting.Printf
        The test framework shall provide printf()-like output functions.

        TF.Reporting.Printf.WithFP
            There shall be a printf()-like output function with floating point
            support.

        TF.Reporting.Printf.WithoutFP
            There shall be a printf()-like output function without floating
            point support on RTEMS.

    TF.Reporting.Platform
        The test platform shall be reported.

        TF.Reporting.Platform.RTEMS.Git
            The RTEMS source Git commit shall be reported.

        TF.Reporting.Platform.RTEMS.Arch
            The RTEMS architecture name shall be reported.

        TF.Reporting.Platform.RTEMS.BSP
            The RTEMS BSP name shall be reported.

        TF.Reporting.Platform.RTEMS.Tools
            The RTEMS tool chain version shall be reported.

        TF.Reporting.Platform.RTEMS.Config.Debug
            The shall be reported if RTEMS_DEBUG is defined.

        TF.Reporting.Platform.RTEMS.Config.Multiprocessing
            The shall be reported if RTEMS_MULTIPROCESSING is defined.

        TF.Reporting.Platform.RTEMS.Config.POSIX
            The shall be reported if RTEMS_POSIX_API is defined.

        TF.Reporting.Platform.RTEMS.Config.Profiling
            The shall be reported if RTEMS_PROFILING is defined.

        TF.Reporting.Platform.RTEMS.Config.SMP
            The shall be reported if RTEMS_SMP is defined.

    TF.Reporting.TestCase
        The test cases shall be reported.

        TF.Reporting.TestCase.Begin
            The test case begin shall be reported.

        TF.Reporting.TestCase.End
            The test case end shall be reported.

        TF.Reporting.TestCase.Tests
            The count of test checks of the test case shall be reported.

        TF.Reporting.TestCase.Failures
            The count of failed test checks of the test case shall be reported.

        TF.Reporting.TestCase.Timing
            Test case timing shall be reported.

        TF.Reporting.TestCase.Tracing
            Automatic tracing and reporting of thread context switches and
            interrupt service routines shall be optionally performed.

Environment Requirements
------------------------

TF.Environment
    The test framework shall support all environment conditions of the platform.

    TF.Environment.SystemStart
        The test framework shall run during early stages of the system start,
        e.g. valid stack pointer, initialized data and cleared BSS, nothing
        more.

    TF.Environment.BeforeDeviceDrivers
        The test framework shall run before device drivers are initialized.

    TF.Environment.InterruptContext
        The test framework shall support test case code in interrupt context.

Usability Requirements
----------------------

TF.Usability
    The test framework shall be easy to use.

    TF.Usability.TestCase
        It shall be possible to write test cases.

        TF.Usability.TestCase.Independence
            It shall be possible to write test cases in modules independent of
            the test runner.

        TF.Usability.TestCase.AutomaticRegistration
            Test cases shall be registered automatically, e.g. via constructors
            or linker sets.

        TF.Usability.TestCase.Order
            It shall be possible to sort the registered test cases (e.g. random,
            by name) before they are executed.

        TF.Usability.TestCase.Resources
            It shall be possible to use resources with a life time restricted to
            the test case.

            TF.Usability.TestCase.Resources.Memory
                It shall be possible to dynamically allocate memory which is
                automatically freed once the test case completed.

            TF.Usability.TestCase.Resources.File
                It shall be possible to create a file which is automatically
                unlinked once the test case completed.

            TF.Usability.TestCase.Resources.Directory
                It shall be possible to create a directory which is automatically
                removed once the test case completed.

            TF.Usability.TestCase.Resources.FileDescriptor
                It shall be possible to open a file descriptor which is
                automatically closed once the test case completed.

        TF.Usability.TestCase.Fixture
            It shall be possible to use a text fixture for test cases.

            TF.Usability.TestCase.Fixture.SetUp
                It shall be possible to provide a set up handler for each test case.

            TF.Usability.TestCase.Fixture.TearDown
                It shall be possible to provide a tear down handler for each test
                case.

        TF.Usability.TestCase.Context
            The test case context shall be verified a certain points.

            TF.Usability.TestCase.Context.VerifyAtEnd
                After a test case exection it shall be verified that the context
                is equal to the context at the test case begin.  This helps to
                ensure that test cases are independent of each other.

            TF.Usability.TestCase.Context.VerifyThread
                The test framework shall provide a function to ensure that the
                test case code executes in normal thread context.  This helps
                to ensure that operating system service calls return to a sane
                context.

            TF.Usability.TestCase.Context.Configurable
                The context verified in test case shall be configurable at link-time.

            TF.Usability.TestCase.Context.ThreadDispatchDisableLevel
                It shall be possible to verify the thread dispatch disable level.

            TF.Usability.TestCase.Context.ISRNestLevel
                It shall be possible to verify the ISR nest level.

            TF.Usability.TestCase.Context.InterruptLevel
                It shall be possible to verify the interrupt level (interrupts
                enabled/disabled).

            TF.Usability.TestCase.Context.Workspace
                It shall be possible to verify the workspace.

            TF.Usability.TestCase.Context.Heap
                It shall be possible to verify the heap.

            TF.Usability.TestCase.Context.OpenFileDescriptors
                It shall be possible to verify the open file descriptors.

            TF.Usability.TestCase.Context.Classic
                It shall be possible to verify Classic API objects.

                TF.Usability.TestCase.Context.Classic.Barrier
                    It shall be possible to verify Classic API Barrier objects.

                TF.Usability.TestCase.Context.Classic.Extensions
                    It shall be possible to verify Classic API User Extensions
                    objects.

                TF.Usability.TestCase.Context.Classic.MessageQueues
                    It shall be possible to verify Classic API Message Queue
                    objects.

                TF.Usability.TestCase.Context.Classic.Partitions
                    It shall be possible to verify Classic API Partition objects.

                TF.Usability.TestCase.Context.Classic.Periods
                    It shall be possible to verify Classic API Rate Monotonic
                    Period objects.

                TF.Usability.TestCase.Context.Classic.Regions
                    It shall be possible to verify Classic API Region objects.

                TF.Usability.TestCase.Context.Classic.Semaphores
                    It shall be possible to verify Classic API Semaphore
                    objects.

                TF.Usability.TestCase.Context.Classic.Tasks
                    It shall be possible to verify Classic API Task objects.

                TF.Usability.TestCase.Context.Classic.Timers
                    It shall be possible to verify Classic API Timer objects.

            TF.Usability.TestCase.Context.POSIX
                It shall be possible to verify POSIX API objects.

                TF.Usability.TestCase.Context.POSIX.Keys
                    It shall be possible to verify POSIX API Key objects.

                TF.Usability.TestCase.Context.POSIX.KeyValuePairs
                    It shall be possible to verify POSIX API Key Value Pair
                    objects.

                TF.Usability.TestCase.Context.POSIX.MessageQueues
                    It shall be possible to verify POSIX API Message Queue
                    objects.

                TF.Usability.TestCase.Context.POSIX.Semaphores
                    It shall be possible to verify POSIX API Named Semaphores
                    objects.

                TF.Usability.TestCase.Context.POSIX.Shms
                    It shall be possible to verify POSIX API Shared Memory
                    objects.

                TF.Usability.TestCase.Context.POSIX.Threads
                    It shall be possible to verify POSIX API Thread objects.

                TF.Usability.TestCase.Context.POSIX.Timers
                    It shall be possible to verify POSIX API Timer objects.

    TF.Usability.Assert
        There shall be functions to assert test objectives.

        TF.Usability.Assert.Safe
            Test assert functions shall be safe to use, e.g. assert(a == b) vs.
            assert(a = b) vs. assert_eq(a, b).

        TF.Usability.Assert.Continue
            There shall be assert functions which allow the test case to
            continue in case of an assertion failure.

        TF.Usability.Assert.Abort
            There shall be assert functions which abourt the test case in case
            of an assertion failure.

    TF.Usability.EasyToWrite
        It shall be easy to write test code, e.g. avoid long namespace prefix
        rtems_test_*.

    TF.Usability.Threads
        The test framework shall support multi-threading.

    TF.Usability.Pattern
        The test framework shall support test patterns.

        TF.Usability.Pattern.Interrupts
            The test framework shall support test cases which use interrupts,
            e.g. spintrcritical*.

        TF.Usability.Pattern.Parallel
            The test framework shall support test cases which want to run code
            in parallel on SMP machines.

        TF.Usability.Pattern.Timing
            The test framework shall support test cases which want to measure
            the timing of code sections under various platform conditions, e.g.
            dirty cache, empty cache, hot cache, with load from other
            processors, etc..

    TF.Usability.Configuration
        The test framework shall be configurable.

        TF.Usability.Configuration.Time
            The timestamp function shall be configurable, e.g. to allow test
            runs without a clock driver.

Performance Requirements
------------------------

TF.Performance.RTEMS.No64BitDivision
    The test framework shall not use 64-bit divisions on RTEMS.

Off-the-shelf Test Frameworks
=============================

There are several
`off-the-shelf test frameworks for C/C++ <https://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C>`_.
The first obstacle for test frameworks is the license requirement
(`TF.License.Permissive`).

bdd-for-c
---------

In the `bdd-for-c <https://github.com/grassator/bdd-for-c>`_ framework the
complete test suite must be contained in one file and the main function is
generated.  This violates `TF.Usability.TestCase.Independence`.

CBDD
----

The `CBDD <https://github.com/nassersala/cbdd>`_ framework uses the
`C blocks <https://clang.llvm.org/docs/BlockLanguageSpec.html>`_ extension from
clang.  This violates `TF.Portability.C11`.

Google Test
-----------

`Google Test 1.8.1 <https://git.rtems.org/sebh/rtems-gtest.git/>`_
is supported by RTEMS.  Unfortunately, it is written in C++ and is to heavy
weight for low-end platforms.  Otherwise it is a nice framework.

Unity
-----

The `Unity Test API <https://github.com/ThrowTheSwitch/Unity>`_ does not meet
our requirements.  There was a `discussion on the mailing list in 2013
<https://lists.rtems.org/pipermail/devel/2013-September/004499.html>`_.

Standard Test Report Formats
============================

JUnit XML
---------

A common test report format is `JUnit XML <http://llg.cubic.org/docs/junit/>`_.

.. code-block:: xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <testsuites id="xyz" name="abc" tests="225" failures="1262" time="0.001">
      <testsuite id="def" name="ghi" tests="45" failures="17" time="0.001">
        <testcase id="jkl" name="mno" time="0.001">
          <failure message="pqr" type="stu"></failure>
          <system-out>stdout</system-out>
          <system-err>stderr</system-err>
        </testcase>
      </testsuite>
    </testsuites>

The major problem with this format is that you have to output the failure count
of all test suites and the individual test suite before the test case output.
You know the failure count only after a complete test run.  This runs contrary
to requirement `TF.Portability.Small.Memory`.  It is also a bit verbose
(`TF.Reporting.Compact`).

It is easy to convert a full test report generated by :ref:`The RTEMS Test
Framework <RTEMSTestFramework>` to the JUnit XML format.

Test Anything Protocol
----------------------

The
`Test Anything Protocol <http://testanything.org/>`_
(TAP) is easy to consume and produce.

.. code-block:: none

    1..4
    ok 1 - Input file opened
    not ok 2 - First line of the input valid
    ok 3 - Read the rest of the file
    not ok 4 - Summarized correctly # TODO Not written yet

You have to know in advance how many test statements you want to execute in a
test case.  The problem with this format is that there is no standard way to
provide auxiliary data such as test timing or a tracing report.

It is easy to convert a full test report generated by :ref:`The RTEMS Test
Framework <RTEMSTestFramework>` to the TAP format.
