mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-18 12:52:44 +01:00
tests: Initial import of congure_abe
tests
This commit is contained in:
parent
463317391b
commit
a4e7c93d8f
18
tests/congure_abe/Makefile
Normal file
18
tests/congure_abe/Makefile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
include ../Makefile.tests_common
|
||||||
|
|
||||||
|
USEMODULE += congure_abe
|
||||||
|
USEMODULE += congure_test
|
||||||
|
USEMODULE += fmt
|
||||||
|
USEMODULE += shell
|
||||||
|
|
||||||
|
INCLUDES += -I$(CURDIR)
|
||||||
|
|
||||||
|
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=6
|
||||||
|
endif
|
8
tests/congure_abe/Makefile.ci
Normal file
8
tests/congure_abe/Makefile.ci
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
BOARD_INSUFFICIENT_MEMORY := \
|
||||||
|
arduino-duemilanove \
|
||||||
|
arduino-leonardo \
|
||||||
|
arduino-nano \
|
||||||
|
arduino-uno \
|
||||||
|
atmega328p \
|
||||||
|
atmega328p-xplained-mini \
|
||||||
|
#
|
30
tests/congure_abe/README.md
Normal file
30
tests/congure_abe/README.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
Tests for the CongURE TCP ABE implementation
|
||||||
|
=============================================
|
||||||
|
|
||||||
|
This test tests the `congure_abe` implementation.
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected result
|
||||||
|
---------------
|
||||||
|
|
||||||
|
The application's test script passes without error code.
|
4
tests/congure_abe/app.config
Normal file
4
tests/congure_abe/app.config
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
CONFIG_KCONFIG_USEMODULE_CONGURE_TEST=y
|
||||||
|
CONFIG_KCONFIG_USEMODULE_SHELL=y
|
||||||
|
CONFIG_CONGURE_TEST_LOST_MSG_POOL_SIZE=6
|
||||||
|
CONFIG_SHELL_NO_ECHO=y
|
9
tests/congure_abe/app.config.test
Normal file
9
tests/congure_abe/app.config.test
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
CONFIG_MODULE_CONGURE=y
|
||||||
|
CONFIG_MODULE_CONGURE_ABE=y
|
||||||
|
CONFIG_MODULE_CONGURE_TEST=y
|
||||||
|
CONFIG_MODULE_FMT=y
|
||||||
|
CONFIG_MODULE_SEQ=y
|
||||||
|
CONFIG_MODULE_SHELL=y
|
||||||
|
|
||||||
|
CONFIG_CONGURE_TEST_LOST_MSG_POOL_SIZE=6
|
||||||
|
CONFIG_SHELL_NO_ECHO=y
|
76
tests/congure_abe/congure_impl.c
Normal file
76
tests/congure_abe/congure_impl.c
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* 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 <stdbool.h>
|
||||||
|
#include "kernel_defines.h"
|
||||||
|
|
||||||
|
#include "congure_impl.h"
|
||||||
|
|
||||||
|
static unsigned _fr_calls;
|
||||||
|
static bool _same_wnd_adv_res;
|
||||||
|
|
||||||
|
static void _fr(congure_abe_snd_t *c);
|
||||||
|
static bool _same_wnd_adv(congure_abe_snd_t *c, congure_snd_ack_t *ack);
|
||||||
|
|
||||||
|
static const congure_abe_snd_consts_t _consts[] = {
|
||||||
|
{
|
||||||
|
.reno = {
|
||||||
|
.fr = _fr,
|
||||||
|
.same_wnd_adv = _same_wnd_adv,
|
||||||
|
.init_mss = 1460,
|
||||||
|
.cwnd_lower = 1095,
|
||||||
|
.cwnd_upper = 2190,
|
||||||
|
.init_ssthresh = CONGURE_WND_SIZE_MAX,
|
||||||
|
.frthresh = 3,
|
||||||
|
},
|
||||||
|
.abe_multiplier_numerator = CONFIG_CONGURE_ABE_MULTIPLIER_NUMERATOR_DEFAULT,
|
||||||
|
.abe_multiplier_denominator = CONFIG_CONGURE_ABE_MULTIPLIER_DENOMINATOR_DEFAULT,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int congure_test_snd_setup(congure_test_snd_t *c, unsigned id)
|
||||||
|
{
|
||||||
|
if (id >= ARRAY_SIZE(_consts)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
_fr_calls = 0;
|
||||||
|
congure_abe_snd_setup(c, &_consts[id]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned congure_abe_test_get_fr_calls(void)
|
||||||
|
{
|
||||||
|
return _fr_calls;
|
||||||
|
}
|
||||||
|
|
||||||
|
void congure_abe_test_set_same_wnd_adv_res(bool value)
|
||||||
|
{
|
||||||
|
_same_wnd_adv_res = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _fr(congure_abe_snd_t *c)
|
||||||
|
{
|
||||||
|
(void)c;
|
||||||
|
_fr_calls++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _same_wnd_adv(congure_abe_snd_t *c, congure_snd_ack_t *ack)
|
||||||
|
{
|
||||||
|
(void)c;
|
||||||
|
(void)ack;
|
||||||
|
return _same_wnd_adv_res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
36
tests/congure_abe/congure_impl.h
Normal file
36
tests/congure_abe/congure_impl.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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/abe.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef congure_abe_snd_t congure_test_snd_t;
|
||||||
|
|
||||||
|
int congure_test_snd_setup(congure_test_snd_t *c, unsigned id);
|
||||||
|
unsigned congure_abe_test_get_fr_calls(void);
|
||||||
|
void congure_abe_test_set_same_wnd_adv_res(bool value);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* CONGURE_IMPL_H */
|
||||||
|
/** @} */
|
151
tests/congure_abe/main.c
Normal file
151
tests/congure_abe/main.c
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* 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 <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.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 int _set_cwnd(int argc, char **argv);
|
||||||
|
static int _get_fr_calls(int argc, char **argv);
|
||||||
|
static int _set_same_wnd_adv_res(int argc, char **argv);
|
||||||
|
|
||||||
|
static congure_abe_snd_t _congure_state;
|
||||||
|
static const shell_command_t shell_commands[] = {
|
||||||
|
{ "state", "Prints current CongURE state object as JSON", _json_statham },
|
||||||
|
{ "set_cwnd", "Set cwnd member for CongURE state object", _set_cwnd },
|
||||||
|
{ "get_ff_calls",
|
||||||
|
"Get the number of calls to fast_retransmit callback of CongURE state "
|
||||||
|
"object", _get_fr_calls },
|
||||||
|
{ "set_same_wnd_adv",
|
||||||
|
"Set the result for the same_window_advertised callback of CongURE state "
|
||||||
|
"object", _set_same_wnd_adv_res },
|
||||||
|
{ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PRINT_FIELD_PTR(obj_ptr, field) \
|
||||||
|
print_str("\"" #field "\":\"0x"); \
|
||||||
|
print_u32_hex((intptr_t)((obj_ptr)->field)); \
|
||||||
|
print_str("\",")
|
||||||
|
|
||||||
|
#define PRINT_FIELD_UINT(obj, field) \
|
||||||
|
print_str("\"" #field "\":"); \
|
||||||
|
print_u32_dec((obj).field); \
|
||||||
|
print_str(",")
|
||||||
|
|
||||||
|
static void _print_congure_abe_consts(const congure_abe_snd_consts_t *consts)
|
||||||
|
{
|
||||||
|
print_str("\"consts\":");
|
||||||
|
|
||||||
|
if (consts) {
|
||||||
|
print_str("{");
|
||||||
|
PRINT_FIELD_PTR(&consts->reno, fr);
|
||||||
|
PRINT_FIELD_PTR(&consts->reno, same_wnd_adv);
|
||||||
|
PRINT_FIELD_PTR(&consts->reno, ss_cwnd_inc);
|
||||||
|
PRINT_FIELD_PTR(&consts->reno, ca_cwnd_inc);
|
||||||
|
PRINT_FIELD_PTR(&consts->reno, fr_cwnd_dec);
|
||||||
|
PRINT_FIELD_UINT(consts->reno, init_mss);
|
||||||
|
PRINT_FIELD_UINT(consts->reno, cwnd_upper);
|
||||||
|
PRINT_FIELD_UINT(consts->reno, cwnd_lower);
|
||||||
|
PRINT_FIELD_UINT(consts->reno, init_ssthresh);
|
||||||
|
PRINT_FIELD_UINT(consts->reno, frthresh);
|
||||||
|
PRINT_FIELD_UINT(*consts, abe_multiplier_numerator);
|
||||||
|
PRINT_FIELD_UINT(*consts, abe_multiplier_denominator);
|
||||||
|
print_str("},");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print_str("null,");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _json_statham(int argc, char **argv)
|
||||||
|
{
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
print_str("{");
|
||||||
|
|
||||||
|
PRINT_FIELD_UINT(_congure_state.super, cwnd);
|
||||||
|
_print_congure_abe_consts(
|
||||||
|
(congure_abe_snd_consts_t *)_congure_state.consts
|
||||||
|
);
|
||||||
|
PRINT_FIELD_UINT(_congure_state, mss);
|
||||||
|
PRINT_FIELD_UINT(_congure_state, last_ack);
|
||||||
|
PRINT_FIELD_UINT(_congure_state, ssthresh);
|
||||||
|
PRINT_FIELD_UINT(_congure_state, in_flight_size);
|
||||||
|
PRINT_FIELD_UINT(_congure_state, dup_acks);
|
||||||
|
|
||||||
|
print_str("}\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _set_cwnd(int argc, char **argv)
|
||||||
|
{
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
if (argc < 2) {
|
||||||
|
print_str("{\"error\":\"`cwnd` argument expected\"}");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
tmp = scn_u32_dec(argv[1], strlen(argv[1]));
|
||||||
|
if (tmp > CONGURE_WND_SIZE_MAX) {
|
||||||
|
print_str("{\"error\":\"`ssthresh` not 16 bit wide\"}\n");
|
||||||
|
}
|
||||||
|
_congure_state.super.cwnd = (congure_wnd_size_t)tmp;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _get_fr_calls(int argc, char **argv)
|
||||||
|
{
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
|
print_str("{\"fr_calls\":");
|
||||||
|
print_u32_dec(congure_abe_test_get_fr_calls());
|
||||||
|
print_str("}\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _set_same_wnd_adv_res(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc < 2) {
|
||||||
|
print_str("{\"error\":\"`value` argument expected\"}");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
congure_abe_test_set_same_wnd_adv_res(
|
||||||
|
(bool)scn_u32_dec(argv[1], strlen(argv[1]))
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
332
tests/congure_abe/tests/01-run.py
Executable file
332
tests/congure_abe/tests/01-run.py
Executable file
@ -0,0 +1,332 @@
|
|||||||
|
#! /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 sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from riotctrl.ctrl import RIOTCtrl
|
||||||
|
from riotctrl.shell.json import RapidJSONShellInteractionParser, rapidjson
|
||||||
|
|
||||||
|
from riotctrl_shell.congure_test import CongureTest
|
||||||
|
|
||||||
|
|
||||||
|
class TestCongUREBase(unittest.TestCase):
|
||||||
|
# pylint: disable=too-many-public-methods
|
||||||
|
# it's just one more ...
|
||||||
|
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 = CongureTest(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.clear()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.shell.msgs_reset()
|
||||||
|
|
||||||
|
def _parse(self, res):
|
||||||
|
self.logger.debug(res)
|
||||||
|
if res.strip():
|
||||||
|
return self.json_parser.parse(res)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def exec_cmd(self, cmd, timeout=-1, async_=False):
|
||||||
|
res = self.shell.cmd(cmd, timeout, async_)
|
||||||
|
return self._parse(res)
|
||||||
|
|
||||||
|
def assertSlowStart(self, state):
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
# trying to be in line with `unittest`
|
||||||
|
"""
|
||||||
|
> The slow start algorithm is used when cwnd < ssthresh, while the
|
||||||
|
> congestion avoidance algorithm is used when cwnd > ssthresh. When
|
||||||
|
> cwnd and ssthresh are equal, the sender may use either slow start or
|
||||||
|
> congestion avoidance.
|
||||||
|
"""
|
||||||
|
self.assertLess(state['cwnd'], state['ssthresh'])
|
||||||
|
|
||||||
|
def assertInFastRetransmit(self, state):
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
# trying to be in line with `unittest`
|
||||||
|
"""
|
||||||
|
> The TCP sender SHOULD use the "fast retransmit" algorithm to detect
|
||||||
|
> and repair loss, based on incoming duplicate ACKs. The fast
|
||||||
|
> retransmit algorithm uses the arrival of 3 duplicate ACKs [...] as
|
||||||
|
> an indication that a segment has been lost.
|
||||||
|
"""
|
||||||
|
self.assertGreaterEqual(state['dup_acks'], state['consts']['frthresh'])
|
||||||
|
|
||||||
|
def assertNotInFastRetransmit(self, state):
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
# trying to be in line with `unittest`
|
||||||
|
"""Reverse of self.assertInFastRetransmit()"""
|
||||||
|
self.assertLess(state['dup_acks'], state['consts']['frthresh'])
|
||||||
|
|
||||||
|
def get_ff_calls(self):
|
||||||
|
res = self.exec_cmd('get_ff_calls')
|
||||||
|
return res['fr_calls']
|
||||||
|
|
||||||
|
def set_same_wnd_adv(self, value):
|
||||||
|
self.exec_cmd(f'set_same_wnd_adv {value:d}')
|
||||||
|
|
||||||
|
def set_cwnd(self, cwnd):
|
||||||
|
self.exec_cmd(f'set_cwnd {cwnd}')
|
||||||
|
|
||||||
|
def cong_state(self):
|
||||||
|
return self.exec_cmd('state')
|
||||||
|
|
||||||
|
def cong_init(self, ctx=0):
|
||||||
|
res = self.shell.init(ctx)
|
||||||
|
return self._parse(res)
|
||||||
|
|
||||||
|
def cong_report_msg_sent(self, msg_size):
|
||||||
|
res = self.shell.report_msg_sent(msg_size)
|
||||||
|
return self._parse(res)
|
||||||
|
|
||||||
|
def cong_report_msg_acked(self, msg, ack):
|
||||||
|
res = self.shell.report_msg_acked(msg, ack)
|
||||||
|
return self._parse(res)
|
||||||
|
|
||||||
|
def cong_report_ecn_ce(self, time):
|
||||||
|
res = self.shell.report_ecn_ce(time)
|
||||||
|
return self._parse(res)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCongUREABEWithoutSetup(TestCongUREBase):
|
||||||
|
def test_no_setup(self):
|
||||||
|
state = self.exec_cmd('state')
|
||||||
|
self.assertEqual(state, {
|
||||||
|
'cwnd': 0,
|
||||||
|
'consts': None,
|
||||||
|
'mss': 0,
|
||||||
|
'last_ack': 0,
|
||||||
|
'ssthresh': 0,
|
||||||
|
'in_flight_size': 0,
|
||||||
|
'dup_acks': 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class TestCongUREABEDefaultInitTests(TestCongUREBase):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
res = self.shell.setup(0)
|
||||||
|
self.assertIn('success', res)
|
||||||
|
|
||||||
|
def test_setup(self):
|
||||||
|
state = self.cong_state()
|
||||||
|
self.assertIsNotNone(state['consts'])
|
||||||
|
# fast_retransmit and same_window_advertised need to be set to a
|
||||||
|
# function pointer
|
||||||
|
self.assertNotEqual(int(state['consts']['fr'], base=16), 0)
|
||||||
|
self.assertNotEqual(int(state['consts']['same_wnd_adv'], base=16), 0)
|
||||||
|
# ss_cwnd_inc, ca_cwnd_inc, fr_cwnd_dec are optional and setup 0 need
|
||||||
|
# to be set to a function pointer
|
||||||
|
self.assertEqual(int(state['consts']['ss_cwnd_inc'], base=16), 0)
|
||||||
|
self.assertEqual(int(state['consts']['ca_cwnd_inc'], base=16), 0)
|
||||||
|
self.assertEqual(int(state['consts']['fr_cwnd_dec'], base=16), 0)
|
||||||
|
self.assertEqual(state['consts']['init_mss'], 1460)
|
||||||
|
self.assertEqual(state['consts']['cwnd_lower'], 1095)
|
||||||
|
self.assertEqual(state['consts']['cwnd_upper'], 2190)
|
||||||
|
self.assertEqual(state['consts']['init_ssthresh'], 0xffff)
|
||||||
|
self.assertEqual(state['consts']['frthresh'], 3)
|
||||||
|
# https://tools.ietf.org/html/rfc8511#section-3.1
|
||||||
|
beta_ecn = (state['consts']['abe_multiplier_numerator'] /
|
||||||
|
state['consts']['abe_multiplier_denominator'])
|
||||||
|
self.assertAlmostEqual(beta_ecn, 0.8)
|
||||||
|
self.assertEqual(state['consts']['frthresh'], 3)
|
||||||
|
|
||||||
|
def test_init(self):
|
||||||
|
"""
|
||||||
|
This is inherited from `congure_reno`, so it should be the same as
|
||||||
|
in `tests/congure_reno`.
|
||||||
|
|
||||||
|
https://tools.ietf.org/html/rfc5681#section-3.1
|
||||||
|
|
||||||
|
> IW, the initial value of cwnd, MUST be set using the following
|
||||||
|
> guidelines as an upper bound.
|
||||||
|
>
|
||||||
|
> If SMSS > 2190 bytes:
|
||||||
|
> IW = 2 * SMSS bytes and MUST NOT be more than 2 segments
|
||||||
|
> If (SMSS > 1095 bytes) and (SMSS <= 2190 bytes):
|
||||||
|
> IW = 3 * SMSS bytes and MUST NOT be more than 3 segments
|
||||||
|
> if SMSS <= 1095 bytes:
|
||||||
|
> IW = 4 * SMSS bytes and MUST NOT be more than 4 segments
|
||||||
|
"""
|
||||||
|
res = self.cong_init()
|
||||||
|
self.assertIn('success', res)
|
||||||
|
state = self.cong_state()
|
||||||
|
self.assertEqual(state['consts']['init_mss'], state['mss'])
|
||||||
|
# (SMSS > 1095 bytes)
|
||||||
|
self.assertGreater(state['mss'], state['consts']['cwnd_lower'])
|
||||||
|
# (SMSS <= 2190 bytes)
|
||||||
|
self.assertLessEqual(state['mss'], state['consts']['cwnd_upper'])
|
||||||
|
# as such, IW = 3 * SMSS bytes
|
||||||
|
self.assertEqual(state['cwnd'], 3 * state['mss'])
|
||||||
|
# We start with slow start
|
||||||
|
self.assertSlowStart(state)
|
||||||
|
self.assertNotInFastRetransmit(state)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCongUREABE(TestCongUREBase):
|
||||||
|
"""
|
||||||
|
Most functionality should be the same as for `congure_reno`, except
|
||||||
|
for the behavior of `report_ecn_ce`. So only test some basics from
|
||||||
|
`tests/congure_reno` and focus testing on `report_ecn_ce`
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
res = self.shell.setup(0)
|
||||||
|
self.assertIn('success', res)
|
||||||
|
res = self.cong_init()
|
||||||
|
self.assertIn('success', res)
|
||||||
|
|
||||||
|
def _send_msg_and_recv_ack(self, msg_size, msg_resends=0,
|
||||||
|
ack_id=15, ack_size=None, ack_clean=True):
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
|
# already reduced number of arguments, cong_report_msg_acked would
|
||||||
|
# need...
|
||||||
|
if ack_size is None:
|
||||||
|
# set ack_size to arbitrary value
|
||||||
|
ack_size = msg_size
|
||||||
|
res = self.cong_report_msg_sent(msg_size=msg_size)
|
||||||
|
self.assertIn('success', res)
|
||||||
|
state = self.cong_state()
|
||||||
|
self.assertEqual(state['in_flight_size'], msg_size)
|
||||||
|
res = self.cong_report_msg_acked(
|
||||||
|
msg={'send_time': 1000, 'size': msg_size, 'resends': msg_resends},
|
||||||
|
ack={'recv_time': 1100, 'id': ack_id, 'size': ack_size,
|
||||||
|
'clean': ack_clean, 'wnd': 1234, 'delay': 0},
|
||||||
|
)
|
||||||
|
self.assertIn('success', res)
|
||||||
|
|
||||||
|
def test_slow_start_increase(self):
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
# name chosen to be in line with RFC
|
||||||
|
"""
|
||||||
|
See test_slow_start_increase_large_N() from `tests/congure_reno`
|
||||||
|
"""
|
||||||
|
state = self.cong_state()
|
||||||
|
init_cwnd = state['cwnd']
|
||||||
|
init_mss = state['mss']
|
||||||
|
init_ssthresh = state['ssthresh']
|
||||||
|
self.assertEqual(state['in_flight_size'], 0)
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
# name chosen to be in line with RFC
|
||||||
|
# set N to larger than SMSS
|
||||||
|
N = state['mss'] + 1337
|
||||||
|
self._send_msg_and_recv_ack(N)
|
||||||
|
state = self.cong_state()
|
||||||
|
# MSS did not change
|
||||||
|
self.assertEqual(state['mss'], init_mss)
|
||||||
|
self.assertEqual(state['cwnd'], init_cwnd + state['mss'])
|
||||||
|
self.assertEqual(state['in_flight_size'], 0)
|
||||||
|
self.assertEqual(state['ssthresh'], init_ssthresh)
|
||||||
|
self.assertNotInFastRetransmit(state)
|
||||||
|
|
||||||
|
def test_enter_fast_retransmit(self):
|
||||||
|
"""
|
||||||
|
See self.test_enter_fast_retransmit_all_check_true() from
|
||||||
|
`tests/congure_reno`
|
||||||
|
"""
|
||||||
|
state = self.cong_state()
|
||||||
|
self.assertEqual(0, self.get_ff_calls())
|
||||||
|
self.assertNotInFastRetransmit(state)
|
||||||
|
self.assertEqual(state['in_flight_size'], 0)
|
||||||
|
self._send_msg_and_recv_ack(42, ack_id=15, ack_size=0, ack_clean=True)
|
||||||
|
# make condition (a) true
|
||||||
|
self.cong_report_msg_sent(52)
|
||||||
|
state = self.cong_state()
|
||||||
|
self.assertEqual(state['in_flight_size'], 52)
|
||||||
|
# make condition (e) not true
|
||||||
|
self.set_same_wnd_adv(True)
|
||||||
|
# condition (b) ack['size'] == 0, (c) ack['clean'] == True,
|
||||||
|
# (d) ack['id'] == 15
|
||||||
|
for _ in range(3):
|
||||||
|
res = self.cong_report_msg_acked(
|
||||||
|
msg={'send_time': 1000, 'size': 42, 'resends': 0},
|
||||||
|
ack={'recv_time': 1100, 'id': 15, 'size': 0,
|
||||||
|
'clean': True, 'wnd': 1234, 'delay': 0},
|
||||||
|
)
|
||||||
|
self.assertIn('success', res)
|
||||||
|
self.assertEqual(1, self.get_ff_calls())
|
||||||
|
self.assertInFastRetransmit(self.cong_state())
|
||||||
|
|
||||||
|
def test_ecn_ce_small_flight_size(self):
|
||||||
|
"""
|
||||||
|
https://tools.ietf.org/html/rfc8511#section-3
|
||||||
|
|
||||||
|
> As permitted by RFC 8311, this document specifies a sender-side
|
||||||
|
> change to TCP where receipt of a packet with the ECN-Echo flag SHOULD
|
||||||
|
> trigger the TCP source to set the slow start threshold (ssthresh) to
|
||||||
|
> 0.8 times the FlightSize, with a lower bound of 2 * SMSS applied to
|
||||||
|
> the result (where SMSS stands for Sender Maximum Segment Size)). As
|
||||||
|
> in [RFC5681], the TCP sender also reduces the cwnd value to no more
|
||||||
|
> than the new ssthresh value. Section 6.1.2 of RFC 3168 provides
|
||||||
|
> guidance on setting a cwnd less than 2 * SMSS.
|
||||||
|
"""
|
||||||
|
state = self.cong_state()
|
||||||
|
beta_ecn = (state['consts']['abe_multiplier_numerator'] /
|
||||||
|
state['consts']['abe_multiplier_denominator'])
|
||||||
|
self.assertAlmostEqual(beta_ecn, 0.8)
|
||||||
|
flight_size = 150
|
||||||
|
# put some bytes in the air
|
||||||
|
self.cong_report_msg_sent(flight_size)
|
||||||
|
self.cong_report_ecn_ce(1204)
|
||||||
|
state = self.cong_state()
|
||||||
|
# [...] set the slow start threshold (ssthresh) to 0.8 times the
|
||||||
|
# FlightSize, with a lower bound of 2 * SMSS applied to the result
|
||||||
|
self.assertEqual(state['ssthresh'], 2 * state['mss'])
|
||||||
|
# the TCP sender also reduces the cwnd value to no more
|
||||||
|
# than the new ssthresh value
|
||||||
|
self.assertLessEqual(state['cwnd'], state['ssthresh'])
|
||||||
|
|
||||||
|
def test_ecn_ce_large_flight_size(self):
|
||||||
|
"""
|
||||||
|
Same as test_ecn_ce_small_flight_size, but with flight size
|
||||||
|
larger than (2 * SMSS / 0.8)
|
||||||
|
"""
|
||||||
|
state = self.cong_state()
|
||||||
|
beta_ecn = (state['consts']['abe_multiplier_numerator'] /
|
||||||
|
state['consts']['abe_multiplier_denominator'])
|
||||||
|
self.assertAlmostEqual(beta_ecn, 0.8)
|
||||||
|
flight_size = 3 * state['mss']
|
||||||
|
# increase congestion window large enough to send all those bytes
|
||||||
|
self.set_cwnd(flight_size)
|
||||||
|
# put some bytes in the air
|
||||||
|
for _ in range(3):
|
||||||
|
self.cong_report_msg_sent(state['mss'])
|
||||||
|
self.cong_report_ecn_ce(1204)
|
||||||
|
state = self.cong_state()
|
||||||
|
# [...] set the slow start threshold (ssthresh) to 0.8 times the
|
||||||
|
# FlightSize, with a lower bound of 2 * SMSS applied to the result
|
||||||
|
self.assertEqual(state['ssthresh'], beta_ecn * flight_size)
|
||||||
|
# the TCP sender also reduces the cwnd value to no more
|
||||||
|
# than the new ssthresh value
|
||||||
|
self.assertLessEqual(state['cwnd'], state['ssthresh'])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue
Block a user