This changes the API of nanocoap with the goal to reduce the expose of
UDP specifics in the API. The plan is to eventually support transports
such as CoAP over TCP and CoAP over WebSocket directly in nanocoap
while sharing most of the code, as e.g. the CoAP Option processing
remains identical. Specifically, the plan is to unlock a transport with
modules and introduce overhead for dispatching to specific transport
only when multiple transports are actually in use.
Support for OSCORE directly in nanocoap is probably not sensible, as
the serialization is very much unlike the other transports. A unified
CoAP API for multiple transports including OSCORE is probably best
implemented on top. But when limited to the boring set of CoAP
transports, we probably can support them well with nanocoap with less
overhead.
Breaking API Changes:
=====================
- `coap_parse()` now returns `ssize_t` instead of `int`
- This function is not really user facing, so the impact should
be limited
- This is useful for stream transports where the buffer may
contain data of more than one packet. The return value contains
the number of bytes actually consumed, which will match the
buffer size for non-stream transports.
API Changes:
============
- `coap_pkt_t` now contains a `uint8_t *buf` pointer instead of a
`coap_hdr_t *hdr` pointer to the beginning of the buffer
- This will also work when the buffer is used by non-UDP
transports
- A deprecated `coap_udp_hdr_t *hdr` has been crammed into
an unnamed `union` with `uint8_t *buf`. For architectures
where pointers have the same memory layout regardless of type
(e.g. all of the supported ones), this will make `hdr` an
alias for `buf`.
- The alias will only be provided if no transport besides UDP is
used in nanocoap. So existing apps will continue to work, new
apps that want to support other transports need to move to
adapt.
- `coap_hdr_t` has been renamed to `coap_udp_hdr_t`
- A deprecated alias was created for deprecation
- `coap_hdr*()` functions have been deprecated
- Equivalent `coap_pkt*()` functions have been created that work
on `coap_pkt_t *` instead of `coap_hdr_t *`
- If non-UDP transports are used, the deprecated `coap_hdr*()`
will probably not be exposed to avoid footguns.
- `coap_build_hdr()` has been renamed to `coap_build_udp_hdr()` and
that works on an `uint8_t *` buffer with a given length, rather than
on a `coap_hdr_t *` with a *figers crossed* length
- a deprecated `coap_build_hdr()` function was added that calls
to `coap_build_udp_hdr()` and has the same signature, so that
users have time to update
Unittests
Building and running tests
Tests can be built 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
make term
or flash them to your board as you would flash any RIOT application to the board (see Supported Boards).
You can debug your tests by running
make debug
and using GDB as usual.
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" - Color:
OUTPUT="COLOR"(like default, but with red/green output) - Colored-Text:
OUTPUT="COLORTEXT"(likeTEXT, but with red/green output)
Compile example
OUTPUT="COMPILER" make tests-core
make term
(only outputs in case of test failures)
Text example
OUTPUT="TEXT" make tests-core
make term
- core_bitarithm_tests
1) OK test_SETBIT_null_null
2) OK test_SETBIT_null_limit
3) ...
- core_clist_tests
25) ...
- ...
OK (... tests)
XML example
OUTPUT="XML" make tests-core
make term
<?xml version="1.0" encoding='shift_jis' standalone='yes' ?>
<TestRun>
<core_bitarithm_tests>
<Test id="1">
<Name>test_SETBIT_null_null</Name>
</Test>
<Test id="2">
<Name>test_SETBIT_null_limit</Name>
</Test>
...
</core_bitarithm_tests>
<core_clist_tests>
<Test id="25">
<Name>test_clist_add_one</Name>
</Test>
...
</core_clist_tests>
<Statistics>
<Tests>...</Tests>
</Statistics>
</TestRun>
Writing unit tests
File structure
RIOT uses embUnit for unit testing.
All unit tests are organized in tests/unittests and can be built module-wise, if needed.
For each module there exists a tests-<modulename>/tests-<modulename>.h file, at least one C file in tests-<modulename>/ and a tests-<modulename>/Makefile.
It is recommended to add a C file named tests-<modulename>/tests-<modulename>-<headername>.c for every header file that defines functions (or macros) implemented in the module.
If there is only one such header file tests-<modulename>/tests-<modulename>.c should suffice.
Each *.c file should implement a function defined in tests-<modulename>/tests-<modulename>.h, named like
Test *tests_<modulename>_<headername>_tests(void);
/* or respectively */
Test *tests_<modulename>_tests(void);
Testing a module
To write new tests for a module you need to do three things:
- Create a Makefile: add a file
tests-<modulename>/Makefile - Define a test header: add a file
tests-<modulename>/tests-<modulename>.h - Implement tests: for each header file, that defines a function or macro implemented or related to the module, add a file
tests-<modulename>/tests-<modulename>-<headername>.cortests-<modulename>/tests-<modulename>.cif there is only one header.
Create a Makefile
The Makefile should have the following content:
include $(RIOTBASE)/Makefile.base
Define a test header.
The test header tests-<modulename>/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 v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#ifndef TESTS_<MODULE>_H
#define TESTS_<MODULE>_H
/**
* @addtogroup unittests
* @{
*
* @file
* @brief Unittests for the ``module`` module
*
* @author <author>
*/
#include "embUnit/embUnit.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @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);
/* ... */
#ifdef __cplusplus
}
#endif
/** @} */
#endif /* TESTS_<MODULE>_H */
Implement tests
Every tests-<modulename>/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 v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/* clib includes */
#include "embUnit.h"
#include "<header>.h"
#include "tests-<module>.h"
/* your macros */
/* your global variables */
static void set_up(void)
{
/* omit if not needed */
}
static void tear_down(void)
{
/* omit if not needed */
}
static void test_<function1>_<what1>(void) {
/* ... */
TEST_ASSERT(/* ... */);
}
static void test_<function1>_<what2>(void) {
/* ... */
TEST_ASSERT(/* ... */);
}
/* ... */
static void test_<function2>_<what1>(void) {
/* ... */
TEST_ASSERT(/* ... */);
}
static void test_<function2>_<what2>(void) {
/* ... */
TEST_ASSERT(/* ... */);
}
/* ... */
Test *tests_<module>_<header>_tests(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_<function1>_<what1>),
new_TestFixture(test_<function1>_<what2>),
new_TestFixture(test_<function2>_<what1>),
new_TestFixture(test_<function2>_<what2>),
/* ... */
};
EMB_UNIT_TESTCALLER(<module>_<header>_tests, set_up, tear_down, fixtures);
/* set up and tear down function can be NULL if omitted */
return (Test *)&<module>_<header>_tests;
}
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. |
Out of Tree Unit Tests
Export the environment variable EXTERNAL_UNITTEST_DIRS that contains a space
separated list of out-of-tree unit tests to also include in the test. The tests
will be treated the exact same way as tests in this folder and must follow the
same naming convention (each folder in EXTERNAL_UNITTEST_DIRS should have
tests-<name> folders containing the unit tests).
This feature works best with EXTERNAL_MODULE_DIRS that contain the code the
external unit tests should cover.