dcsctp: Add more unit tests for DataTracker

There were some missing unit tests that are now written. When doing
this, it was found that SACKs weren't sent for duplicate received
chunks, which they should be according to the spec.

Bug: webrtc:12614
Change-Id: I8296473c0c8cbaf0329785de95e9b9945f254339
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/220607
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34165}
This commit is contained in:
Victor Boivie 2021-05-28 17:21:39 +02:00 committed by WebRTC LUCI CQ
parent 5429d71022
commit 5d4c3c5128
2 changed files with 121 additions and 17 deletions

View File

@ -58,24 +58,36 @@ void DataTracker::Observe(TSN tsn,
if (duplicate_tsns_.size() < kMaxDuplicateTsnReported) {
duplicate_tsns_.insert(unwrapped_tsn.Wrap());
}
return;
}
if (unwrapped_tsn == last_cumulative_acked_tsn_.next_value()) {
last_cumulative_acked_tsn_ = unwrapped_tsn;
// The cumulative acked tsn may be moved even further, if a gap was filled.
while (!additional_tsns_.empty() &&
*additional_tsns_.begin() ==
last_cumulative_acked_tsn_.next_value()) {
last_cumulative_acked_tsn_.Increment();
additional_tsns_.erase(additional_tsns_.begin());
}
// https://datatracker.ietf.org/doc/html/rfc4960#section-6.2
// "When a packet arrives with duplicate DATA chunk(s) and with no new DATA
// chunk(s), the endpoint MUST immediately send a SACK with no delay. If a
// packet arrives with duplicate DATA chunk(s) bundled with new DATA chunks,
// the endpoint MAY immediately send a SACK."
UpdateAckState(AckState::kImmediate, "duplicate data");
} else {
bool inserted = additional_tsns_.insert(unwrapped_tsn).second;
if (!inserted) {
// Already seen before.
if (duplicate_tsns_.size() < kMaxDuplicateTsnReported) {
duplicate_tsns_.insert(unwrapped_tsn.Wrap());
if (unwrapped_tsn == last_cumulative_acked_tsn_.next_value()) {
last_cumulative_acked_tsn_ = unwrapped_tsn;
// The cumulative acked tsn may be moved even further, if a gap was
// filled.
while (!additional_tsns_.empty() &&
*additional_tsns_.begin() ==
last_cumulative_acked_tsn_.next_value()) {
last_cumulative_acked_tsn_.Increment();
additional_tsns_.erase(additional_tsns_.begin());
}
} else {
bool inserted = additional_tsns_.insert(unwrapped_tsn).second;
if (!inserted) {
// Already seen before.
if (duplicate_tsns_.size() < kMaxDuplicateTsnReported) {
duplicate_tsns_.insert(unwrapped_tsn.Wrap());
}
// https://datatracker.ietf.org/doc/html/rfc4960#section-6.2
// "When a packet arrives with duplicate DATA chunk(s) and with no new
// DATA chunk(s), the endpoint MUST immediately send a SACK with no
// delay. If a packet arrives with duplicate DATA chunk(s) bundled with
// new DATA chunks, the endpoint MAY immediately send a SACK."
// No need to do this. SACKs are sent immediately on packet loss below.
}
}
}

View File

@ -295,5 +295,97 @@ TEST_F(DataTrackerTest, LimitsNumberOfGapAckBlocksReported) {
SizeIs(DataTracker::kMaxGapAckBlocksReported));
}
TEST_F(DataTrackerTest, SendsSackForFirstPacketObserved) {
Observer({11});
buf_.ObservePacketEnd();
EXPECT_TRUE(buf_.ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
}
TEST_F(DataTrackerTest, SendsSackEverySecondPacketWhenThereIsNoPacketLoss) {
Observer({11});
buf_.ObservePacketEnd();
EXPECT_TRUE(buf_.ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({12});
buf_.ObservePacketEnd();
EXPECT_FALSE(buf_.ShouldSendAck());
EXPECT_TRUE(timer_->is_running());
Observer({13});
buf_.ObservePacketEnd();
EXPECT_TRUE(buf_.ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({14});
buf_.ObservePacketEnd();
EXPECT_FALSE(buf_.ShouldSendAck());
EXPECT_TRUE(timer_->is_running());
Observer({15});
buf_.ObservePacketEnd();
EXPECT_TRUE(buf_.ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
}
TEST_F(DataTrackerTest, SendsSackEveryPacketOnPacketLoss) {
Observer({11});
buf_.ObservePacketEnd();
EXPECT_TRUE(buf_.ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({13});
buf_.ObservePacketEnd();
EXPECT_TRUE(buf_.ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({14});
buf_.ObservePacketEnd();
EXPECT_TRUE(buf_.ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({15});
buf_.ObservePacketEnd();
EXPECT_TRUE(buf_.ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({16});
buf_.ObservePacketEnd();
EXPECT_TRUE(buf_.ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
// Fill the hole.
Observer({12});
buf_.ObservePacketEnd();
EXPECT_FALSE(buf_.ShouldSendAck());
EXPECT_TRUE(timer_->is_running());
// Goes back to every second packet
Observer({17});
buf_.ObservePacketEnd();
EXPECT_TRUE(buf_.ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({18});
buf_.ObservePacketEnd();
EXPECT_FALSE(buf_.ShouldSendAck());
EXPECT_TRUE(timer_->is_running());
}
TEST_F(DataTrackerTest, SendsSackOnDuplicateDataChunks) {
Observer({11});
buf_.ObservePacketEnd();
EXPECT_TRUE(buf_.ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({11});
buf_.ObservePacketEnd();
EXPECT_TRUE(buf_.ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({12});
buf_.ObservePacketEnd();
EXPECT_FALSE(buf_.ShouldSendAck());
EXPECT_TRUE(timer_->is_running());
// Goes back to every second packet
Observer({13});
buf_.ObservePacketEnd();
EXPECT_TRUE(buf_.ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
// Duplicate again
Observer({12});
buf_.ObservePacketEnd();
EXPECT_TRUE(buf_.ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
}
} // namespace
} // namespace dcsctp