diff --git a/sys/Makefile.dep b/sys/Makefile.dep index 54c243b363..019d117ef1 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -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 diff --git a/sys/congure/Kconfig b/sys/congure/Kconfig index a4c788b615..2cc77d5001 100644 --- a/sys/congure/Kconfig +++ b/sys/congure/Kconfig @@ -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 diff --git a/sys/congure/Makefile b/sys/congure/Makefile index 48422e909a..12697067ff 100644 --- a/sys/congure/Makefile +++ b/sys/congure/Makefile @@ -1 +1,5 @@ +ifneq (,$(filter congure_test,$(USEMODULE))) + DIRS += test +endif + include $(RIOTBASE)/Makefile.base diff --git a/sys/congure/test/Kconfig b/sys/congure/test/Kconfig new file mode 100644 index 0000000000..f710d4f23d --- /dev/null +++ b/sys/congure/test/Kconfig @@ -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 diff --git a/sys/congure/test/Kconfig.config b/sys/congure/test/Kconfig.config new file mode 100644 index 0000000000..d33ebccd2f --- /dev/null +++ b/sys/congure/test/Kconfig.config @@ -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. diff --git a/sys/congure/test/Makefile b/sys/congure/test/Makefile new file mode 100644 index 0000000000..2c32d373f3 --- /dev/null +++ b/sys/congure/test/Makefile @@ -0,0 +1,3 @@ +MODULE := congure_test + +include $(RIOTBASE)/Makefile.base diff --git a/sys/congure/test/congure_test.c b/sys/congure/test/congure_test.c new file mode 100644 index 0000000000..675dd18b9f --- /dev/null +++ b/sys/congure/test/congure_test.c @@ -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 + */ + +#include +#include +#include +#include + +#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; +} + +/** @} */ diff --git a/sys/include/congure.h b/sys/include/congure.h index 9031b54b05..d993002893 100644 --- a/sys/include/congure.h +++ b/sys/include/congure.h @@ -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); diff --git a/sys/include/congure/test.h b/sys/include/congure/test.h new file mode 100644 index 0000000000..3383aa6962 --- /dev/null +++ b/sys/include/congure/test.h @@ -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 + */ + +#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 ``"} @endcode + * When `argv[1] = ""` is not a known sub-command. + * - @code {"error":"`` argument expected"} @endcode + * When `argv[i] = ""` is expected but `argc <= i`. + * - @code {"error":"`` expected to be integer"} @endcode + * When `argv[i] = ""` is expected to be an integer but is not + * parseable + * - @code {"error":"`` expected not 16 bit wide"} @endcode + * When `argv[i] = ""` is expected to be an 16-bit unsigned integer + * but is is greater than or equal to $2^{16}$ + * - @code {"error":"At least `` arguments expecdted"} + * @endcode + * When `argc` is smaller than expected. `` provides the number of + * arguments beyond the sub-command (i.e. `argc` needs at least to be + * ` + 2`), with the names of the arguments expected provided in + * `` as a comma-seperated list of ``. + * - @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 */ +/** @} */ diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c index f2d5a13a25..50dc2ce4b1 100644 --- a/sys/shell/commands/shell_commands.c +++ b/sys/shell/commands/shell_commands.c @@ -22,6 +22,9 @@ #include #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} };