1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

congure_test: initial import of CongURE test framework

This commit is contained in:
Martine Lenders 2021-02-01 20:08:51 +01:00
parent 28592c203c
commit e65fee4587
No known key found for this signature in database
GPG Key ID: CCD317364F63286F
10 changed files with 769 additions and 2 deletions

View File

@ -25,6 +25,10 @@ ifneq (,$(filter congure_%,$(USEMODULE)))
USEMODULE += congure
endif
ifneq (,$(filter congure_test,$(USEMODULE)))
USEMODULE += fmt
endif
ifneq (,$(filter eepreg,$(USEMODULE)))
FEATURES_REQUIRED += periph_eeprom
endif

View File

@ -8,6 +8,8 @@ if !TEST_KCONFIG
menu "CongURE congestion control abstraction"
depends on USEMODULE_CONGURE
rsource "test/Kconfig"
endmenu # CongURE congestion control abstraction
endif # !TEST_KCONFIG
@ -18,5 +20,7 @@ menuconfig MODULE_CONGURE
if MODULE_CONGURE
rsource "test/Kconfig"
endif # MODULE_CONGURE
endif # TEST_KCONFIG

View File

@ -1 +1,5 @@
ifneq (,$(filter congure_test,$(USEMODULE)))
DIRS += test
endif
include $(RIOTBASE)/Makefile.base

30
sys/congure/test/Kconfig Normal file
View File

@ -0,0 +1,30 @@
# Copyright (c) 2021 Freie Universität Berlin
#
# 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.
if !TEST_KCONFIG
menuconfig KCONFIG_USEMODULE_CONGURE_TEST
bool "Configure CongURE test framework"
depends on USEMODULE_CONGURE_TEST
help
Configure CongURE test framework via Kconfig.
if KCONFIG_USEMODULE_CONGURE_TEST
rsource "Kconfig.config"
endif # KCONFIG_USEMODULE_CONGURE_TEST
endif # !TEST_KCONFIG
if TEST_KCONFIG
menuconfig MODULE_CONGURE_TEST
bool "CongURE test framework"
depends on TEST_KCONFIG
select MODULE_FMT
if MODULE_CONGURE_TEST
rsource "Kconfig.config"
endif # MODULE_CONGURE_TEST
endif # TEST_KCONFIG

View File

@ -0,0 +1,19 @@
# Copyright (c) 2021 Freie Universität Berlin
#
# 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.
#
# XXX This file only is required since there is no easy way to use these config
# options with both the final MODULE_SHELL and KCONFIG_USEMODULE_SHELL in a
# nicely looking and easy to migrate way. After migration, the content of this
# file can be folded back into `sys/shell/Kconfig`
config CONGURE_TEST_LOST_MSG_POOL_SIZE
int "Pool size for the list elements for a lost message report"
default 4
help
@see congure_snd_driver_t::report_msg_lost
This defines the maximum number of 3-tuples you can use with
@ref congure_test_call_report() when argv[1] is msg_lost.

View File

