blob: f579a83481436ae1f2c1cb4b9f23d16265ab0aab [file] [log] [blame]
/*
* Copyright (c) 2025 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 "modules/congestion_controller/scream/test/cc_feedback_generator.h"
#include "api/transport/ecn_marking.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "system_wrappers/include/clock.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/network/simulated_network.h"
namespace webrtc {
namespace {
constexpr DataSize kPacketSize = DataSize::Bytes(1000);
using ::testing::MockFunction;
using ::testing::SizeIs;
TEST(CcFeedbackGeneratorTest, BasicFeedback) {
// Link capacity of 1000Kbps means it will take 1000*8/1000 = 8ms to send one
// packet through the narrow section + 25ms fixed one way delay.
// With perfect pacing, smoothed_rtt should be 58ms.
// With a send rate of 500Kbps, where each packet is 1000bytes, one packet is
// sent every 16ms.
// The first feedback packet is expected to be sent as soon as the first
// packet has been delivered and received 25ms later. At that time, there
// should be 3 packets in flight (3*16=48ms).
SimulatedClock clock(Timestamp::Seconds(1234));
CcFeedbackGenerator feedback_generator(
{.network_config = {.queue_delay_ms = 25,
.link_capacity = DataRate::KilobitsPerSec(1000)},
.time_between_feedback = TimeDelta::Millis(50)});
TransportPacketsFeedback feedback_1 =
feedback_generator.ProcessUntilNextFeedback(
/*send_rate=*/DataRate::KilobitsPerSec(500), clock, nullptr);
EXPECT_EQ(feedback_1.feedback_time, clock.CurrentTime());
EXPECT_EQ(feedback_1.data_in_flight, 3 * kPacketSize);
ASSERT_THAT(feedback_1.packet_feedbacks, SizeIs(1));
EXPECT_EQ(feedback_1.packet_feedbacks[0].arrival_time_offset,
TimeDelta::Zero());
for (const PacketResult& packet : feedback_1.packet_feedbacks) {
EXPECT_EQ((packet.receive_time - packet.sent_packet.send_time),
TimeDelta::Millis(25 + 8));
EXPECT_EQ(packet.sent_packet.size, kPacketSize);
EXPECT_EQ(packet.ecn, EcnMarking::kEct1);
}
TransportPacketsFeedback feedback_2 =
feedback_generator.ProcessUntilNextFeedback(
/*send_rate=*/DataRate::KilobitsPerSec(500), clock, nullptr);
EXPECT_EQ((feedback_2.feedback_time - feedback_1.feedback_time).ms(), 50);
}
TEST(CcFeedbackGeneratorTest, CeMarksPacketsIfSendRateIsTooHigh) {
SimulatedClock clock(Timestamp::Seconds(1234));
CcFeedbackGenerator feedback_generator(
{.network_config = {.queue_delay_ms = 25,
.link_capacity = DataRate::KilobitsPerSec(1000)}});
int number_of_ce_marks = 0;
for (int i = 0; i < 5; ++i) {
TransportPacketsFeedback feedback =
feedback_generator.ProcessUntilNextFeedback(
/*send_rate=*/DataRate::KilobitsPerSec(1100), clock, nullptr);
number_of_ce_marks += CcFeedbackGenerator::CountCeMarks(feedback);
}
EXPECT_GE(number_of_ce_marks, 2);
}
TEST(CcFeedbackGeneratorTest, NoCeMarksIfSendRateIsBelowLinkCapacity) {
SimulatedClock clock(Timestamp::Seconds(1234));
CcFeedbackGenerator feedback_generator(
{.network_config = {.queue_delay_ms = 25,
.link_capacity = DataRate::KilobitsPerSec(1000)},
.time_between_feedback = TimeDelta::Millis(25)});
int number_of_ce_marks = 0;
for (int i = 0; i < 5; ++i) {
TransportPacketsFeedback feedback =
feedback_generator.ProcessUntilNextFeedback(
/*send_rate=*/DataRate::KilobitsPerSec(1000), clock, nullptr);
number_of_ce_marks += CcFeedbackGenerator::CountCeMarks(feedback);
}
EXPECT_EQ(number_of_ce_marks, 0);
}
TEST(CcFeedbackGeneratorTest, DropPacketsIfSendRateIsTooHigh) {
SimulatedClock clock(Timestamp::Seconds(1234));
SimulatedNetwork::Config network_config = {
.queue_length_packets = 1, // Only one packet is allowed to be queued
.queue_delay_ms = 25,
.link_capacity = DataRate::KilobitsPerSec(1000),
};
CcFeedbackGenerator feedback_generator(
{.network_config = network_config, .send_as_ect1 = false});
int number_of_lost_packets = 0;
for (int i = 0; i < 5; ++i) {
TransportPacketsFeedback feedback =
feedback_generator.ProcessUntilNextFeedback(
/*send_rate=*/DataRate::KilobitsPerSec(1100), clock, nullptr);
number_of_lost_packets += feedback.LostWithSendInfo().size();
}
EXPECT_GE(number_of_lost_packets, 2);
}
TEST(CcFeedbackGeneratorTest, InvokesPacketSentCallbackWithDataInflight) {
SimulatedClock clock(Timestamp::Seconds(1234));
SimulatedNetwork::Config network_config = {};
CcFeedbackGenerator feedback_generator({
.network_config = network_config,
});
MockFunction<void(const SentPacket&)> packet_sent_cb;
DataSize last_data_in_flight;
EXPECT_CALL(packet_sent_cb, Call)
.WillRepeatedly([&](const SentPacket& packet) {
last_data_in_flight = packet.data_in_flight;
});
TransportPacketsFeedback feedback =
feedback_generator.ProcessUntilNextFeedback(
/*send_rate=*/DataRate::KilobitsPerSec(1000), clock,
packet_sent_cb.AsStdFunction());
// After feedback, data in flight should be less than after sending last
// packet.
EXPECT_GT(last_data_in_flight, feedback.data_in_flight);
}
} // namespace
} // namespace webrtc