mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #15951 from miri64/congure/feat/initial
congure: initial import of a congestion control framework
This commit is contained in:
commit
fc6627606c
8
.murdock
8
.murdock
@ -8,7 +8,7 @@
|
||||
: ${TEST_BOARDS_LLVM_COMPILE:=""}
|
||||
|
||||
: ${TEST_KCONFIG_samr21_xpro:="examples/hello-world tests/periph_*
|
||||
tests/test_tools tests/xtimer_* tests/ztimer_*
|
||||
tests/test_tools tests/congure_* tests/xtimer_* tests/ztimer_*
|
||||
tests/driver_ad7746 tests/driver_adcxx1c tests/driver_ads101x tests/driver_adt101x
|
||||
tests/driver_adt7310 tests/driver_adxl345 tests/driver_aip31068 tests/driver_apa102
|
||||
tests/driver_apds99xx tests/driver_apds99xx_full tests/driver_at tests/driver_at24cxxx
|
||||
@ -25,9 +25,9 @@ tests/mtd_mapper tests/driver_o* tests/driver_p* tests/driver_q*
|
||||
tests/driver_r* tests/driver_s* tests/driver_t* tests/driver_u*
|
||||
tests/driver_v*"}
|
||||
: ${TEST_KCONFIG_native:="examples/hello-world tests/periph_* tests/sys_crypto
|
||||
tests/test_tools tests/prng_* tests/xtimer_* tests/ztimer_* tests/driver_ws281x
|
||||
tests/posix_sleep tests/pkg_umorse tests/cb_mux* tests/eepreg tests/shell
|
||||
tests/struct_tm_utility"}
|
||||
tests/test_tools tests/congure_* tests/prng_* tests/xtimer_* tests/ztimer_*
|
||||
tests/driver_ws281x tests/posix_sleep tests/pkg_umorse tests/cb_mux* tests/eepreg
|
||||
tests/shell tests/struct_tm_utility"}
|
||||
|
||||
: ${TEST_WITH_CONFIG_SUPPORTED:="examples/suit_update tests/driver_at86rf2xx_aes"}
|
||||
|
||||
|
2
dist/tools/uncrustify/whitelist.txt
vendored
2
dist/tools/uncrustify/whitelist.txt
vendored
@ -8,5 +8,7 @@ cpu/riscv_common/include/.*\.h
|
||||
cpu/riscv_common/periph/.*\.c
|
||||
sys/riotboot/.*\.h
|
||||
sys/riotboot/.*\.c
|
||||
sys/congure.*\.c
|
||||
sys/include/congure.*\.h
|
||||
sys/ztimer/.*\.c
|
||||
sys/include/ztimer.*\.h
|
||||
|
@ -18,6 +18,7 @@ rsource "cb_mux/Kconfig"
|
||||
rsource "checksum/Kconfig"
|
||||
rsource "color/Kconfig"
|
||||
rsource "crypto/Kconfig"
|
||||
rsource "congure/Kconfig"
|
||||
rsource "div/Kconfig"
|
||||
rsource "embunit/Kconfig"
|
||||
rsource "entropy_source/Kconfig"
|
||||
|
@ -21,6 +21,14 @@ ifneq (,$(filter arduino_pwm,$(FEATURES_USED)))
|
||||
FEATURES_REQUIRED += periph_pwm
|
||||
endif
|
||||
|
||||
ifneq (,$(filter congure_%,$(USEMODULE)))
|
||||
USEMODULE += congure
|
||||
endif
|
||||
|
||||
ifneq (,$(filter congure_test,$(USEMODULE)))
|
||||
USEMODULE += fmt
|
||||
endif
|
||||
|
||||
ifneq (,$(filter eepreg,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_eeprom
|
||||
endif
|
||||
|
28
sys/congure/Kconfig
Normal file
28
sys/congure/Kconfig
Normal file
@ -0,0 +1,28 @@
|
||||
# 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
|
||||
menu "CongURE congestion control abstraction"
|
||||
depends on USEMODULE_CONGURE
|
||||
|
||||
rsource "mock/Kconfig"
|
||||
rsource "test/Kconfig"
|
||||
|
||||
endmenu # CongURE congestion control abstraction
|
||||
endif # !TEST_KCONFIG
|
||||
|
||||
if TEST_KCONFIG
|
||||
menuconfig MODULE_CONGURE
|
||||
bool "CongURE congestion control abstraction"
|
||||
depends on TEST_KCONFIG
|
||||
|
||||
if MODULE_CONGURE
|
||||
|
||||
rsource "mock/Kconfig"
|
||||
rsource "test/Kconfig"
|
||||
|
||||
endif # MODULE_CONGURE
|
||||
endif # TEST_KCONFIG
|
8
sys/congure/Makefile
Normal file
8
sys/congure/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
ifneq (,$(filter congure_mock,$(USEMODULE)))
|
||||
DIRS += mock
|
||||
endif
|
||||
ifneq (,$(filter congure_test,$(USEMODULE)))
|
||||
DIRS += test
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
3
sys/congure/mock/Kconfig
Normal file
3
sys/congure/mock/Kconfig
Normal file
@ -0,0 +1,3 @@
|
||||
config MODULE_CONGURE_MOCK
|
||||
bool "CongURE mock implementation for testing"
|
||||
depends on MODULE_CONGURE
|
3
sys/congure/mock/Makefile
Normal file
3
sys/congure/mock/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE := congure_mock
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
121
sys/congure/mock/congure_mock.c
Normal file
121
sys/congure/mock/congure_mock.c
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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 Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include "congure/mock.h"
|
||||
|
||||
static void _snd_init(congure_snd_t *cong, void *ctx);
|
||||
static int32_t _snd_inter_msg_interval(congure_snd_t *cong, unsigned msg_size);
|
||||
static void _snd_report_msg_sent(congure_snd_t *cong, unsigned msg_size);
|
||||
static void _snd_report_msg_discarded(congure_snd_t *cong, unsigned msg_size);
|
||||
static void _snd_report_msgs_lost(congure_snd_t *cong, congure_snd_msg_t *msgs);
|
||||
static void _snd_report_msgs_timeout(congure_snd_t *cong,
|
||||
congure_snd_msg_t *msgs);
|
||||
static void _snd_report_msg_acked(congure_snd_t *cong, congure_snd_msg_t *msg,
|
||||
congure_snd_ack_t *ack);
|
||||
static void _snd_report_ecn_ce(congure_snd_t *cong, ztimer_now_t time);
|
||||
|
||||
static const congure_snd_driver_t _driver = {
|
||||
.init = _snd_init,
|
||||
.inter_msg_interval = _snd_inter_msg_interval,
|
||||
.report_msg_sent = _snd_report_msg_sent,
|
||||
.report_msg_discarded = _snd_report_msg_discarded,
|
||||
.report_msgs_timeout = _snd_report_msgs_timeout,
|
||||
.report_msgs_lost = _snd_report_msgs_lost,
|
||||
.report_msg_acked = _snd_report_msg_acked,
|
||||
.report_ecn_ce = _snd_report_ecn_ce,
|
||||
};
|
||||
|
||||
void congure_mock_snd_setup(congure_mock_snd_t *c)
|
||||
{
|
||||
c->super.driver = &_driver;
|
||||
}
|
||||
|
||||
static void _snd_init(congure_snd_t *cong, void *ctx)
|
||||
{
|
||||
congure_mock_snd_t *c = (congure_mock_snd_t *)cong;
|
||||
|
||||
c->init_calls++;
|
||||
c->init_args.c = &c->super;
|
||||
c->init_args.ctx = ctx;
|
||||
}
|
||||
|
||||
static int32_t _snd_inter_msg_interval(congure_snd_t *cong, unsigned msg_size)
|
||||
{
|
||||
congure_mock_snd_t *c = (congure_mock_snd_t *)cong;
|
||||
|
||||
c->inter_msg_interval_calls++;
|
||||
c->inter_msg_interval_args.c = &c->super;
|
||||
c->inter_msg_interval_args.msg_size = msg_size;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void _snd_report_msg_sent(congure_snd_t *cong, unsigned msg_size)
|
||||
{
|
||||
congure_mock_snd_t *c = (congure_mock_snd_t *)cong;
|
||||
|
||||
c->report_msg_sent_calls++;
|
||||
c->report_msg_sent_args.c = &c->super;
|
||||
c->report_msg_sent_args.msg_size = msg_size;
|
||||
}
|
||||
|
||||
static void _snd_report_msg_discarded(congure_snd_t *cong, unsigned msg_size)
|
||||
{
|
||||
congure_mock_snd_t *c = (congure_mock_snd_t *)cong;
|
||||
|
||||
c->report_msg_discarded_calls++;
|
||||
c->report_msg_discarded_args.c = &c->super;
|
||||
c->report_msg_discarded_args.msg_size = msg_size;
|
||||
}
|
||||
|
||||
static void _snd_report_msgs_lost(congure_snd_t *cong, congure_snd_msg_t *msgs)
|
||||
{
|
||||
congure_mock_snd_t *c = (congure_mock_snd_t *)cong;
|
||||
|
||||
c->report_msgs_lost_calls++;
|
||||
c->report_msgs_lost_args.c = &c->super;
|
||||
c->report_msgs_lost_args.msgs = msgs;
|
||||
}
|
||||
|
||||
static void _snd_report_msgs_timeout(congure_snd_t *cong,
|
||||
congure_snd_msg_t *msgs)
|
||||
{
|
||||
congure_mock_snd_t *c = (congure_mock_snd_t *)cong;
|
||||
|
||||
c->report_msgs_timeout_calls++;
|
||||
c->report_msgs_timeout_args.c = &c->super;
|
||||
c->report_msgs_timeout_args.msgs = msgs;
|
||||
}
|
||||
|
||||
static void _snd_report_msg_acked(congure_snd_t *cong, congure_snd_msg_t *msg,
|
||||
congure_snd_ack_t *ack)
|
||||
{
|
||||
congure_mock_snd_t *c = (congure_mock_snd_t *)cong;
|
||||
|
||||
c->report_msg_acked_calls++;
|
||||
c->report_msg_acked_args.c = &c->super;
|
||||
c->report_msg_acked_args.msg = msg;
|
||||
c->report_msg_acked_args.ack = ack;
|
||||
}
|
||||
|
||||
static void _snd_report_ecn_ce(congure_snd_t *cong, ztimer_now_t time)
|
||||
{
|
||||
congure_mock_snd_t *c = (congure_mock_snd_t *)cong;
|
||||
|
||||
c->report_ecn_ce_calls++;
|
||||
c->report_ecn_ce_args.c = &c->super;
|
||||
c->report_ecn_ce_args.time = time;
|
||||
}
|
||||
|
||||
/** @} */
|
30
sys/congure/test/Kconfig
Normal file
30
sys/congure/test/Kconfig
Normal 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
|
19
sys/congure/test/Kconfig.config
Normal file
19
sys/congure/test/Kconfig.config
Normal 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.
|
3
sys/congure/test/Makefile
Normal file
3
sys/congure/test/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE := congure_test
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
369
sys/congure/test/congure_test.c
Normal file
369
sys/congure/test/congure_test.c
Normal 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;
|
||||
}
|
||||
|
||||
/** @} */
|
225
sys/include/congure.h
Normal file
225
sys/include/congure.h
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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 CongURE - A Congestion control framework
|
||||
* @ingroup sys
|
||||
* @brief <b>Cong</b>estion control <b>u</b>tilizing <b>r</b>e-usable
|
||||
* <b>e</b>lements
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* @author Martine S. Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef CONGURE_H
|
||||
#define CONGURE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "clist.h"
|
||||
#include "ztimer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Maximum value the window size can take
|
||||
*/
|
||||
#define CONGURE_WND_SIZE_MAX (UINT16_MAX)
|
||||
|
||||
/**
|
||||
* @brief Define type for window size to allow for possible window size
|
||||
* scaling
|
||||
*/
|
||||
typedef uint16_t congure_wnd_size_t;
|
||||
|
||||
/**
|
||||
* @brief Forward-declariton for the driver struct
|
||||
*/
|
||||
typedef struct congure_snd_driver congure_snd_driver_t;
|
||||
|
||||
/**
|
||||
* @brief Base state object for CongURE implementations
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Driver for the state object. See @ref congure_snd_driver_t.
|
||||
*/
|
||||
const congure_snd_driver_t *driver;
|
||||
/**
|
||||
* @brief Context for callbacks specific to the congestion control
|
||||
*
|
||||
* E.g. A TCP PCB.
|
||||
*/
|
||||
void *ctx;
|
||||
congure_wnd_size_t cwnd; /**< Congestion window size */
|
||||
} congure_snd_t;
|
||||
|
||||
/**
|
||||
* @brief Object to represent a collection of sent messages.
|
||||
*/
|
||||
typedef struct {
|
||||
clist_node_t super; /**< see @ref clist_node_t */
|
||||
/**
|
||||
* @brief timestamp in milliseconds of when the message was sent.
|
||||
*/
|
||||
ztimer_now_t send_time;
|
||||
congure_wnd_size_t size; /**< size in initiator-defined units */
|
||||
/**
|
||||
* @brief number of times the message has already been resent.
|
||||
*
|
||||
* This does not include the first send.
|
||||
*/
|
||||
uint8_t resends;
|
||||
} congure_snd_msg_t;
|
||||
|
||||
/**
|
||||
* @brief Object to represent an ACK to a message
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Timestamp in milliseconds of when the ACK was received.
|
||||
*/
|
||||
ztimer_now_t recv_time;
|
||||
uint32_t id; /**< ID of the message the ACK is for */
|
||||
/**
|
||||
* @brief size of the ACK in initiator-defined units
|
||||
*/
|
||||
congure_wnd_size_t size;
|
||||
/**
|
||||
* @brief the peer-reported window size in caller defined units. Leave
|
||||
* 0 if not supplied by the protocol.
|
||||
*/
|
||||
congure_wnd_size_t wnd;
|
||||
/**
|
||||
* @brief true, if ACK only contains an ACK, false if not
|
||||
*
|
||||
* This e.g. can be used to tell the congestion control mechanism that the
|
||||
* SYN or FIN tag are cleared in the ACK with TCP.
|
||||
*/
|
||||
uint16_t clean;
|
||||
/**
|
||||
* @brief the peer-reported time in milliseconds the ACK was delayed
|
||||
* since message reception. Leave 0 if not supplied by the
|
||||
* protocol.
|
||||
*/
|
||||
uint16_t delay;
|
||||
} congure_snd_ack_t;
|
||||
|
||||
/**
|
||||
* @brief Driver for CongURE objects.
|
||||
*/
|
||||
struct congure_snd_driver {
|
||||
/**
|
||||
* @brief Initializes a CongURE object.
|
||||
*
|
||||
* @param[in,out] c The CongURE object to initialize.
|
||||
* @param[in] ctx Context for callbacks specific to the
|
||||
* congestion control (such as a TCP PCB).
|
||||
* May be NULL.
|
||||
*/
|
||||
void (*init)(congure_snd_t *c, void *ctx);
|
||||
|
||||
/**
|
||||
* @brief Get current interval between messages for pacing.
|
||||
*
|
||||
* @param[in] c The CongURE state object.
|
||||
* @param[in] msg_size The size of the next message to send in
|
||||
* caller-defined unit.
|
||||
*
|
||||
* @return The current interval between sent messages in microseconds
|
||||
* when pacing supported by congestion control implementation.
|
||||
* @return -1, if pacing is not supported by the congestion control
|
||||
* implementation.
|
||||
*/
|
||||
int32_t (*inter_msg_interval)(congure_snd_t *c, unsigned msg_size);
|
||||
|
||||
/**
|
||||
* @brief Report that a message was sent.
|
||||
*
|
||||
* @param[in] c The CongURE state object.
|
||||
* @param[in] msg_size Size of the message in caller-defined unit.
|
||||
*/
|
||||
void (*report_msg_sent)(congure_snd_t *c, unsigned msg_size);
|
||||
|
||||
/**
|
||||
* @brief Report message as discarded.
|
||||
*
|
||||
* Discarded messages are not further taken into account for congestion
|
||||
* control.
|
||||
*
|
||||
* @param[in] c The CongURE state object.
|
||||
* @param[in] msg_size Size of the discarded message in caller-defined
|
||||
* unit.
|
||||
*/
|
||||
void (*report_msg_discarded)(congure_snd_t *c, unsigned msg_size);
|
||||
|
||||
/**
|
||||
* @brief Report that the ACKs for a collection of messages timed out.
|
||||
*
|
||||
* @note As many congestion control algorithms do not distinguish loss
|
||||
* and ACK timeout, this method and
|
||||
* congure_snd_t::report_msgs_lost need to have the same
|
||||
* signature so the same function can be used here
|
||||
*
|
||||
* @param[in] c The CongURE state object.
|
||||
* @param[in] msgs A collection of messages for which the ACK
|
||||
* timed out. The list must not be changed by the
|
||||
* method.
|
||||
*/
|
||||
void (*report_msgs_timeout)(congure_snd_t *c, congure_snd_msg_t *msgs);
|
||||
|
||||
/**
|
||||
* @brief Report that a collection of messages that is known to be lost.
|
||||
*
|
||||
* One indicator for a lost message may e.g. be the reception of an ACK of a
|
||||
* later sent packet, but not a ACK timeout (see
|
||||
* congure_snd_driver_t::report_msgs_timeout() for that).
|
||||
*
|
||||
* @note As many congestion control algorithms do not distinguish loss
|
||||
* and ACK timeout, this method and
|
||||
* congure_snd_t::report_msgs_timeout need to have the same
|
||||
* signature so the same function can be used here
|
||||
*
|
||||
* @param[in] c The CongURE state object.
|
||||
* @param[in] msgs A collection of messages that are known to
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @brief Report that the ACK for a message was received.
|
||||
*
|
||||
* @param[in] c The CongURE state object.
|
||||
* @param[in] msg The ACK'd message.
|
||||
* @param[in] ack The received ACK.
|
||||
*/
|
||||
void (*report_msg_acked)(congure_snd_t *c, congure_snd_msg_t *msg,
|
||||
congure_snd_ack_t *ack);
|
||||
|
||||
/**
|
||||
* @brief Report that "congestion encountered" CE signals were received
|
||||
* for a message by means of explicit congestion notification
|
||||
* (ECN).
|
||||
*
|
||||
* @param[in] c The CongURE state object.
|
||||
* @param[in] time Timestamp in milliseconds of the message the CE
|
||||
* event occurred for was sent.
|
||||
*/
|
||||
void (*report_ecn_ce)(congure_snd_t *c, ztimer_now_t time);
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONGURE_H */
|
||||
/** @} */
|
32
sys/include/congure/config.h
Normal file
32
sys/include/congure/config.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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_config CongURE compile time configuration
|
||||
* @ingroup sys_congure
|
||||
* @ingroup config
|
||||
* @brief Configuration for congestion control using @ref sys_congure
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* @author Martine S. Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef CONGURE_CONFIG_H
|
||||
#define CONGURE_CONFIG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONGURE_CONFIG_H */
|
||||
/** @} */
|
164
sys/include/congure/mock.h
Normal file
164
sys/include/congure/mock.h
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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_mock CongURE mock implementation
|
||||
* @ingroup sys_congure
|
||||
* @brief A mock for testing @ref sys_congure
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* @author Martine S. Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef CONGURE_MOCK_H
|
||||
#define CONGURE_MOCK_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "congure.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief A mock CongURE state object
|
||||
*
|
||||
* @extends congure_snd_t
|
||||
*/
|
||||
typedef struct {
|
||||
congure_snd_t super; /**< see @ref congure_snd_t */
|
||||
/**
|
||||
* @brief How often was the congure_snd_driver_t::init() method called?
|
||||
*/
|
||||
uint8_t init_calls;
|
||||
/**
|
||||
* @brief How often was the congure_snd_driver_t::inter_msg_interval()
|
||||
* method called?
|
||||
*/
|
||||
uint8_t inter_msg_interval_calls;
|
||||
/**
|
||||
* @brief How often was the congure_snd_driver_t::report_msg_sent()
|
||||
* method called?
|
||||
*/
|
||||
uint8_t report_msg_sent_calls;
|
||||
/**
|
||||
* @brief How often was the congure_snd_driver_t::report_msg_discarded()
|
||||
* method called?
|
||||
*/
|
||||
uint8_t report_msg_discarded_calls;
|
||||
/**
|
||||
* @brief How often was the congure_snd_driver_t::report_msgs_timeout()
|
||||
* method called?
|
||||
*/
|
||||
uint8_t report_msgs_timeout_calls;
|
||||
/**
|
||||
* @brief How often was the congure_snd_driver_t::report_msgs_lost()
|
||||
* method called?
|
||||
*/
|
||||
uint8_t report_msgs_lost_calls;
|
||||
/**
|
||||
* @brief How often was the congure_snd_driver_t::report_msg_acked()
|
||||
* method called?
|
||||
*/
|
||||
uint8_t report_msg_acked_calls;
|
||||
/**
|
||||
* @brief How often was the congure_snd_driver_t::report_ecn_ce()
|
||||
* method called?
|
||||
*/
|
||||
uint8_t report_ecn_ce_calls;
|
||||
/**
|
||||
* @brief What were the arguments for the last
|
||||
* congure_snd_driver_t::init() call?
|
||||
*/
|
||||
struct {
|
||||
congure_snd_t *c; /**< The CongURE object to initialize */
|
||||
void *ctx; /**< Context for callbacks specific to CC */
|
||||
} init_args;
|
||||
/**
|
||||
* @brief What were the arguments for the last
|
||||
* congure_snd_driver_t::inter_msg_interval() call?
|
||||
*/
|
||||
struct {
|
||||
congure_snd_t *c; /**< The CongURE state object */
|
||||
unsigned msg_size; /**< The size of the next message to send */
|
||||
} inter_msg_interval_args;
|
||||
/**
|
||||
* @brief What were the arguments for the last
|
||||
* congure_snd_driver_t::report_msg_sent() call?
|
||||
*/
|
||||
struct {
|
||||
congure_snd_t *c; /**< The CongURE state object */
|
||||
unsigned msg_size; /**< Size of the message */
|
||||
} report_msg_sent_args;
|
||||
/**
|
||||
* @brief What were the arguments for the last
|
||||
* congure_snd_driver_t::report_msg_discarded() call?
|
||||
*/
|
||||
struct {
|
||||
congure_snd_t *c; /**< The CongURE state object */
|
||||
unsigned msg_size; /**< Size of the message */
|
||||
} report_msg_discarded_args;
|
||||
/**
|
||||
* @brief What were the arguments for the last
|
||||
* congure_snd_driver_t::report_msgs_timeout() call?
|
||||
*/
|
||||
struct {
|
||||
congure_snd_t *c; /**< The CongURE state object */
|
||||
/**
|
||||
* @brief A collection of messages for which the ACK timed out
|
||||
*/
|
||||
congure_snd_msg_t *msgs;
|
||||
} report_msgs_timeout_args;
|
||||
/**
|
||||
* @brief What were the arguments for the last
|
||||
* congure_snd_driver_t::report_msgs_lost() call?
|
||||
*/
|
||||
struct {
|
||||
congure_snd_t *c; /**< The CongURE state object */
|
||||
/**
|
||||
* @brief A collection of messages that are known to be lost
|
||||
*/
|
||||
congure_snd_msg_t *msgs;
|
||||
} report_msgs_lost_args;
|
||||
/**
|
||||
* @brief What were the arguments for the last
|
||||
* congure_snd_driver_t::report_msg_acked() call?
|
||||
*/
|
||||
struct {
|
||||
congure_snd_t *c; /**< The CongURE state object */
|
||||
congure_snd_msg_t *msg; /**< The ACK'd message */
|
||||
congure_snd_ack_t *ack; /**< The received ACK */
|
||||
} report_msg_acked_args;
|
||||
/**
|
||||
* @brief What were the arguments for the last
|
||||
* congure_snd_driver_t::report_ecn_ce() call?
|
||||
*/
|
||||
struct {
|
||||
congure_snd_t *c; /**< The CongURE state object */
|
||||
/**
|
||||
* @brief Timestamp of the message the CE event occurred for was sent
|
||||
*/
|
||||
ztimer_now_t time;
|
||||
} report_ecn_ce_args;
|
||||
} congure_mock_snd_t;
|
||||
|
||||
/**
|
||||
* @brief Sets up the driver for CongURE mock object
|
||||
*
|
||||
* @param[in] c A CongURE mock object
|
||||
*/
|
||||
void congure_mock_snd_setup(congure_mock_snd_t *c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONGURE_MOCK_H */
|
||||
/** @} */
|
319
sys/include/congure/test.h
Normal file
319
sys/include/congure/test.h
Normal 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 */
|
||||
/** @} */
|
@ -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}
|
||||
};
|
||||
|
29
tests/congure_test/Makefile
Normal file
29
tests/congure_test/Makefile
Normal file
@ -0,0 +1,29 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
USEMODULE += congure_mock
|
||||
USEMODULE += congure_test
|
||||
USEMODULE += fmt
|
||||
USEMODULE += shell
|
||||
USEMODULE += shell_commands
|
||||
|
||||
INCLUDES += -I$(CURDIR)
|
||||
|
||||
# Use a terminal that does not introduce extra characters into the stream.
|
||||
RIOT_TERMINAL ?= socat
|
||||
|
||||
# As there is an 'app.config' we want to explicitly disable Kconfig by setting
|
||||
# the variable to empty
|
||||
SHOULD_RUN_KCONFIG ?=
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
ifndef CONFIG_SHELL_NO_ECHO
|
||||
CFLAGS += -DCONFIG_SHELL_NO_ECHO=1
|
||||
endif
|
||||
|
||||
ifndef CONFIG_CONGURE_TEST_LOST_MSG_POOL_SIZE
|
||||
CFLAGS += -DCONFIG_CONGURE_TEST_LOST_MSG_POOL_SIZE=4
|
||||
export LOST_MSG_POOL_SIZE=4
|
||||
else
|
||||
export LOST_MSG_POOL_SIZE=$(CONFIG_CONGURE_TEST_LOST_MSG_POOL_SIZE)
|
||||
endif
|
7
tests/congure_test/Makefile.ci
Normal file
7
tests/congure_test/Makefile.ci
Normal file
@ -0,0 +1,7 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
arduino-duemilanove \
|
||||
arduino-leonardo \
|
||||
arduino-nano \
|
||||
arduino-uno \
|
||||
atmega328p \
|
||||
#
|
40
tests/congure_test/README.md
Normal file
40
tests/congure_test/README.md
Normal file
@ -0,0 +1,40 @@
|
||||
Basic tests for the CongURE API
|
||||
===============================
|
||||
|
||||
This test tests the `congure_test` test framework used for the other CongURE
|
||||
tests.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
The test requires an up-to-date version of `riotctrl` with `rapidjson` support:
|
||||
|
||||
```console
|
||||
$ pip install --upgrade riotctrl[rapidjson]
|
||||
```
|
||||
|
||||
Then simply run the application using:
|
||||
|
||||
```console
|
||||
$ BOARD="<board>" make flash test
|
||||
```
|
||||
|
||||
It can also executed with pytest:
|
||||
|
||||
```console
|
||||
$ pytest tests/01-run.py
|
||||
```
|
||||
|
||||
Note that this only works from within the directory of the test, so if you are
|
||||
somewhere else, use
|
||||
|
||||
```console
|
||||
$ cd tests/congure_test
|
||||
```
|
||||
|
||||
first to change into that.
|
||||
|
||||
Expected result
|
||||
---------------
|
||||
|
||||
The application's test script passes without error code.
|
2
tests/congure_test/app.config
Normal file
2
tests/congure_test/app.config
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_KCONFIG_USEMODULE_SHELL=y
|
||||
CONFIG_SHELL_NO_ECHO=y
|
7
tests/congure_test/app.config.test
Normal file
7
tests/congure_test/app.config.test
Normal file
@ -0,0 +1,7 @@
|
||||
CONFIG_MODULE_CONGURE=y
|
||||
CONFIG_MODULE_CONGURE_MOCK=y
|
||||
CONFIG_MODULE_CONGURE_TEST=y
|
||||
CONFIG_MODULE_FMT=y
|
||||
CONFIG_MODULE_SHELL=y
|
||||
CONFIG_MODULE_SHELL_COMMANDS=y
|
||||
CONFIG_SHELL_NO_ECHO=y
|
29
tests/congure_test/congure_impl.c
Normal file
29
tests/congure_test/congure_impl.c
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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 Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include "congure/mock.h"
|
||||
|
||||
#include "congure_impl.h"
|
||||
|
||||
int congure_test_snd_setup(congure_test_snd_t *c, unsigned id)
|
||||
{
|
||||
if (id > 0) {
|
||||
return -1;
|
||||
}
|
||||
congure_mock_snd_setup(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
34
tests/congure_test/congure_impl.h
Normal file
34
tests/congure_test/congure_impl.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef CONGURE_IMPL_H
|
||||
#define CONGURE_IMPL_H
|
||||
|
||||
#include "congure/mock.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef congure_mock_snd_t congure_test_snd_t;
|
||||
|
||||
int congure_test_snd_setup(congure_test_snd_t *c, unsigned id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONGURE_IMPL_H */
|
||||
/** @} */
|
333
tests/congure_test/main.c
Normal file
333
tests/congure_test/main.c
Normal file
@ -0,0 +1,333 @@
|
||||
/*
|
||||
* 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 <stdlib.h>
|
||||
|
||||
#include "clist.h"
|
||||
#include "congure/test.h"
|
||||
#include "fmt.h"
|
||||
#include "shell.h"
|
||||
|
||||
#include "congure_impl.h"
|
||||
|
||||
static int _json_statham(int argc, char **argv);
|
||||
|
||||
static congure_test_snd_t _congure_state;
|
||||
static const shell_command_t shell_commands[] = {
|
||||
{ "state", "Prints current CongURE state object as JSON", _json_statham },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char line_buf[SHELL_DEFAULT_BUFSIZE];
|
||||
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
congure_test_snd_t *congure_test_get_state(void)
|
||||
{
|
||||
return &_congure_state;
|
||||
}
|
||||
|
||||
static int _print_congure_snd_msg(clist_node_t *node, void *arg)
|
||||
{
|
||||
congure_snd_msg_t *msg = (congure_snd_msg_t *)node;
|
||||
|
||||
(void)arg;
|
||||
print_str("{");
|
||||
|
||||
print_str("\"send_time\":");
|
||||
print_u64_dec(msg->send_time);
|
||||
print_str(",");
|
||||
|
||||
print_str("\"size\":");
|
||||
print_u32_dec(msg->size);
|
||||
print_str(",");
|
||||
|
||||
print_str("\"resends\":");
|
||||
print_u32_dec(msg->resends);
|
||||
|
||||
print_str("},");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _print_congure_snd_ack(congure_snd_ack_t *ack)
|
||||
{
|
||||
print_str("{");
|
||||
|
||||
print_str("\"recv_time\":");
|
||||
print_u64_dec(ack->recv_time);
|
||||
print_str(",");
|
||||
|
||||
print_str("\"id\":");
|
||||
print_u32_dec(ack->id);
|
||||
print_str(",");
|
||||
|
||||
print_str("\"size\":");
|
||||
print_u32_dec(ack->size);
|
||||
print_str(",");
|
||||
|
||||
print_str("\"clean\":");
|
||||
print_str(ack->clean ? "true" : "false");
|
||||
print_str(",");
|
||||
|
||||
print_str("\"wnd\":");
|
||||
print_u32_dec(ack->wnd);
|
||||
print_str(",");
|
||||
|
||||
print_str("\"delay\":");
|
||||
print_u32_dec(ack->delay);
|
||||
print_str(",");
|
||||
|
||||
print_str("},");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _print_init_state(void)
|
||||
{
|
||||
print_str("\"init\":{");
|
||||
|
||||
print_str("\"calls\":");
|
||||
print_u32_dec(_congure_state.init_calls);
|
||||
print_str(",");
|
||||
|
||||
print_str("\"last_args\":{");
|
||||
|
||||
print_str("\"c\":\"0x");
|
||||
print_u32_hex((intptr_t)_congure_state.init_args.c);
|
||||
print_str("\",");
|
||||
|
||||
print_str("\"ctx\":\"0x");
|
||||
print_u32_hex((intptr_t)_congure_state.init_args.ctx);
|
||||
print_str("\"");
|
||||
|
||||
print_str("}");
|
||||
|
||||
print_str("},");
|
||||
}
|
||||
|
||||
static void _print_inter_msg_interval_state(void)
|
||||
{
|
||||
print_str("\"inter_msg_interval\":{");
|
||||
|
||||
print_str("\"calls\":");
|
||||
print_u32_dec(_congure_state.inter_msg_interval_calls);
|
||||
print_str(",");
|
||||
|
||||
print_str("\"last_args\":{");
|
||||
|
||||
print_str("\"c\":\"0x");
|
||||
print_u32_hex((intptr_t)_congure_state.inter_msg_interval_args.c);
|
||||
print_str("\",");
|
||||
|
||||
print_str("\"msg_size\":");
|
||||
print_u32_dec(_congure_state.inter_msg_interval_args.msg_size);
|
||||
print_str("");
|
||||
|
||||
print_str("}");
|
||||
|
||||
print_str("},");
|
||||
}
|
||||
|
||||
static void _print_report_msg_sent_state(void)
|
||||
{
|
||||
print_str("\"report_msg_sent\":{");
|
||||
|
||||
print_str("\"calls\":");
|
||||
print_u32_dec(_congure_state.report_msg_sent_calls);
|
||||
print_str(",");
|
||||
|
||||
print_str("\"last_args\":{");
|
||||
|
||||
print_str("\"c\":\"0x");
|
||||
print_u32_hex((intptr_t)_congure_state.report_msg_sent_args.c);
|
||||
print_str("\",");
|
||||
|
||||
print_str("\"msg_size\":");
|
||||
print_u32_dec(_congure_state.report_msg_sent_args.msg_size);
|
||||
print_str("");
|
||||
|
||||
print_str("}");
|
||||
|
||||
print_str("},");
|
||||
}
|
||||
|
||||
static void _print_report_msg_discarded_state(void)
|
||||
{
|
||||
print_str("\"report_msg_discarded\":{");
|
||||
|
||||
print_str("\"calls\":");
|
||||
print_u32_dec(_congure_state.report_msg_discarded_calls);
|
||||
print_str(",");
|
||||
|
||||
print_str("\"last_args\":{");
|
||||
|
||||
print_str("\"c\":\"0x");
|
||||
print_u32_hex((intptr_t)_congure_state.report_msg_discarded_args.c);
|
||||
print_str("\",");
|
||||
|
||||
print_str("\"msg_size\":");
|
||||
print_u32_dec(_congure_state.report_msg_discarded_args.msg_size);
|
||||
print_str("");
|
||||
|
||||
print_str("}");
|
||||
|
||||
print_str("},");
|
||||
}
|
||||
|
||||
static void _print_report_msgs_timeout_state(void)
|
||||
{
|
||||
print_str("\"report_msgs_timeout\":{");
|
||||
|
||||
print_str("\"calls\":");
|
||||
print_u32_dec(_congure_state.report_msgs_timeout_calls);
|
||||
print_str(",");
|
||||
|
||||
print_str("\"last_args\":{");
|
||||
|
||||
print_str("\"c\":\"0x");
|
||||
print_u32_hex((intptr_t)_congure_state.report_msgs_timeout_args.c);
|
||||
print_str("\",");
|
||||
|
||||
print_str("\"msgs\":[");
|
||||
clist_foreach((clist_node_t *)&_congure_state.report_msgs_timeout_args.msgs,
|
||||
_print_congure_snd_msg, NULL);
|
||||
print_str("]");
|
||||
|
||||
print_str("}");
|
||||
|
||||
print_str("},");
|
||||
}
|
||||
|
||||
static void _print_report_msgs_lost_state(void)
|
||||
{
|
||||
print_str("\"report_msgs_lost\":{");
|
||||
|
||||
print_str("\"calls\":");
|
||||
print_u32_dec(_congure_state.report_msgs_lost_calls);
|
||||
print_str(",");
|
||||
|
||||
print_str("\"last_args\":{");
|
||||
|
||||
print_str("\"c\":\"0x");
|
||||
print_u32_hex((intptr_t)_congure_state.report_msgs_lost_args.c);
|
||||
print_str("\",");
|
||||
|
||||
print_str("\"msgs\":[");
|
||||
clist_foreach((clist_node_t *)&_congure_state.report_msgs_lost_args.msgs,
|
||||
_print_congure_snd_msg, NULL);
|
||||
print_str("]");
|
||||
|
||||
print_str("}");
|
||||
|
||||
print_str("},");
|
||||
}
|
||||
|
||||
static void _print_report_msg_acked_state(void)
|
||||
{
|
||||
print_str("\"report_msg_acked\":{");
|
||||
|
||||
print_str("\"calls\":");
|
||||
print_u32_dec(_congure_state.report_msg_acked_calls);
|
||||
print_str(",");
|
||||
|
||||
print_str("\"last_args\":{");
|
||||
|
||||
print_str("\"c\":\"0x");
|
||||
print_u32_hex((intptr_t)_congure_state.report_msg_acked_args.c);
|
||||
print_str("\",");
|
||||
|
||||
assert((_congure_state.report_msg_acked_args.msg == NULL) ||
|
||||
(_congure_state.report_msg_acked_args.msg->super.next == NULL));
|
||||
print_str("\"msg\":");
|
||||
if (_congure_state.report_msg_acked_args.msg) {
|
||||
_print_congure_snd_msg(
|
||||
(clist_node_t *)_congure_state.report_msg_acked_args.msg, NULL
|
||||
);
|
||||
}
|
||||
else {
|
||||
print_str("null,");
|
||||
}
|
||||
|
||||
print_str("\"ack\":");
|
||||
if (_congure_state.report_msg_acked_args.ack) {
|
||||
_print_congure_snd_ack(_congure_state.report_msg_acked_args.ack);
|
||||
}
|
||||
else {
|
||||
print_str("null");
|
||||
}
|
||||
|
||||
print_str("}");
|
||||
|
||||
print_str("},");
|
||||
}
|
||||
|
||||
static void _print_report_ecn_ce_state(void)
|
||||
{
|
||||
print_str("\"report_ecn_ce\":{");
|
||||
|
||||
print_str("\"calls\":");
|
||||
print_u32_dec(_congure_state.report_ecn_ce_calls);
|
||||
print_str(",");
|
||||
|
||||
print_str("\"last_args\":{");
|
||||
|
||||
print_str("\"c\":\"0x");
|
||||
print_u32_hex((intptr_t)_congure_state.report_ecn_ce_args.c);
|
||||
print_str("\",");
|
||||
|
||||
print_str("\"time\":");
|
||||
print_u64_dec((intptr_t)_congure_state.report_ecn_ce_args.time);
|
||||
print_str(",");
|
||||
|
||||
print_str("}");
|
||||
|
||||
print_str("},");
|
||||
}
|
||||
|
||||
static int _json_statham(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
print_str("{");
|
||||
print_str("\"driver\":\"0x");
|
||||
print_u32_hex((intptr_t)_congure_state.super.driver);
|
||||
print_str("\",");
|
||||
|
||||
print_str("\"ctx\":\"0x");
|
||||
print_u32_hex((intptr_t)_congure_state.super.ctx);
|
||||
print_str("\",");
|
||||
|
||||
print_str("\"cwnd\":");
|
||||
print_u32_dec(_congure_state.super.cwnd);
|
||||
print_str(",");
|
||||
|
||||
_print_init_state();
|
||||
_print_inter_msg_interval_state();
|
||||
_print_report_msg_sent_state();
|
||||
_print_report_msg_discarded_state();
|
||||
_print_report_msgs_timeout_state();
|
||||
_print_report_msgs_lost_state();
|
||||
_print_report_msg_acked_state();
|
||||
_print_report_ecn_ce_state();
|
||||
|
||||
print_str("}\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
450
tests/congure_test/tests/01-run.py
Executable file
450
tests/congure_test/tests/01-run.py
Executable file
@ -0,0 +1,450 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
# 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.
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from riotctrl.ctrl import RIOTCtrl
|
||||
from riotctrl.shell import ShellInteraction
|
||||
from riotctrl.shell.json import RapidJSONShellInteractionParser, rapidjson
|
||||
|
||||
|
||||
class TestCongUREBase(unittest.TestCase):
|
||||
DEBUG = False
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.ctrl = RIOTCtrl()
|
||||
cls.ctrl.start_term()
|
||||
if cls.DEBUG:
|
||||
cls.ctrl.term.logfile = sys.stdout
|
||||
cls.ctrl.reset()
|
||||
cls.shell = ShellInteraction(cls.ctrl)
|
||||
cls.json_parser = RapidJSONShellInteractionParser()
|
||||
cls.json_parser.set_parser_args(
|
||||
parse_mode=rapidjson.PM_TRAILING_COMMAS
|
||||
)
|
||||
cls.logger = logging.getLogger(cls.__name__)
|
||||
if cls.DEBUG:
|
||||
cls.logger.setLevel(logging.DEBUG)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.ctrl.stop_term()
|
||||
|
||||
def setUp(self):
|
||||
self.shell.cmd('cong_clear')
|
||||
|
||||
def exec_cmd(self, cmd, timeout=-1, async_=False):
|
||||
res = self.shell.cmd(cmd, timeout, async_)
|
||||
self.logger.debug(repr(res))
|
||||
if res.strip():
|
||||
return self.json_parser.parse(res)
|
||||
return None
|
||||
|
||||
|
||||
class TestCongUREWithoutSetup(TestCongUREBase):
|
||||
def test_no_setup(self):
|
||||
state = self.exec_cmd('state')
|
||||
self.assertEqual(state, {
|
||||
'driver': '0x00000000',
|
||||
'ctx': '0x00000000',
|
||||
'cwnd': 0,
|
||||
'init': {
|
||||
'calls': 0,
|
||||
'last_args': {
|
||||
'c': '0x00000000',
|
||||
'ctx': '0x00000000',
|
||||
},
|
||||
},
|
||||
'inter_msg_interval': {
|
||||
'calls': 0,
|
||||
'last_args': {
|
||||
'c': '0x00000000',
|
||||
'msg_size': 0,
|
||||
},
|
||||
},
|
||||
'report_msg_sent': {
|
||||
'calls': 0,
|
||||
'last_args': {
|
||||
'c': '0x00000000',
|
||||
'msg_size': 0,
|
||||
},
|
||||
},
|
||||
'report_msg_discarded': {
|
||||
'calls': 0,
|
||||
'last_args': {
|
||||
'c': '0x00000000',
|
||||
'msg_size': 0,
|
||||
},
|
||||
},
|
||||
'report_msgs_timeout': {
|
||||
'calls': 0,
|
||||
'last_args': {
|
||||
'c': '0x00000000',
|
||||
'msgs': [],
|
||||
},
|
||||
},
|
||||
'report_msgs_lost': {
|
||||
'calls': 0,
|
||||
'last_args': {
|
||||
'c': '0x00000000',
|
||||
'msgs': [],
|
||||
},
|
||||
},
|
||||
'report_msg_acked': {
|
||||
'calls': 0,
|
||||
'last_args': {
|
||||
'c': '0x00000000',
|
||||
'msg': None,
|
||||
'ack': None,
|
||||
},
|
||||
},
|
||||
'report_ecn_ce': {
|
||||
'calls': 0,
|
||||
'last_args': {
|
||||
'c': '0x00000000',
|
||||
'time': 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
def test_setup_invalid_id(self):
|
||||
self.test_no_setup()
|
||||
res = self.exec_cmd('cong_setup abcd')
|
||||
self.assertEqual(res, {'error': "`id` expected to be integer"})
|
||||
res = self.exec_cmd('cong_setup 1')
|
||||
self.assertEqual(res, {'error': "`id` is invalid"})
|
||||
|
||||
def test_setup(self):
|
||||
self.test_no_setup()
|
||||
res = self.exec_cmd('cong_setup')
|
||||
# res['success'] is 32-bit hexadecimal number greater zero, representing
|
||||
# the pointer to the congure state object
|
||||
self.assertGreater(int(res['success'], base=16), 0)
|
||||
self.assertEqual(len(res['success']), len('0x00000000'))
|
||||
res = self.exec_cmd('cong_setup 0')
|
||||
# res['success'] is 32-bit hexadecimal number greater zero, representing
|
||||
# the pointer to the congure state object
|
||||
self.assertGreater(int(res['success'], base=16), 0)
|
||||
self.assertEqual(len(res['success']), len('0x00000000'))
|
||||
|
||||
def test_init_wo_setup(self):
|
||||
res = self.exec_cmd('cong_init 0x12345')
|
||||
self.assertEqual(res, {'error': "State object not set up"})
|
||||
|
||||
def test_inter_msg_interval_wo_setup(self):
|
||||
res = self.exec_cmd('cong_imi 689')
|
||||
self.assertEqual(res, {'error': "State object not set up"})
|
||||
|
||||
def test_report_wo_setup(self):
|
||||
res = self.exec_cmd('cong_report')
|
||||
self.assertEqual(res, {'error': "State object not set up"})
|
||||
|
||||
|
||||
class TestCongUREWithSetup(TestCongUREBase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
res = self.exec_cmd('cong_setup')
|
||||
self.congure_state_ptr = int(res['success'], base=16)
|
||||
|
||||
def test_init_no_args(self):
|
||||
res = self.exec_cmd('cong_init')
|
||||
self.assertEqual(res, {'error': "`ctx` argument expected"})
|
||||
|
||||
def test_init_arg_not_hex(self):
|
||||
res = self.exec_cmd('cong_init foobar')
|
||||
self.assertEqual(res, {'error': "`ctx` expected to be hex"})
|
||||
res = self.exec_cmd('cong_init 123456')
|
||||
self.assertEqual(res, {'error': "`ctx` expected to be hex"})
|
||||
|
||||
def test_init_success(self):
|
||||
ctx = 0x12345
|
||||
res = self.exec_cmd('cong_init 0x{ctx:x}'.format(ctx=ctx))
|
||||
self.assertIsNone(res['success'])
|
||||
res = self.exec_cmd('state')
|
||||
self.assertEqual(res['init']['calls'], 1)
|
||||
self.assertEqual(int(res['init']['last_args']['c'], base=16),
|
||||
self.congure_state_ptr)
|
||||
self.assertEqual(int(res['init']['last_args']['ctx'], base=16),
|
||||
ctx)
|
||||
|
||||
def test_inter_msg_interval_no_args(self):
|
||||
res = self.exec_cmd('cong_imi')
|
||||
self.assertEqual(res, {'error': '`msg_size` argument expected'})
|
||||
|
||||
def test_inter_msg_interval_not_int(self):
|
||||
res = self.exec_cmd('cong_imi foobar')
|
||||
self.assertEqual(res, {'error': '`msg_size` expected to be integer'})
|
||||
|
||||
def test_inter_msg_interval_success(self):
|
||||
msg_size = 521
|
||||
res = self.exec_cmd('cong_imi {msg_size}'.format(msg_size=msg_size))
|
||||
assert res == {'success': -1}
|
||||
res = self.exec_cmd('state')
|
||||
self.assertEqual(res['inter_msg_interval']['calls'], 1)
|
||||
self.assertEqual(int(res['inter_msg_interval']['last_args']['c'],
|
||||
base=16),
|
||||
self.congure_state_ptr)
|
||||
self.assertEqual(res['inter_msg_interval']['last_args']['msg_size'],
|
||||
msg_size)
|
||||
|
||||
def test_report_no_args(self):
|
||||
res = self.exec_cmd('cong_report foobar')
|
||||
self.assertEqual(res, {'error': 'Unknown command `foobar`'})
|
||||
|
||||
def test_report_unknown_command(self):
|
||||
res = self.exec_cmd('cong_report')
|
||||
self.assertEqual(res, {'error': 'No report command provided'})
|
||||
|
||||
def test_report_msg_sent_no_args(self):
|
||||
res = self.exec_cmd('cong_report msg_sent')
|
||||
self.assertEqual(res, {'error': '`msg_size` argument expected'})
|
||||
|
||||
def test_report_msg_sent_not_int(self):
|
||||
res = self.exec_cmd('cong_report msg_sent this one')
|
||||
self.assertEqual(res, {'error': '`msg_size` expected to be integer'})
|
||||
|
||||
def test_report_msg_sent_success(self):
|
||||
msg_size = 1234
|
||||
res = self.exec_cmd('cong_report msg_sent {msg_size}'
|
||||
.format(msg_size=msg_size))
|
||||
self.assertIsNone(res['success'])
|
||||
res = self.exec_cmd('state')
|
||||
self.assertEqual(res['report_msg_sent']['calls'], 1)
|
||||
self.assertEqual(int(res['report_msg_sent']['last_args']['c'],
|
||||
base=16),
|
||||
self.congure_state_ptr)
|
||||
self.assertEqual(res['report_msg_sent']['last_args']['msg_size'],
|
||||
msg_size)
|
||||
|
||||
def test_report_msg_discarded_no_args(self):
|
||||
res = self.exec_cmd('cong_report msg_discarded')
|
||||
self.assertEqual(res, {'error': "`msg_size` argument expected"})
|
||||
|
||||
def test_report_msg_discarded_not_int(self):
|
||||
res = self.exec_cmd('cong_report msg_discarded this one')
|
||||
self.assertEqual(res, {'error': "`msg_size` expected to be integer"})
|
||||
|
||||
def test_report_msg_discarded_success(self):
|
||||
msg_size = 1234
|
||||
res = self.exec_cmd('cong_report msg_discarded {msg_size}'
|
||||
.format(msg_size=msg_size))
|
||||
self.assertIsNone(res['success'])
|
||||
res = self.exec_cmd('state')
|
||||
self.assertEqual(res['report_msg_discarded']['calls'], 1)
|
||||
self.assertEqual(int(res['report_msg_discarded']['last_args']['c'],
|
||||
base=16),
|
||||
self.congure_state_ptr)
|
||||
self.assertEqual(res['report_msg_discarded']['last_args']['msg_size'],
|
||||
msg_size)
|
||||
|
||||
def _report_msgs_timeout_lost_acked_not_enough_args(self, cmd, exp_params):
|
||||
args = ""
|
||||
# gradually append more arguments but never get full set
|
||||
for i in range(len(exp_params) - 1):
|
||||
args += ' {}'.format(i + 1)
|
||||
res = self.exec_cmd('cong_report {cmd} {args}'
|
||||
.format(cmd=cmd, args=args))
|
||||
self.assertEqual(res, {
|
||||
'error': 'At least {} arguments {} expected'
|
||||
.format(len(exp_params),
|
||||
', '.join('`{}`'.format(p)
|
||||
for p in exp_params))
|
||||
})
|
||||
|
||||
def _report_msgs_timeout_lost_argc_not_mod_3(self, cmd):
|
||||
res = self.exec_cmd('cong_report {cmd} 1 2 3 4'.format(cmd=cmd))
|
||||
self.assertEqual(res, {
|
||||
'error': 'Number of arguments must be divisible by 3'
|
||||
})
|
||||
res = self.exec_cmd('cong_report {cmd} 1 2 3 4 5'.format(cmd=cmd))
|
||||
self.assertEqual(res, {
|
||||
'error': 'Number of arguments must be divisible by 3'
|
||||
})
|
||||
|
||||
def _report_msgs_timeout_lost_acked_args_not_int(self, cmd, exp_params):
|
||||
# generate list of arguments that are exp_params string parameters
|
||||
args = [chr(i + ord('a')) for i in range(len(exp_params))]
|
||||
if cmd != 'msg_acked':
|
||||
# and exp_params integer parameters for msgs_timeout and msgs_lost
|
||||
args += [str(i + len(exp_params)) for i in range(len(exp_params))]
|
||||
res = self.exec_cmd('cong_report {} {}'.format(cmd, ' '.join(args)))
|
||||
self.assertEqual(res, {
|
||||
'error': '`{}` expected to be integer'.format(exp_params[0])
|
||||
})
|
||||
# gradually transform all but the last string to integer and test again
|
||||
for i in range(len(exp_params) - 1):
|
||||
args[i] = str(i + 1)
|
||||
res = self.exec_cmd(
|
||||
'cong_report {} {}'.format(cmd, ' '.join(args))
|
||||
)
|
||||
self.assertEqual(res, {
|
||||
'error': '`{}` expected to be integer'
|
||||
.format(exp_params[i + 1])
|
||||
})
|
||||
|
||||
def _report_msgs_timeout_lost_exceed_msg_pool_size(self, cmd):
|
||||
# expected to be set by Makefile
|
||||
pool_size = int(os.environ.get('LOST_MSG_POOL_SIZE', 4))
|
||||
args = ' '.join('1' for _ in range(3 * pool_size))
|
||||
args += ' 1 1 1'
|
||||
res = self.exec_cmd('cong_report {cmd} {args}'
|
||||
.format(cmd=cmd, args=args))
|
||||
self.assertEqual(res, {
|
||||
'error': 'List element pool depleted'
|
||||
})
|
||||
|
||||
def _report_msgs_timeout_lost_success(self, cmd):
|
||||
msgs = [{'send_time': 76543, 'size': 1234, 'resends': 2},
|
||||
{'send_time': 5432, 'size': 987, 'resends': 32}]
|
||||
res = self.exec_cmd(
|
||||
'cong_report {cmd} '
|
||||
'{msgs[0][send_time]} {msgs[0][size]} {msgs[0][resends]} '
|
||||
'{msgs[1][send_time]} {msgs[1][size]} {msgs[1][resends]}'
|
||||
.format(cmd=cmd, msgs=msgs)
|
||||
)
|
||||
self.assertIsNone(res['success'])
|
||||
res = self.exec_cmd('state')
|
||||
self.assertEqual(res['report_{}'.format(cmd)]['calls'], 1)
|
||||
self.assertEqual(int(res['report_{}'.format(cmd)]['last_args']['c'],
|
||||
base=16),
|
||||
self.congure_state_ptr)
|
||||
self.assertEqual(res['report_{}'.format(cmd)]['last_args']['msgs'],
|
||||
msgs)
|
||||
|
||||
def test_report_msgs_timeout_not_enough_args(self):
|
||||
self._report_msgs_timeout_lost_acked_not_enough_args(
|
||||
'msgs_timeout', ['msg_send_time', 'msg_size', 'msg_resends']
|
||||
)
|
||||
|
||||
def test_report_msgs_timeout_argc_not_mod_3(self):
|
||||
self._report_msgs_timeout_lost_argc_not_mod_3('msgs_timeout')
|
||||
|
||||
def test_report_msgs_timeout_args_not_int(self):
|
||||
self._report_msgs_timeout_lost_acked_args_not_int(
|
||||
'msgs_timeout', ['msg_send_time', 'msg_size', 'msg_resends']
|
||||
)
|
||||
|
||||
def test_report_msgs_timeout_exceed_msg_pool_size(self):
|
||||
self._report_msgs_timeout_lost_exceed_msg_pool_size('msgs_timeout')
|
||||
|
||||
def test_report_msgs_timeout_success(self):
|
||||
self._report_msgs_timeout_lost_success('msgs_timeout')
|
||||
|
||||
def test_report_msgs_lost_not_enough_args(self):
|
||||
self._report_msgs_timeout_lost_acked_not_enough_args(
|
||||
'msgs_lost', ['msg_send_time', 'msg_size', 'msg_resends']
|
||||
)
|
||||
|
||||
def test_report_msgs_lost_argc_not_mod_3(self):
|
||||
self._report_msgs_timeout_lost_argc_not_mod_3('msgs_lost')
|
||||
|
||||
def test_report_msgs_lost_msg_args_not_int(self):
|
||||
self._report_msgs_timeout_lost_acked_args_not_int(
|
||||
'msgs_lost', ['msg_send_time', 'msg_size', 'msg_resends']
|
||||
)
|
||||
|
||||
def test_report_msgs_lost_exceed_msg_pool_size(self):
|
||||
self._report_msgs_timeout_lost_exceed_msg_pool_size('msgs_lost')
|
||||
|
||||
def test_report_msgs_lost_success(self):
|
||||
self._report_msgs_timeout_lost_success('msgs_lost')
|
||||
|
||||
def test_report_msg_acked_not_enough_args(self):
|
||||
self._report_msgs_timeout_lost_acked_not_enough_args(
|
||||
'msg_acked', [
|
||||
'msg_send_time', 'msg_size', 'msg_resends', 'ack_recv_time',
|
||||
'ack_id', 'ack_size', 'ack_clean', 'ack_wnd', 'ack_delay',
|
||||
]
|
||||
|
||||
)
|
||||
|
||||
def test_report_msg_acked_msg_args_not_int(self):
|
||||
self._report_msgs_timeout_lost_acked_args_not_int(
|
||||
'msg_acked', [
|
||||
'msg_send_time', 'msg_size', 'msg_resends', 'ack_recv_time',
|
||||
'ack_id', 'ack_size', 'ack_clean', 'ack_wnd', 'ack_delay',
|
||||
]
|
||||
)
|
||||
|
||||
def test_report_msg_acked_wnd_not_wnd_size(self):
|
||||
msg = {'send_time': 12345, 'size': 456, 'resends': 0}
|
||||
ack = {'recv_time': 12432, 'id': 18846, 'size': 12,
|
||||
'clean': 1, 'wnd': (1 << 16) + 7642, 'delay': 1235}
|
||||
res = self.exec_cmd(
|
||||
'cong_report msg_acked '
|
||||
'{msg[send_time]} {msg[size]} {msg[resends]} '
|
||||
'{ack[recv_time]} {ack[id]} {ack[size]} {ack[clean]} '
|
||||
'{ack[wnd]} {ack[delay]}'
|
||||
.format(msg=msg, ack=ack)
|
||||
)
|
||||
self.assertEqual(res, {
|
||||
'error': '`ack_wnd` not 16 bit wide'
|
||||
})
|
||||
|
||||
def test_report_msg_acked_delay_not_uint16(self):
|
||||
msg = {'send_time': 12345, 'size': 456, 'resends': 0}
|
||||
ack = {'recv_time': 12432, 'id': 18846, 'size': 12,
|
||||
'clean': 1, 'wnd': 7642, 'delay': (1 << 16) + 1235}
|
||||
res = self.exec_cmd(
|
||||
'cong_report msg_acked '
|
||||
'{msg[send_time]} {msg[size]} {msg[resends]} '
|
||||
'{ack[recv_time]} {ack[id]} {ack[size]} {ack[clean]} '
|
||||
'{ack[wnd]} {ack[delay]}'
|
||||
.format(msg=msg, ack=ack)
|
||||
)
|
||||
self.assertEqual(res, {
|
||||
'error': '`ack_delay` not 16 bit wide'
|
||||
})
|
||||
|
||||
def test_report_msg_acked_success(self):
|
||||
msg = {'send_time': 12345, 'size': 456, 'resends': 0}
|
||||
ack = {'recv_time': 12432, 'id': 18846, 'size': 12,
|
||||
'clean': 1, 'wnd': 742, 'delay': 1235}
|
||||
res = self.exec_cmd(
|
||||
'cong_report msg_acked '
|
||||
'{msg[send_time]} {msg[size]} {msg[resends]} '
|
||||
'{ack[recv_time]} {ack[id]} {ack[size]} {ack[clean]} '
|
||||
'{ack[wnd]} {ack[delay]}'
|
||||
.format(msg=msg, ack=ack)
|
||||
)
|
||||
self.assertIsNone(res['success'])
|
||||
res = self.exec_cmd('state')
|
||||
self.assertEqual(res['report_msg_acked']['calls'], 1)
|
||||
self.assertEqual(int(res['report_msg_acked']['last_args']['c'],
|
||||
base=16),
|
||||
self.congure_state_ptr)
|
||||
self.assertEqual(res['report_msg_acked']['last_args']['msg'], msg)
|
||||
self.assertEqual(res['report_msg_acked']['last_args']['ack'], ack)
|
||||
|
||||
def test_report_ecn_ce_no_args(self):
|
||||
res = self.exec_cmd('cong_report ecn_ce')
|
||||
self.assertEqual(res, {'error': '`time` argument expected'})
|
||||
|
||||
def test_report_ecn_ce_not_int(self):
|
||||
res = self.exec_cmd('cong_report ecn_ce this one')
|
||||
self.assertEqual(res, {'error': '`time` expected to be integer'})
|
||||
|
||||
def test_report_ecn_ce_success(self):
|
||||
time = 64352
|
||||
res = self.exec_cmd('cong_report ecn_ce {time}'.format(time=time))
|
||||
self.assertIsNone(res['success'])
|
||||
res = self.exec_cmd('state')
|
||||
self.assertEqual(res['report_ecn_ce']['calls'], 1)
|
||||
self.assertEqual(int(res['report_ecn_ce']['last_args']['c'],
|
||||
base=16),
|
||||
self.congure_state_ptr)
|
||||
self.assertEqual(res['report_ecn_ce']['last_args']['time'],
|
||||
time)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue
Block a user