CS144Lab/tests/fsm_retx_win.cc
2021-09-28 17:03:04 -07:00

132 lines
4.9 KiB
C++

#include "fsm_retx.hh"
#include "tcp_config.hh"
#include "util.hh"
#include <algorithm>
#include <cstdlib>
#include <iostream>
using namespace std;
using State = TCPTestHarness::State;
int main() {
try {
TCPConfig cfg{};
cfg.recv_capacity = 65000;
auto rd = get_random_generator();
// multiple segments with intervening ack
{
WrappingInt32 tx_ackno(rd());
TCPTestHarness test_2 = TCPTestHarness::in_established(cfg, tx_ackno - 1, tx_ackno - 1);
string d1 = "asdf";
string d2 = "qwer";
test_2.execute(Write{d1});
test_2.execute(Tick(1));
test_2.execute(Tick(20));
test_2.execute(Write{d2});
test_2.execute(Tick(1));
test_2.execute(ExpectSegmentAvailable{}, "test 2 failed: cannot read after write()s");
check_segment(test_2, d1, true, __LINE__);
check_segment(test_2, d2, false, __LINE__);
test_2.execute(Tick(cfg.rt_timeout - 23));
test_2.execute(ExpectNoSegment{}, "test 2 failed: re-tx too fast");
test_2.execute(Tick(4));
check_segment(test_2, d1, false, __LINE__);
test_2.execute(Tick(2 * cfg.rt_timeout - 2));
test_2.execute(ExpectNoSegment{}, "test 2 failed: re-tx too fast");
test_2.send_ack(tx_ackno, tx_ackno + 4); // make sure RTO timer restarts on successful ACK
test_2.execute(Tick(cfg.rt_timeout - 2));
test_2.execute(ExpectNoSegment{}, "test 2 failed: re-tx of 2nd seg after ack for 1st seg too fast");
test_2.execute(Tick(3));
check_segment(test_2, d2, false, __LINE__);
}
// multiple segments without intervening ack
{
WrappingInt32 tx_ackno(rd());
TCPTestHarness test_3 = TCPTestHarness::in_established(cfg, tx_ackno - 1, tx_ackno - 1);
string d1 = "asdf";
string d2 = "qwer";
test_3.execute(Write{d1});
test_3.execute(Tick(1));
test_3.execute(Tick(20));
test_3.execute(Write{d2});
test_3.execute(Tick(1));
test_3.execute(ExpectSegmentAvailable{}, "test 3 failed: cannot read after write()s");
check_segment(test_3, d1, true, __LINE__);
check_segment(test_3, d2, false, __LINE__);
test_3.execute(Tick(cfg.rt_timeout - 23));
test_3.execute(ExpectNoSegment{}, "test 3 failed: re-tx too fast");
test_3.execute(Tick(4));
check_segment(test_3, d1, false, __LINE__);
test_3.execute(Tick(2 * cfg.rt_timeout - 2));
test_3.execute(ExpectNoSegment{}, "test 3 failed: re-tx of 2nd seg too fast");
test_3.execute(Tick(3));
check_segment(test_3, d1, false, __LINE__);
}
// check that ACK of new data resets exponential backoff and restarts timer
auto backoff_test = [&](const unsigned int num_backoffs) {
WrappingInt32 tx_ackno(rd());
TCPTestHarness test_4 = TCPTestHarness::in_established(cfg, tx_ackno - 1, tx_ackno - 1);
string d1 = "asdf";
string d2 = "qwer";
test_4.execute(Write{d1});
test_4.execute(Tick(1));
test_4.execute(Tick(20));
test_4.execute(Write{d2});
test_4.execute(Tick(1));
test_4.execute(ExpectSegmentAvailable{}, "test 4 failed: cannot read after write()s");
check_segment(test_4, d1, true, __LINE__);
check_segment(test_4, d2, false, __LINE__);
test_4.execute(Tick(cfg.rt_timeout - 23));
test_4.execute(ExpectNoSegment{}, "test 4 failed: re-tx too fast");
test_4.execute(Tick(4));
check_segment(test_4, d1, false, __LINE__);
for (unsigned i = 1; i < num_backoffs; ++i) {
test_4.execute(Tick((cfg.rt_timeout << i) - i)); // exponentially increasing delay length
test_4.execute(ExpectNoSegment{}, "test 4 failed: re-tx too fast after timeout");
test_4.execute(Tick(i));
check_segment(test_4, d1, false, __LINE__);
}
test_4.send_ack(tx_ackno, tx_ackno + 4); // make sure RTO timer restarts on successful ACK
test_4.execute(Tick(cfg.rt_timeout - 2));
test_4.execute(ExpectNoSegment{},
"test 4 failed: re-tx of 2nd seg after ack for 1st seg too fast after " +
to_string(num_backoffs) + " backoffs");
test_4.execute(Tick(3));
check_segment(test_4, d2, false, __LINE__);
};
for (unsigned int i = 0; i < TCPConfig::MAX_RETX_ATTEMPTS; ++i) {
backoff_test(i);
}
} catch (const exception &e) {
cerr << e.what() << endl;
return 1;
}
return EXIT_SUCCESS;
}