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