Skip to content
Martine Lenders edited this page Apr 1, 2014 · 18 revisions

This page refers to mechanics in the not yet merged PR #381

Building and running tests

Tests can be build by calling:

cd tests/unittests
make

If there are tests for a module you even can build tests specifically for this module:

make tests-<module>
# e.g.
make tests-core

You then can run the tests by calling

./bin/native/unittests.elf [<tap>]

or flash them to your board as you would flash any RIOT application to the board (see board documentation).

Other output formats

Other output formats using embUnit's textui library are available by setting the environment variable OUTPUT:

  • Compiler: OUTPUT="COMPILER"
  • Text: OUTPUT="TEXT"
  • XML: OUTPUT="XML"

Compile example

OUTPUT="COMPILER" make tests-core
./bin/native/unittests.elf

(only outputs in case of test failures)

Text example

OUTPUT="TEXT" make tests-core
./bin/native/unittests.elf
- core_bitarithm_tests
1) OK tests_core_bitarithm_SETBIT_null_null
2) OK tests_core_bitarithm_SETBIT_null_limit
3) ...
- core_clist_tests
25) ...
- ...

OK (... tests)

XML example

OUTPUT="XML" make tests-core
./bin/native/unittests.elf
<?xml version="1.0" encoding='shift_jis' standalone='yes' ?>
<TestRun>
<core_bitarithm_tests>
<Test id="1">
<Name>tests_core_bitarithm_SETBIT_null_null</Name>
</Test>
<Test id="2">
<Name>tests_core_bitarithm_SETBIT_null_limit</Name>
</Test>
...
</core_bitarithm_tests>
<core_clist_tests>
<Test id="25">
<Name>tests_core_clist_add_one</Name>
</Test>
...
</core_clist_tests>
<Statistics>
<Tests>...</Tests>
</Statistics>
</TestRun>

Writing unit tests

File struture

RIOT uses embUnit for unit testing. All unit tests are organized in tests/unittests and can be build module-wise, if needed. For each module there exists a tests-<modulename>.h file and at least one tests-<modulename>*.c. It is recommended to add a *.c file for every header file that defines functions (or macros) implemented in the module named tests-<modulename>-<headername>.c. If there is only one such header file tests-<modulename>.c should suffice.

Each *.c file should implement a function defined in tests-<modulename>.h, named like

Test *tests_<modulename>_<headername>_tests(void);

/* or respectively */

Test *tests_<modulename>_tests(void);

For each module there should be a target in tests/unittests/Makefile:

tests-<modulename> : CFLAGS += -DTEST_NOT_ALL -DTESTS_<MODULENAME>_ENABLED
tests-<modulename> : SRC := $(wildcard tests-<modulename>*.c) main.c
tests-<modulename> : OBJ := $(SRC:%.c=${BINDIR}${PROJECT}/%.o)
tests-<modulename> : all

This way there can be a build path for every module in main.c

int main(void)
{
    /* ... */

    TESTS_START();

#if !defined(TEST_NOT_ALL) || defined(TEST_<MODULE1>_ENABLED)
    TESTS_RUN(tests_<module1>_<header1>_tests());
    TESTS_RUN(tests_<module1>_<header2>_tests());
    /* ... */
#endif

#if !defined(TEST_NOT_ALL) || defined(TEST_<MODULE2>_ENABLED)
    TESTS_RUN(tests_<module2>_<header1>_tests());
    TESTS_RUN(tests_<module2>_<header2>_tests());
    /* ... */
#endif

    TESTS_END();

    return 0;
}

Testing a module

To write new tests for a module you need to do four things:

  1. Define a test header: add a file tests_<modulename>.h
  2. Implement tests: for each header file, that defines a function or macro implemented or related to the module, add a file tests_<modulename>_<headername>.c or tests_<modulename>.c if there is only one header.
  3. Update main.c: add the build path for the module to the main() function.
  4. Update Makefile: add a build target for the module.

Define a test header.

The test header tests-<module>.h of a module you add to tests/unittests/ should have the following structure

/*
 * Copyright (C) <year> <author>
 *
 * This file is subject to the terms and conditions of the GNU Lesser General
 * Public License. See the file LICENSE in the top level directory for more
 * details.
 */

/**
 * @addtogroup  unittests
 * @{
 *
 * @file        tests-<module>.h
 * @brief       Unittests for the ``module`` module
 *
 * @author      <author>
 */
