Files
platform-external-webrtc/net/dcsctp/rx/data_tracker_test.cc
Victor Boivie b2d539be6b dcsctp: Add Data Tracker
The Data Tracker's purpose is to keep track of all received DATA chunks
and to ACK/NACK that data, by generating SACK chunks reflecting its view
of what has been received and what has been lost.

It also contains logic for _when_ to send the SACKs, as that's different
depending on e.g. packet loss. Generally, SACKs are sent every second
packet on a connection with no packet loss, and can also be sent on a
delayed timer.

In case partial reliability is used, and the transmitter has decided
that some data shouldn't be retransmitted, it will send a FORWARD-TSN
chunk, which this class also handles, by "forgetting" about those
chunks.

Bug: webrtc:12614
Change-Id: Ifafb0c211f6a47872e81830165ab5fc43ee7f366
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/213664
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Reviewed-by: Tommi <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33676}
2021-04-11 18:37:50 +00:00

210 lines
6.7 KiB
C++

/*
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "net/dcsctp/rx/data_tracker.h"
#include <cstdint>
#include <initializer_list>
#include <memory>
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "net/dcsctp/packet/chunk/sack_chunk.h"
#include "net/dcsctp/timer/fake_timeout.h"
#include "net/dcsctp/timer/timer.h"
#include "rtc_base/gunit.h"
#include "test/gmock.h"
namespace dcsctp {
namespace {
using ::testing::ElementsAre;
using ::testing::IsEmpty;
constexpr size_t kArwnd = 10000;
class DataTrackerTest : public testing::Test {
protected:
DataTrackerTest()
: timeout_manager_([this]() { return now_; }),
timer_manager_([this]() { return timeout_manager_.CreateTimeout(); }),
timer_(timer_manager_.CreateTimer(
"test/delayed_ack",
[]() { return absl::nullopt; },
TimerOptions(DurationMs(0)))),
buf_("log: ", timer_.get(), /*peer_initial_tsn=*/TSN(11)) {}
void Observer(std::initializer_list<uint32_t> tsns) {
for (const uint32_t tsn : tsns) {
buf_.Observe(TSN(tsn), AnyDataChunk::ImmediateAckFlag(false));
}
}
TimeMs now_ = TimeMs(0);
FakeTimeoutManager timeout_manager_;
TimerManager timer_manager_;
std::unique_ptr<Timer> timer_;
DataTracker buf_;
};
TEST_F(DataTrackerTest, Empty) {
SackChunk sack = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
EXPECT_THAT(sack.duplicate_tsns(), IsEmpty());
}
TEST_F(DataTrackerTest, ObserverSingleInOrderPacket) {
Observer({11});
SackChunk sack = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(11));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
EXPECT_THAT(sack.duplicate_tsns(), IsEmpty());
}
TEST_F(DataTrackerTest, ObserverManyInOrderMovesCumulativeTsnAck) {
Observer({11, 12, 13});
SackChunk sack = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(13));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
EXPECT_THAT(sack.duplicate_tsns(), IsEmpty());
}
TEST_F(DataTrackerTest, ObserveOutOfOrderMovesCumulativeTsnAck) {
Observer({12, 13, 14, 11});
SackChunk sack = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(14));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
EXPECT_THAT(sack.duplicate_tsns(), IsEmpty());
}
TEST_F(DataTrackerTest, SingleGap) {
Observer({12});
SackChunk sack = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10));
EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2)));
EXPECT_THAT(sack.duplicate_tsns(), IsEmpty());
}
TEST_F(DataTrackerTest, ExampleFromRFC4960Section334) {
Observer({11, 12, 14, 15, 17});
SackChunk sack = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(12));
EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 3),
SackChunk::GapAckBlock(5, 5)));
EXPECT_THAT(sack.duplicate_tsns(), IsEmpty());
}
TEST_F(DataTrackerTest, AckAlreadyReceivedChunk) {
Observer({11});
SackChunk sack1 = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack1.cumulative_tsn_ack(), TSN(11));
EXPECT_THAT(sack1.gap_ack_blocks(), IsEmpty());
// Receive old chunk
Observer({8});
SackChunk sack2 = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack2.cumulative_tsn_ack(), TSN(11));
EXPECT_THAT(sack2.gap_ack_blocks(), IsEmpty());
}
TEST_F(DataTrackerTest, DoubleSendRetransmittedChunk) {
Observer({11, 13, 14, 15});
SackChunk sack1 = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack1.cumulative_tsn_ack(), TSN(11));
EXPECT_THAT(sack1.gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(2, 4)));
// Fill in the hole.
Observer({12, 16, 17, 18});
SackChunk sack2 = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack2.cumulative_tsn_ack(), TSN(18));
EXPECT_THAT(sack2.gap_ack_blocks(), IsEmpty());
// Receive chunk 12 again.
Observer({12, 19, 20, 21});
SackChunk sack3 = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack3.cumulative_tsn_ack(), TSN(21));
EXPECT_THAT(sack3.gap_ack_blocks(), IsEmpty());
}
TEST_F(DataTrackerTest, ForwardTsnSimple) {
// Messages (11, 12, 13), (14, 15) - first message expires.
Observer({11, 12, 15});
buf_.HandleForwardTsn(TSN(13));
SackChunk sack = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(13));
EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2)));
}
TEST_F(DataTrackerTest, ForwardTsnSkipsFromGapBlock) {
// Messages (11, 12, 13), (14, 15) - first message expires.
Observer({11, 12, 14});
buf_.HandleForwardTsn(TSN(13));
SackChunk sack = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(14));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
}
TEST_F(DataTrackerTest, ExampleFromRFC3758) {
buf_.HandleForwardTsn(TSN(102));
Observer({102, 104, 105, 107});
buf_.HandleForwardTsn(TSN(103));
SackChunk sack = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(105));
EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2)));
}
TEST_F(DataTrackerTest, EmptyAllAcks) {
Observer({11, 13, 14, 15});
buf_.HandleForwardTsn(TSN(100));
SackChunk sack = buf_.CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(100));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
}
TEST_F(DataTrackerTest, SetsArwndCorrectly) {
SackChunk sack1 = buf_.CreateSelectiveAck(/*a_rwnd=*/100);
EXPECT_EQ(sack1.a_rwnd(), 100u);
SackChunk sack2 = buf_.CreateSelectiveAck(/*a_rwnd=*/101);
EXPECT_EQ(sack2.a_rwnd(), 101u);
}
TEST_F(DataTrackerTest, WillIncreaseCumAckTsn) {
EXPECT_EQ(buf_.last_cumulative_acked_tsn(), TSN(10));
EXPECT_FALSE(buf_.will_increase_cum_ack_tsn(TSN(10)));
EXPECT_TRUE(buf_.will_increase_cum_ack_tsn(TSN(11)));
EXPECT_FALSE(buf_.will_increase_cum_ack_tsn(TSN(12)));
Observer({11, 12, 13, 14, 15});
EXPECT_EQ(buf_.last_cumulative_acked_tsn(), TSN(15));
EXPECT_FALSE(buf_.will_increase_cum_ack_tsn(TSN(15)));
EXPECT_TRUE(buf_.will_increase_cum_ack_tsn(TSN(16)));
EXPECT_FALSE(buf_.will_increase_cum_ack_tsn(TSN(17)));
}
TEST_F(DataTrackerTest, ForceShouldSendSackImmediately) {
EXPECT_FALSE(buf_.ShouldSendAck());
buf_.ForceImmediateSack();
EXPECT_TRUE(buf_.ShouldSendAck());
}
} // namespace
} // namespace dcsctp