/* * Copyright 2026 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/loss_estimator.h" #include "api/environment/environment.h" #include "api/units/data_size.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "modules/congestion_controller/scream/scream_feedback.h" #include "modules/congestion_controller/scream/scream_v2_parameters.h" #include "system_wrappers/include/clock.h" #include "test/create_test_environment.h" #include "test/gtest.h" namespace webrtc { namespace { ScreamFeedback CreateFeedbackWithLoss(Timestamp feedback_time, bool with_loss) { ScreamFeedback feedback; feedback.feedback_time = feedback_time; feedback.num_received_packets = 1; feedback.acked_not_marked_size = DataSize::Bytes(1000); if (with_loss) { feedback.num_lost_packets = 1; feedback.acked_not_marked_size += DataSize::Bytes(1000); } return feedback; } TEST(LossEstimatorTest, EstimatesCongestionLevel) { SimulatedClock clock(Timestamp::Seconds(1000)); Environment env = CreateTestEnvironment({.time = &clock}); ScreamV2Parameters params(env.field_trials()); LossEstimator estimator(params); EXPECT_EQ(estimator.congestion_level(), 0.0); EXPECT_FALSE(estimator.congested()); // RTT 1: Loss occurs. Since last_rtt_update_time_ starts at MinusInfinity, // this report executes the RTT boundary update immediately. ScreamFeedback feedback1 = CreateFeedbackWithLoss(clock.CurrentTime(), /*with_loss=*/true); EXPECT_TRUE(estimator.Update(feedback1, TimeDelta::Millis(25))); EXPECT_NEAR(estimator.congestion_level(), 1.0 / 3.0, 0.001); EXPECT_FALSE(estimator.congested()); // RTT 2: Loss occurs. clock.AdvanceTime(TimeDelta::Millis(25)); ScreamFeedback feedback2 = CreateFeedbackWithLoss(clock.CurrentTime(), /*with_loss=*/true); EXPECT_TRUE(estimator.Update(feedback2, TimeDelta::Millis(25))); EXPECT_NEAR(estimator.congestion_level(), 2.0 / 3.0, 0.001); EXPECT_FALSE(estimator.congested()); // RTT 3: Loss occurs. clock.AdvanceTime(TimeDelta::Millis(25)); ScreamFeedback feedback3 = CreateFeedbackWithLoss(clock.CurrentTime(), /*with_loss=*/true); EXPECT_TRUE(estimator.Update(feedback3, TimeDelta::Millis(25))); EXPECT_NEAR(estimator.congestion_level(), 1.0, 0.001); EXPECT_TRUE(estimator.congested()); // RTT 4: No Loss (Step Down of 0.5). clock.AdvanceTime(TimeDelta::Millis(25)); ScreamFeedback feedback4 = CreateFeedbackWithLoss(clock.CurrentTime(), /*with_loss=*/false); EXPECT_FALSE(estimator.Update(feedback4, TimeDelta::Millis(25))); EXPECT_NEAR(estimator.congestion_level(), 0.5, 0.001); EXPECT_FALSE(estimator.congested()); // RTT 5: Loss occurs. clock.AdvanceTime(TimeDelta::Millis(25)); ScreamFeedback feedback5 = CreateFeedbackWithLoss(clock.CurrentTime(), /*with_loss=*/true); EXPECT_TRUE(estimator.Update(feedback5, TimeDelta::Millis(25))); EXPECT_NEAR(estimator.congestion_level(), 0.5 + 1.0 / 3.0, 0.001); EXPECT_FALSE(estimator.congested()); } TEST(LossEstimatorTest, ClearsCongestionLevelUponFullRecovery) { SimulatedClock clock(Timestamp::Seconds(1000)); Environment env = CreateTestEnvironment({.time = &clock}); ScreamV2Parameters params(env.field_trials()); LossEstimator estimator(params); // Report loss. ScreamFeedback feedback1 = CreateFeedbackWithLoss(clock.CurrentTime(), /*with_loss=*/true); EXPECT_TRUE(estimator.Update(feedback1, TimeDelta::Millis(25))); EXPECT_GT(estimator.congestion_level(), 0.0); // Report recovery. ScreamFeedback recovery_feedback; recovery_feedback.feedback_time = clock.CurrentTime(); recovery_feedback.num_received_packets = 1; recovery_feedback.acked_not_marked_size = DataSize::Bytes(1000); recovery_feedback.num_recovered_packets = 1; EXPECT_FALSE(estimator.Update(recovery_feedback, TimeDelta::Millis(25))); EXPECT_EQ(estimator.congestion_level(), 0.0); } } // namespace } // namespace webrtc