#ifndef __TESTS_<MODULE>_H_
#define __TESTS_<MODULE>_H_
#include "embUnit/embUnit.h"

/**
 * @brief   Generates tests for <header1>.h
 *
 * @return  embUnit tests if successful, NULL if not.
 */
Test *tests_<module>_<header1>_tests(void);

/**
 * @brief   Generates tests for <header2>.h
 *
 * @return  embUnit tests if successful, NULL if not.
 */
Test *tests_<module>_<header2>_tests(void);

/* ... */

#endif /* __TESTS_<MODULE>_H_ */
/** @} */

Implement tests

Every tests-<module>*.c file you add to tests/unittests/ should have the following structure:

/*
 * Copyright (C) <year> <author>
 *
 * This file is subject to the terms and conditions of the GNU Lesser General
 * Public License. See the file LICENSE in the top level directory for more
 * details.
 */

/* clib includes */

#include "embUnit/embUnit.h"

#include "<header>.h"

#include "tests-<module>.h"

/* your macros */

/* your global variables */

static void tests_<module>_<header>_set_up(void)
{
    /* omit if not needed */
}

static void tests_<module>_<header>_tear_down(void)
{
    /* omit if not needed */
}

static void tests_<module>_<header>_<function1>_<what1>(void) {
    /* ... */
    
    TEST_ASSERT(/* ... */);
}

static void tests_<module>_<header>_<function1>_<what2>(void) {
    /* ... */
    
    TEST_ASSERT(/* ... */);
}

/* ... */

static void tests_<module>_<header>_<function2>_<what1>(void) {
    /* ... */
    
    TEST_ASSERT(/* ... */);
}

static void tests_<module>_<header>_<function2>_<what2>(void) {
    /* ... */
    
    TEST_ASSERT(/* ... */);
}

/* ... */

Test *tests_<module>_<header>_tests(void)
{
    EMB_UNIT_TESTFIXTURES(fixtures) {
        new_TestFixture("tests_<module>_<header>_<function1>_<what1>",
                         tests_<module>_<header>_<function1>_<what1>),
        new_TestFixture("tests_<module>_<header>_<function1>_<what2>",
                         tests_<module>_<header>_<function1>_<what2>),
        new_TestFixture("tests_<module>_<header>_<function2>_<what1>",
                         tests_<module>_<header>_<function2>_<what1>),
        new_TestFixture("tests_<module>_<header>_<function2>_<what2>",
                         tests_<module>_<header>_<function2>_<what2>),
        /* ... */
    };

    EMB_UNIT_TESTCALLER(<module>_<header>_tests, "<module>_<header>_tests",
                        tests_<module>_<header>_set_up, 
                        tests_<module>_<header>_tear_down, fixtures);
    /* set up and tear down function can be NULL if omitted */

    return (Test *)&<module>_<header>;        
}

The following assertion macros are available via embUnit

Assertion Description
TEST_ASSERT_EQUAL_STRING(expected,actual) Assert that strings actual and expected are equivalent
TEST_ASSERT_EQUAL_INT(expected,actual) Assert that integers actual and expected are equivalent
TEST_ASSERT_NULL(pointer) Assert that pointer == NULL
TEST_ASSERT_NOT_NULL(pointer) Assert that pointer != NULL
TEST_ASSERT_MESSAGE(condition, message) Assert that condition is TRUE (non-zero) or output customized message on failure.
TEST_ASSERT(condition) Assert that condition is TRUE (non-zero)
TEST_FAIL(message) Register a failed assertion with the specified message. No logical test is performed.

Update main.c

Add the following lines somewhere between TESTS_START(); and TESTS_END(); in tests/unittests/main.c:

#if !defined(TEST_NOT_ALL) || defined(TEST_<MODULE>_ENABLED)
    TESTS_RUN(tests_<module>_<header1>_tests());
    TESTS_RUN(tests_<module>_<header2>_tests());
#endif

Update Makefile

Add the following lines somewhere in tests/unittests/Makefile

tests-<module> : CFLAGS += -DTEST_NOT_ALL -DTESTS_<MODULE>_ENABLED
tests-<module> : SRC := $(wildcard tests-<module>*.c) main.c
tests-<module> : OBJ := $(SRC:%.c=${BINDIR}${PROJECT}/%.o)
tests-<module> : all
Clone this wiki locally