@ -0,0 +1,3 @@
MODULE := congure_test
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,369 @@
/*
* Copyright (C) 2021 Freie Universität Berlin
*
* 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.
*/
/**
* @{
*
* @file
* @author Martine S. Lenders <m.lenders@fu-berlin.de>
*/
#include <limits.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include "fmt.h"
#include "congure/test.h"
static bool _scn_u32_dec_with_zero(const char *str, size_t n, uint32_t *res)
{
if ((n == 1) && str[0] == '0') {
*res = 0;
}
else if ((*res = scn_u32_dec(str, n)) == 0) {
return false;
}
return true;
}
int congure_test_clear_state(int argc, char **argv)
{
(void)argc;
(void)argv;
memset(congure_test_get_state(), 0, sizeof(congure_test_snd_t));
print_str("{\"success\":null}\n");
return 0;
}
int congure_test_call_setup(int argc, char **argv)
{
congure_test_snd_t *c = congure_test_get_state();
uint32_t id = 0;
if (argc > 1) {
if (!_scn_u32_dec_with_zero(argv[1], strlen(argv[1]), &id)) {
print_str("{\"error\":\"`id` expected to be integer\"}\n");
return 1;
}
}
if (congure_test_snd_setup(c, (unsigned)id) < 0) {
print_str("{\"error\":\"`id` is invalid\"}");
return 1;
}
print_str("{");
print_str("\"success\":\"0x");
print_u32_hex((intptr_t)c);
print_str("\"}\n");
return 0;
}
static inline bool _check_driver(congure_test_snd_t *c)
{
if (c->super.driver == NULL) {
print_str("{\"error\":\"State object not set up\"}\n");
return false;
}
return true;
}
int congure_test_call_init(int argc, char **argv)
{
congure_test_snd_t *c = congure_test_get_state();
uint32_t ctx;
size_t arglen;
if (!_check_driver(c)) {
return 1;
}
if (argc < 2) {
print_str("{\"error\":\"`ctx` argument expected\"}\n");
return 1;
}
arglen = strlen(argv[1]);
if ((arglen < 3) || ((argv[1][0] != '0') && (argv[1][1] != 'x'))) {
print_str("{\"error\":\"`ctx` expected to be hex\"}\n");
return 1;
}
ctx = scn_u32_hex(&argv[1][2], arglen - 2);
c->super.driver->init(&c->super, (void *)((intptr_t)ctx));
print_str("{\"success\":null}\n");
return 0;
}
int congure_test_call_inter_msg_interval(int argc, char **argv)
{
congure_test_snd_t *c = congure_test_get_state();
uint32_t msg_size;
int32_t res;
(void)argc;
(void)argv;
if (!_check_driver(c)) {
return 1;
}
if (argc < 2) {
print_str("{\"error\":\"`msg_size` argument expected\"}\n");
return 1;
}
if (!_scn_u32_dec_with_zero(argv[1], strlen(argv[1]), &msg_size)) {
print_str("{\"error\":\"`msg_size` expected to be integer\"}\n");
return 1;
}
res = c->super.driver->inter_msg_interval(&c->super, msg_size);
print_str("{\"success\":");
print_s32_dec(res);
print_str("}\n");
return 0;
}
static int _call_report_msg_sent(int argc, char **argv)
{
congure_test_snd_t *c = congure_test_get_state();
uint32_t msg_size;
if (argc < 2) {
print_str("{\"error\":\"`msg_size` argument expected\"}\n");
return 1;
}
if (!_scn_u32_dec_with_zero(argv[1], strlen(argv[1]), &msg_size)) {
print_str("{\"error\":\"`msg_size` expected to be integer\"}\n");
return 1;
}
c->super.driver->report_msg_sent(&c->super, (unsigned)msg_size);
print_str("{\"success\":null}\n");
return 0;
}
static int _call_report_msg_discarded(int argc, char **argv)
{
congure_test_snd_t *c = congure_test_get_state();
uint32_t msg_size;
if (argc < 2) {
print_str("{\"error\":\"`msg_size` argument expected\"}\n");
return 1;
}
if (!_scn_u32_dec_with_zero(argv[1], strlen(argv[1]), &msg_size)) {
print_str("{\"error\":\"`msg_size` expected to be integer\"}\n");
return 1;
}
c->super.driver->report_msg_discarded(&c->super, (unsigned)msg_size);
print_str("{\"success\":null}\n");
return 0;
}
static int _call_report_msgs_timeout_lost(void (*method)(congure_snd_t *,
congure_snd_msg_t *),
int argc, char **argv)
{
static congure_snd_msg_t list_pool[CONFIG_CONGURE_TEST_LOST_MSG_POOL_SIZE];
clist_node_t msgs = { .next = NULL };
congure_test_snd_t *c = congure_test_get_state();
if (argc < 4) {
print_str("{\"error\":\"At least 3 arguments `msg_send_time`, "
"`msg_size`, `msg_resends` expected\"}\n");
return 1;
}
if ((argc - 1) % 3) {
print_str("{\"error\":\"Number of arguments must be divisible "
"by 3\"}\n");
return 1;
}
if ((unsigned)((argc - 1) / 3) >= ARRAY_SIZE(list_pool)) {
print_str("{\"error\":\"List element pool depleted\"}");
return 1;
}
for (int i = 1; i < argc; i += 3) {
uint32_t tmp;
unsigned pool_idx = ((i - 1) / 3);
list_pool[pool_idx].super.next = NULL;
if (!_scn_u32_dec_with_zero(argv[i], strlen(argv[i]), &tmp)) {
print_str("{\"error\":\"`msg_send_time` expected to be "
"integer\"}\n");
return 1;
}
list_pool[pool_idx].send_time = tmp;
if (!_scn_u32_dec_with_zero(argv[i + 1], strlen(argv[i + 1]), &tmp)) {
print_str("{\"error\":\"`msg_size` expected to be integer\"}\n");
return 1;
}
list_pool[pool_idx].size = tmp;
if (!_scn_u32_dec_with_zero(argv[i + 2], strlen(argv[i + 2]), &tmp)) {
print_str("{\"error\":\"`msg_resends` expected to be "
"integer\"}\n");
return 1;
}
list_pool[pool_idx].resends = tmp;
clist_rpush(&msgs, &list_pool[pool_idx].super);
}
method(&c->super, (congure_snd_msg_t *)msgs.next);
print_str("{\"success\":null}\n");
return 0;
}
static int _call_report_msgs_timeout(int argc, char **argv)
{
congure_test_snd_t *c = congure_test_get_state();
return _call_report_msgs_timeout_lost(c->super.driver->report_msgs_timeout,
argc, argv);
}
static int _call_report_msgs_lost(int argc, char **argv)
{
congure_test_snd_t *c = congure_test_get_state();
return _call_report_msgs_timeout_lost(c->super.driver->report_msgs_lost,
argc, argv);
}
static int _call_report_msg_acked(int argc, char **argv)
{
static congure_snd_msg_t msg = { .size = 0 };
static congure_snd_ack_t ack = { .size = 0 };
congure_test_snd_t *c = congure_test_get_state();
uint32_t tmp;
if (argc < 10) {
print_str("{\"error\":\"At least 9 arguments `msg_send_time`, "
"`msg_size`, `msg_resends`, `ack_recv_time`, `ack_id`, "
"`ack_size`, `ack_clean`, `ack_wnd`, `ack_delay` "
"expected\"}\n");
return 1;
}
if (!_scn_u32_dec_with_zero(argv[1], strlen(argv[1]), &tmp)) {
print_str("{\"error\":\"`msg_send_time` expected to be "
"integer\"}\n");
return 1;
}
msg.send_time = tmp;
if (!_scn_u32_dec_with_zero(argv[2], strlen(argv[2]), &tmp)) {
print_str("{\"error\":\"`msg_size` expected to be integer\"}\n");
return 1;
}
msg.size = tmp;
if (!_scn_u32_dec_with_zero(argv[3], strlen(argv[3]), &tmp)) {
print_str("{\"error\":\"`msg_resends` expected to be integer\"}\n");
return 1;
}
msg.resends = tmp;
if (!_scn_u32_dec_with_zero(argv[4], strlen(argv[4]), &tmp)) {
print_str("{\"error\":\"`ack_recv_time` expected to be integer\"}\n");
return 1;
}
ack.recv_time = tmp;
if (!_scn_u32_dec_with_zero(argv[5], strlen(argv[5]), &tmp)) {
print_str("{\"error\":\"`ack_id` expected to be integer\"}\n");
return 1;
}
ack.id = tmp;
if (!_scn_u32_dec_with_zero(argv[6], strlen(argv[6]), &tmp)) {
print_str("{\"error\":\"`ack_size` expected to be integer\"}\n");
return 1;
}
ack.size = tmp;
if (!_scn_u32_dec_with_zero(argv[7], strlen(argv[7]), &tmp)) {
print_str("{\"error\":\"`ack_clean` expected to be integer\"}\n");
return 1;
}
ack.clean = (bool)tmp;
if (!_scn_u32_dec_with_zero(argv[8], strlen(argv[8]), &tmp)) {
print_str("{\"error\":\"`ack_wnd` expected to be integer\"}\n");
return 1;
}
if (tmp > CONGURE_WND_SIZE_MAX) {
print_str("{\"error\":\"`ack_wnd` not 16 bit wide\"}\n");
return 1;
}
ack.wnd = (uint16_t)tmp;
if (!_scn_u32_dec_with_zero(argv[9], strlen(argv[9]), &tmp)) {
print_str("{\"error\":\"`ack_delay` expected to be integer\"}\n");
return 1;
}
if (tmp > UINT16_MAX) {
print_str("{\"error\":\"`ack_delay` not 16 bit wide\"}\n");
return 1;
}
ack.delay = (uint16_t)tmp;
c->super.driver->report_msg_acked(&c->super, &msg, &ack);
print_str("{\"success\":null}\n");
return 0;
}
static int _call_report_ecn_ce(int argc, char **argv)
{
congure_test_snd_t *c = congure_test_get_state();
uint32_t time;
if (argc < 2) {
print_str("{\"error\":\"`time` argument expected\"}\n");
return 1;
}
if (!_scn_u32_dec_with_zero(argv[1], strlen(argv[1]), &time)) {
print_str("{\"error\":\"`time` expected to be integer\"}\n");
return 1;
}
c->super.driver->report_ecn_ce(&c->super, time);
print_str("{\"success\":null}\n");
return 0;
}
int congure_test_call_report(int argc, char **argv)
{
if (!_check_driver(congure_test_get_state())) {
return 1;
}
if (argc < 2) {
print_str("{\"error\":\"No report command provided\"}\n");
return 1;
}
if (strcmp(argv[1], "msg_sent") == 0) {
return _call_report_msg_sent(argc - 1, &argv[1]);
}
else if (strcmp(argv[1], "msg_discarded") == 0) {
return _call_report_msg_discarded(argc - 1, &argv[1]);
}
else if (strcmp(argv[1], "msgs_timeout") == 0) {
return _call_report_msgs_timeout(argc - 1, &argv[1]);
}
else if (strcmp(argv[1], "msgs_lost") == 0) {
return _call_report_msgs_lost(argc - 1, &argv[1]);
}
else if (strcmp(argv[1], "msg_acked") == 0) {
return _call_report_msg_acked(argc - 1, &argv[1]);
}
else if (strcmp(argv[1], "ecn_ce") == 0) {
return _call_report_ecn_ce(argc - 1, &argv[1]);
}
print_str("{\"error\":\"Unknown command `");
print_str(argv[1]);
print_str("`\"}\n");
return 1;
}
/** @} */

View File

@ -171,7 +171,7 @@ struct congure_snd_driver {
*
* @param[in] c The CongURE state object.
* @param[in] msgs A collection of messages for which the ACK
* timed out. The list may be changed by the
* timed out. The list must not be changed by the
* method.
*/
void (*report_msgs_timeout)(congure_snd_t *c, congure_snd_msg_t *msgs);
@ -190,7 +190,8 @@ struct congure_snd_driver {
*
* @param[in] c The CongURE state object.
* @param[in] msgs A collection of messages that are known to
* be lost. The list may be changed by the method.
* be lost. The list must not be be changed by the
* method.
*/
void (*report_msgs_lost)(congure_snd_t *c, congure_snd_msg_t *msgs);

319
sys/include/congure/test.h Normal file
View File

@ -0,0 +1,319 @@
/*
* Copyright (C) 2021 Freie Universität Berlin
*
* 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.
*/
/**
* @defgroup sys_congure_test CongURE test framework shell commands
* @ingroup sys_congure
* @brief Shell commands to test a CongURE implementation
*
* This module requires an application defined `congure_impl.h` which defines
* the congure_snd_t extension of the CongURE implementation as
* @ref congure_test_snd_t and provides a function declaration
* @ref congure_test_snd_setup() setup said type. E.g.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* typedef congure_reno_snd_t congure_test_snd_t;
*
* void congure_test_snd_setup(congure_test_snd_t *c, int id);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* All constants and initial values can then be set within the application
* specific definition of @ref congure_test_snd_setup().
*
* @{
*
* @file
* @brief Definitions for the CongURE test framework
*
* @author Martine S Lenders <m.lenders@fu-berlin.de>
*/
#ifndef CONGURE_TEST_H
#define CONGURE_TEST_H
#include "congure_impl.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef DOXYGEN
/**
* @brief Application-defined type for the CongURE state object under test
*
* @extends congure_snd_t
*
* @note Needs to be set within an application-provided `congure_impl.h` to
* the state object of the CongURE implementation you want to test.
*/
typedef congure_snd_t congure_test_snd_t;
/**
* @brief Setup the application-defined CongURE state object under test
*
* @note Needs to be defined by the application and declare it within
* an application-provided `congure_impl.h`
*
* @param[in,out] c The CongURE state object under test. May not be NULL.
* @param[in] id And application-specific ID that may identify different
* setup parameters, e.g. a set of different constants to
* use when setting up @p c. If not applicable to your
* application just ignore @p id.
*
* @retval 0 on success
* @retval -1 when @p id is not ignored and is an unknown value to the
* application.
*/
int congure_test_snd_setup(void congure_test_snd_t *c, unsigned id);
#endif /* DOXYGEN */
/**
* @brief Pool size for the message list elements for a lost message report
*
* @see congure_snd_driver_t::report_msg_lost
*
* This defines the maximum number of 3-tuples you can use with
* @ref congure_test_call_report() when `argv[1] = "msg_lost"`.
*/
#ifndef CONFIG_CONGURE_TEST_LOST_MSG_POOL_SIZE
#define CONFIG_CONGURE_TEST_LOST_MSG_POOL_SIZE (4U)
#endif
/**
* @brief Get the application-defined CongURE state object
*
* @note Needs to be defined by the application
*
* @return The CongURE state object.
*/
congure_test_snd_t *congure_test_get_state(void);
/**
* @brief Clears the CongURE state object
*
* Every byte in the state object is set to 0.
*
* @param[in] argc Number of @p argv. Needs to be at least 1.
* @param[in] argv Command line arguments. No extra arguments are required
* except for the command name in `argv[0]`.
*
* Always generates the following JSON object in STDOUT:
* @code {"success": null} @endcode
*
* @return Always 0.
*/
int congure_test_clear_state(int argc, char **argv);
/**
* @brief Setup the CongURE state object
*
* Calls application-defined @ref congure_test_snd_setup()
*
* @param[in] argc Number of @p argv. Needs to be at least 1.
* @param[in] argv Command line arguments. The command name is expected in
* `argv[0]`. If @p argc > 1, a integer is expected in
* `argv[1]` for the `id` parameter of
* @ref congure_test_snd_setup().
*
* This function will generate the following JSON objects in STDOUT:
* - @code {"success": "0x12345678"} @endcode
* On success, with `0x12345678` being replaced with the memory address of the
* state object.
* - @code {"error":"`id` expected to be integer"} @endcode
* On error, when `argv[1]` is not parsable to an unsigned integer.
* - @code {"error":"`id` is invalid"} @endcode
* On error, when `argv[1]` is a valid unsigned integer but is an unknown
* value to the application.
*
* @retval 0 on success.
* @retval 1 on error. Only can happen if `argv[1]` is provided and an invalid
* or unexpected value.
*/
int congure_test_call_setup(int argc, char **argv);
/**
* @brief Calls `init()` method for CongURE state object.
*
* @see congure_snd_driver_t::init()
*
* @param[in] argc Number of @p argv. Needs to be at least 2.
* @param[in] argv Command line arguments. `argv[0]` needs to be the command
* name and `argv[1]` needs to be a hexadecimal integer of
* format 0xXXXX, represending a pointer to the object used as
* the `ctx` parameter for `init()`.
*
* This function will generate the following JSON objects in STDOUT on error:
* - @code {"error":"State object not set up"} @endcode
* When @ref congure_test_snd_setup() was not called before calling this
* command (i.e. the `driver` member of the state object is `NULL`).
* - @code {"error":"`ctx` argument expected"} @endcode
* When @p argc < 2.
* - @code {"error":"`ctx` expected to be hex"} @endcode
* When `argv[1]` is not parseable as a hexadecimal integer.
*
* Always generates the following JSON object in STDOUT:
* @code {"success": null} @endcode
*
* @retval 0 on success.
* @retval 1 on error.
*/
int congure_test_call_init(int argc, char **argv);
/**
* @brief Calls `inter_msg_interval()` method for CongURE state object.
*
* @see congure_snd_driver_t::inter_msg_interval()
*
* @param[in] argc Number of @p argv. Needs to be at least 1.
* @param[in] argv Command line arguments. No extra arguments are required
* except for the command name in `argv[0]`.
*
* This function will generate the following JSON objects in STDOUT:
* - @code {"success":X} @endcode
* On success, with `X` being replaced with the return value of
* congure_snd_driver_t::inter_msg_interval().
* - @code {"error":"State object not set up"} @endcode
* When @ref congure_test_snd_setup() was not called before calling this
* command (i.e. the `driver` member of the state object is `NULL`).
*
*
* @retval 0 on success.
* @retval 1 on error.
*/
int congure_test_call_inter_msg_interval(int argc, char **argv);
/**
* @brief Calls one of the `report_*()` methods for CongURE state object.
*
* @see congure_snd_driver_t::report_msg_sent()
* @see congure_snd_driver_t::report_msg_discarded()
* @see congure_snd_driver_t::report_msg_timeout()
* @see congure_snd_driver_t::report_msg_lost()
* @see congure_snd_driver_t::report_msg_acked()
* @see congure_snd_driver_t::report_ecn_ce()
*
* @param[in] argc Number of @p argv. Needs to be at least 3.
* @param[in] argv Command line arguments. `argv[0]` needs to be the command
* name and `argv[1]` needs to one of the following
* sub-commands that may require at least one extra arguments:
* - `msg_sent`: `argv[2]` is expected to be an integer for the
* `msg_size` parameter of
* congure_snd_driver_t::report_msg_sent()
* - `msg_sent`: `argv[2]` is expected to be an integer for the
* `msg_size` parameter of
* congure_snd_driver_t::report_msg_discarded()
* - `msg_timeout`: `argv` is expected to have a number of
* parameters divisible by 3 after `argv[1]` (i.e.
* `(argc - 2) % 3 == 0` must hold). Each group of 3
* `argv[2+i]`, `argv[3+i]`, argv[4+i] (with `i` being the
* offset of the group) represents an element in the `msgs`
* list parameter of
* congure_snd_driver_t::report_msg_timeout():
* - `argv[2+i]` (`msg_send_time`) is expected to be an
* integer for the `send_time` member of
* @ref congure_snd_msg_t,
* - `argv[3+i]` (`msg_size`) is expected to be a an integer
* for the `size` member of @ref congure_snd_msg_t, and
* - `argv[4+i]` (`msg_resends`) is expected to be an integer
* integer for the `resends` member of
* @ref congure_snd_msg_t.
* - `msg_lost`: `argv` is expected to have a number of
* parameters divisible by 3 after `argv[1]` (i.e.
* `(argc - 2) % 3 == 0` must hold. Each group of 3
* `argv[2+i]`, `argv[3+i]`, argv[4+i] (with `i` being the
* offset of the group) represents an element in the `msgs`
* list parameter of
* congure_snd_driver_t::report_msg_lost():
* - `argv[2+i]` (`msg_send_time`) is expected to be an
* integer for the `send_time` member of
* @ref congure_snd_msg_t,
* - `argv[3+i]` (`msg_size`) is expected to be an integer
* for the `size` member of @ref congure_snd_msg_t, and
* - `argv[4+i]` (`msg_resends`) is expected to be a an integer
* integer for the `resends` member of
* @ref congure_snd_msg_t.
* - `msg_acked`: `argc` must be 11. The first three arguments
* after `argv[1]` represent members of the `msg` parameter
* of congure_snd_driver_t::report_msg_acked():
* - `argv[2]` (`msg_send_time`) is expected to be an
* integer for the `send_time` member of
* @ref congure_snd_msg_t,
* - `argv[3]` (`msg_size`) is expected to be an integer
* for the `size` member of @ref congure_snd_msg_t, and
* - `argv[4]` (`msg_resends`) is expected to be an integer
* for the `resends` member of @ref congure_snd_msg_t.
*
* The `next` member of @ref congure_snd_msg_t will be
* initialized with `NULL`.
*
* The remaining 6 arguments represent members of the `ack`
* parameter of congure_snd_driver_t::report_msg_acked():
* - `argv[5]` (`ack_recv_time`) is expected to be a an
* integer for the `recv_time` member of
* @ref congure_snd_ack_t,
* - `argv[6]` (`ack_id`) is expected to be a an integer
* for the `ack_id` member of @ref congure_snd_ack_t, and
* - `argv[7]` (`ack_size`) is expected to be a an integer
* integer for the `size` member of @ref congure_snd_ack_t.
* - `argv[8]` (`ack_clean`) is expected to be a an integer
* for the `clean` member of @ref congure_snd_ack_t. If
* `argv[8]` is `"0"`, `clean` will be set to `false` and to
* `true` otherwise.
* - `argv[9]` (`ack_wnd`) is expected to be a 16-bit integer
* for the `wnd` member of @ref congure_snd_ack_t.
* - `argv[10]` (`ack_delay`) is expected to be a 16-bit
* integer for the `delay` member of
* @ref congure_snd_ack_t.
* - `ecn_ce`: `argv[2]` is expected to be an integer for the
* `time` parameter of congure_snd_driver_t::report_ecn_ce()
*
* This function will generate the following JSON objects in STDOUT:
* - @code {"success":null} @endcode
* On success
* - @code {"error":"State object not set up"} @endcode
* When @ref congure_test_snd_setup() was not called before calling this
* command (i.e. the `driver` member of the state object is `NULL`).
* - @code {"error":"No report command provided"} @endcode
* When @p argc < 2.
* - @code {"error":"Unknown command `<command>`"} @endcode
* When `argv[1] = "<command>"` is not a known sub-command.
* - @code {"error":"`<arg_name>` argument expected"} @endcode
* When `argv[i] = "<arg_name>"` is expected but `argc <= i`.
* - @code {"error":"`<arg_name>` expected to be integer"} @endcode
* When `argv[i] = "<arg_name>"` is expected to be an integer but is not
* parseable
* - @code {"error":"`<arg_name>` expected not 16 bit wide"} @endcode
* When `argv[i] = "<arg_name>"` is expected to be an 16-bit unsigned integer
* but is is greater than or equal to $2^{16}$
* - @code {"error":"At least `<arg_num>` arguments <arglist> expecdted"}
* @endcode
* When `argc` is smaller than expected. `<arg_num>` provides the number of
* arguments beyond the sub-command (i.e. `argc` needs at least to be
* `<arg_num> + 2`), with the names of the arguments expected provided in
* `<arg_list>` as a comma-seperated list of <tt>`<arg_name>`</tt>.
* - @code {"error":"Number of arguments must be divisible by 3"} @endcode
* When `argv[1] == "msg_timeout"` or `argv[1] == "msg_lost"` but
* the length of the argument list after `argv[1]` is not divisible by 3 (i.e.
* `(argc - 2) % 3 != 0`).
* - @code {"error":"List element pool depleted"} @endcode
* When `argv[1] == "msg_timeout"` or `argv[1] == "msg_lost"` and
* `(argc - 2) / 3` >= @ref CONFIG_CONGURE_TEST_LOST_MSG_POOL_SIZE).
*
* Provides no output on success.
*
* @retval 0 on success.
* @retval 1 on error.
*/
int congure_test_call_report(int argc, char **argv);
#ifdef __cplusplus
}
#endif
#endif /* CONGURE_TEST_H */
/** @} */

View File

@ -22,6 +22,9 @@
#include <stdlib.h>
#include "shell_commands.h"
#ifdef MODULE_CONGURE_TEST
#include "congure/test.h"
#endif
extern int _reboot_handler(int argc, char **argv);
extern int _version_handler(int argc, char **argv);
@ -338,6 +341,17 @@ const shell_command_t _shell_command_list[] = {
#endif
#ifdef MODULE_DFPLAYER
{"dfplayer", "Control a DFPlayer Mini MP3 player", _sc_dfplayer},
#endif
#ifdef MODULE_CONGURE_TEST
{ "cong_clear", "Clears CongURE state object", congure_test_clear_state },
{ "cong_setup", "Calls the setup function for the CongURE state object",
congure_test_call_setup },
{ "cong_init", "Calls init method of the CongURE state object",
congure_test_call_init },
{ "cong_imi", "Calls inter_message_interval method of the CongURE state object",
congure_test_call_inter_msg_interval },
{ "cong_report", "Calls a report_* method of the CongURE state object",
congure_test_call_report },
#endif
{NULL, NULL, NULL}
};