From 06815534d2da29a7f41cad2eaab6d2103f0138c2 Mon Sep 17 00:00:00 2001 From: Per K Date: Tue, 28 May 2024 09:52:44 +0000 Subject: [PATCH] Add SchedulableNetworkBehavior and tests. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a network behaviour that can change its parameters over time as specified with a schedule proto. Bug: webrtc:14525 Change-Id: Idd34cc48c8e3e8311975615f2c3dc3ffb522a708 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/352140 Reviewed-by: Björn Terelius Reviewed-by: Mirko Bonadei Commit-Queue: Per Kjellander Cr-Commit-Position: refs/heads/main@{#42390} --- BUILD.gn | 5 +- api/test/network_emulation/BUILD.gn | 22 ++ .../network_config_schedule.proto | 27 ++ .../schedulable_network_node_builder.cc | 30 +++ .../schedulable_network_node_builder.h | 33 +++ test/network/BUILD.gn | 49 ++++ test/network/schedulable_network_behavior.cc | 138 ++++++++++ test/network/schedulable_network_behavior.h | 56 ++++ .../schedulable_network_behavior_test.cc | 241 ++++++++++++++++++ test/peer_scenario/tests/BUILD.gn | 7 + test/peer_scenario/tests/bwe_ramp_up_test.cc | 57 +++++ 11 files changed, 664 insertions(+), 1 deletion(-) create mode 100644 api/test/network_emulation/network_config_schedule.proto create mode 100644 api/test/network_emulation/schedulable_network_node_builder.cc create mode 100644 api/test/network_emulation/schedulable_network_node_builder.h create mode 100644 test/network/schedulable_network_behavior.cc create mode 100644 test/network/schedulable_network_behavior.h create mode 100644 test/network/schedulable_network_behavior_test.cc diff --git a/BUILD.gn b/BUILD.gn index f090f16120..7328df134a 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -678,7 +678,10 @@ if (rtc_include_tests && !build_with_chromium) { data = rtc_unittests_resources if (rtc_enable_protobuf) { - deps += [ "logging:rtc_event_log_tests" ] + deps += [ + "api/test/network_emulation:network_config_schedule_proto", + "logging:rtc_event_log_tests", + ] } if (is_ios) { diff --git a/api/test/network_emulation/BUILD.gn b/api/test/network_emulation/BUILD.gn index f28fd35c6b..d4aa23b15a 100644 --- a/api/test/network_emulation/BUILD.gn +++ b/api/test/network_emulation/BUILD.gn @@ -6,8 +6,30 @@ # in the file PATENTS. All contributing project authors may # be found in the AUTHORS file in the root of the source tree. +import("//third_party/protobuf/proto_library.gni") import("../../../webrtc.gni") +if (rtc_enable_protobuf) { + proto_library("network_config_schedule_proto") { + visibility = [ "*" ] + sources = [ "network_config_schedule.proto" ] + proto_out_dir = "api/test/network_emulation/" + } + + rtc_source_set("schedulable_network_node_builder") { + visibility = [ "*" ] + sources = [ + "schedulable_network_node_builder.cc", + "schedulable_network_node_builder.h", + ] + deps = [ + ":network_config_schedule_proto", + "../..:network_emulation_manager_api", + "../../../test/network:schedulable_network_behavior", + ] + } +} + rtc_library("network_emulation") { visibility = [ "*" ] diff --git a/api/test/network_emulation/network_config_schedule.proto b/api/test/network_emulation/network_config_schedule.proto new file mode 100644 index 0000000000..0e1036cdc2 --- /dev/null +++ b/api/test/network_emulation/network_config_schedule.proto @@ -0,0 +1,27 @@ +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; +package webrtc.network_behaviour; + +message NetworkConfigScheduleItem { + // Time since the first sent packet when this item should be applied. + // This should typically be 0 for the first item in the schedule. + optional int64 time_since_first_sent_packet_ms = 1; + + // Network parameters, See webrtc::BuiltInNetworkBehaviorConfig. + optional int64 queue_length_packets = 2; + optional int64 queue_delay_ms = 3; + optional int64 link_capacity_kbps = 4; + optional int64 loss_percent = 5; + optional int64 delay_standard_deviation_ms = 6; + optional bool allow_reordering = 7; + optional int64 avg_burst_loss_length = 8; + optional int64 packet_overhead = 9; +} + +// Schedule describing network parameters in a simulated network. +message NetworkConfigSchedule { + optional int64 repeat_schedule_after_last_ms = 1; + // Items should be sorted by time_since_first_sent_packet_ms. + repeated NetworkConfigScheduleItem item = 2; +} diff --git a/api/test/network_emulation/schedulable_network_node_builder.cc b/api/test/network_emulation/schedulable_network_node_builder.cc new file mode 100644 index 0000000000..fd5a26c4fa --- /dev/null +++ b/api/test/network_emulation/schedulable_network_node_builder.cc @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 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 "api/test/network_emulation/schedulable_network_node_builder.h" + +#include +#include + +#include "api/test/network_emulation/network_config_schedule.pb.h" +#include "api/test/network_emulation_manager.h" +#include "test/network/schedulable_network_behavior.h" + +namespace webrtc { + +SchedulableNetworkNodeBuilder::SchedulableNetworkNodeBuilder( + webrtc::NetworkEmulationManager& net, + network_behaviour::NetworkConfigSchedule schedule) + : net_(net), schedule_(std::move(schedule)) {} + +webrtc::EmulatedNetworkNode* SchedulableNetworkNodeBuilder::Build() { + return net_.CreateEmulatedNode(std::make_unique( + std::move(schedule_), *net_.time_controller()->GetClock())); +} +} // namespace webrtc diff --git a/api/test/network_emulation/schedulable_network_node_builder.h b/api/test/network_emulation/schedulable_network_node_builder.h new file mode 100644 index 0000000000..5e705c95fe --- /dev/null +++ b/api/test/network_emulation/schedulable_network_node_builder.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024 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. + */ +#ifndef API_TEST_NETWORK_EMULATION_SCHEDULABLE_NETWORK_NODE_BUILDER_H_ +#define API_TEST_NETWORK_EMULATION_SCHEDULABLE_NETWORK_NODE_BUILDER_H_ + +#include "api/test/network_emulation/network_config_schedule.pb.h" +#include "api/test/network_emulation_manager.h" + +namespace webrtc { + +class SchedulableNetworkNodeBuilder { + public: + SchedulableNetworkNodeBuilder( + webrtc::NetworkEmulationManager& net, + network_behaviour::NetworkConfigSchedule schedule); + + webrtc::EmulatedNetworkNode* Build(); + + private: + webrtc::NetworkEmulationManager& net_; + network_behaviour::NetworkConfigSchedule schedule_; +}; + +} // namespace webrtc + +#endif // API_TEST_NETWORK_EMULATION_SCHEDULABLE_NETWORK_NODE_BUILDER_H_ diff --git a/test/network/BUILD.gn b/test/network/BUILD.gn index 10c0c4e165..abcd11042e 100644 --- a/test/network/BUILD.gn +++ b/test/network/BUILD.gn @@ -199,6 +199,9 @@ if (rtc_include_tests) { ":network_emulation_unittest", ":simulated_network_unittest", ] + if (rtc_enable_protobuf) { + deps += [ ":schedulable_network_behavior_test" ] + } } } } @@ -241,3 +244,49 @@ if (rtc_include_tests) { ] } } + +if (rtc_enable_protobuf) { + rtc_library("schedulable_network_behavior") { + sources = [ + "schedulable_network_behavior.cc", + "schedulable_network_behavior.h", + ] + deps = [ + ":simulated_network", + "../../api:network_emulation_manager_api", + "../../api:sequence_checker", + "../../api:simulated_network_api", + "../../api/task_queue:task_queue", + "../../api/test/network_emulation:network_config_schedule_proto", + "../../api/units:data_rate", + "../../api/units:time_delta", + "../../api/units:timestamp", + "../../api/units:timestamp", + "../../rtc_base:macromagic", + "../../rtc_base/task_utils:repeating_task", + "../../system_wrappers", + ] + } + + if (rtc_include_tests) { + rtc_library("schedulable_network_behavior_test") { + testonly = true + sources = [ "schedulable_network_behavior_test.cc" ] + deps = [ + ":schedulable_network_behavior", + "../:test_support", + "../../api:create_network_emulation_manager", + "../../api:network_emulation_manager_api", + "../../api:simulated_network_api", + "../../api/test/network_emulation:network_config_schedule_proto", + "../../api/units:data_rate", + "../../api/units:data_size", + "../../api/units:time_delta", + "../../api/units:timestamp", + "../../system_wrappers", + "//testing/gtest", + "//third_party/abseil-cpp/absl/algorithm:container", + ] + } + } +} diff --git a/test/network/schedulable_network_behavior.cc b/test/network/schedulable_network_behavior.cc new file mode 100644 index 0000000000..32f5305696 --- /dev/null +++ b/test/network/schedulable_network_behavior.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2024 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 "test/network/schedulable_network_behavior.h" + +#include + +#include "api/sequence_checker.h" +#include "api/task_queue/task_queue_base.h" +#include "api/test/network_emulation/network_config_schedule.pb.h" +#include "api/test/simulated_network.h" +#include "api/units/data_rate.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "rtc_base/task_utils/repeating_task.h" +#include "system_wrappers/include/clock.h" +#include "test/network/simulated_network.h" + +namespace webrtc { + +namespace { + +using ::webrtc::BuiltInNetworkBehaviorConfig; + +void UpdateConfigFromSchedule( + const network_behaviour::NetworkConfigScheduleItem& schedule_item, + BuiltInNetworkBehaviorConfig& config) { + if (schedule_item.has_queue_length_packets()) { + config.queue_length_packets = schedule_item.queue_length_packets(); + } + if (schedule_item.has_queue_delay_ms()) { + config.queue_delay_ms = schedule_item.queue_delay_ms(); + } + if (schedule_item.has_link_capacity_kbps()) { + config.link_capacity = + DataRate::KilobitsPerSec(schedule_item.link_capacity_kbps()); + } + if (schedule_item.has_loss_percent()) { + config.loss_percent = schedule_item.loss_percent(); + } + if (schedule_item.has_delay_standard_deviation_ms()) { + config.delay_standard_deviation_ms = + schedule_item.delay_standard_deviation_ms(); + } + if (schedule_item.has_allow_reordering()) { + config.allow_reordering = schedule_item.allow_reordering(); + } + if (schedule_item.has_avg_burst_loss_length()) { + config.avg_burst_loss_length = schedule_item.avg_burst_loss_length(); + } + if (schedule_item.has_packet_overhead()) { + config.packet_overhead = schedule_item.packet_overhead(); + } +} + +BuiltInNetworkBehaviorConfig GetInitialConfig( + const network_behaviour::NetworkConfigSchedule& schedule) { + BuiltInNetworkBehaviorConfig config; + if (!schedule.item().empty()) { + UpdateConfigFromSchedule(schedule.item(0), config); + } + return config; +} + +} // namespace + +SchedulableNetworkBehavior::SchedulableNetworkBehavior( + network_behaviour::NetworkConfigSchedule schedule, + webrtc::Clock& clock) + : SimulatedNetwork(GetInitialConfig(schedule)), + schedule_(std::move(schedule)), + clock_(clock), + config_(GetInitialConfig(schedule_)) { + if (schedule_.item().size() > 1) { + next_schedule_index_ = 1; + } + sequence_checker_.Detach(); +} + +bool SchedulableNetworkBehavior::EnqueuePacket( + webrtc::PacketInFlightInfo packet_info) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + if (first_send_time_.IsMinusInfinity()) { + first_send_time_ = webrtc::Timestamp::Micros(packet_info.send_time_us); + if (schedule_.item().size() > 1) { + RTC_CHECK_LT(next_schedule_index_, schedule_.item().size()); + webrtc::TimeDelta delay = + webrtc::TimeDelta::Millis(schedule_.item()[next_schedule_index_] + .time_since_first_sent_packet_ms()); + schedule_task_ = RepeatingTaskHandle::DelayedStart( + webrtc::TaskQueueBase::Current(), delay, + [this] { return UpdateConfigAndReschedule(); }); + } + } + return SimulatedNetwork::EnqueuePacket(packet_info); +} + +TimeDelta SchedulableNetworkBehavior::UpdateConfigAndReschedule() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + Timestamp reschedule_time = clock_.CurrentTime(); + RTC_CHECK_LT(next_schedule_index_, schedule_.item().size()); + + auto next_config = schedule_.item()[next_schedule_index_]; + UpdateConfigFromSchedule(next_config, config_); + SimulatedNetwork::SetConfig(config_, reschedule_time); + next_schedule_index_ = ++next_schedule_index_ % schedule_.item().size(); + webrtc::TimeDelta delay = webrtc::TimeDelta::Zero(); + webrtc::TimeDelta time_since_first_sent_packet = + reschedule_time - first_send_time_; + if (next_schedule_index_ != 0) { + delay = webrtc::TimeDelta::Millis(schedule_.item()[next_schedule_index_] + .time_since_first_sent_packet_ms()) - + (time_since_first_sent_packet - wrap_time_delta_); + } else if (!schedule_.has_repeat_schedule_after_last_ms()) { + // No more schedule items. + schedule_task_.Stop(); + return TimeDelta::Zero(); // This is ignored. + } else { + // Wrap around to the first schedule item. + wrap_time_delta_ += + TimeDelta::Millis(schedule_.repeat_schedule_after_last_ms()) + + TimeDelta::Millis(schedule_.item()[schedule_.item().size() - 1] + .time_since_first_sent_packet_ms()); + delay = + webrtc::TimeDelta::Millis(schedule_.repeat_schedule_after_last_ms()); + RTC_DCHECK_GE(delay, TimeDelta::Zero()); + } + + return delay; +} + +} // namespace webrtc diff --git a/test/network/schedulable_network_behavior.h b/test/network/schedulable_network_behavior.h new file mode 100644 index 0000000000..1781368c98 --- /dev/null +++ b/test/network/schedulable_network_behavior.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 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. + */ + +#ifndef TEST_NETWORK_SCHEDULABLE_NETWORK_BEHAVIOR_H_ +#define TEST_NETWORK_SCHEDULABLE_NETWORK_BEHAVIOR_H_ + +#include "api/sequence_checker.h" +#include "api/test/network_emulation/network_config_schedule.pb.h" +#include "api/test/simulated_network.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "rtc_base/task_utils/repeating_task.h" +#include "rtc_base/thread_annotations.h" +#include "system_wrappers/include/clock.h" +#include "test/network/simulated_network.h" + +namespace webrtc { + +// Network behaviour implementation where parameters change over time as +// specified with a schedule proto. +class SchedulableNetworkBehavior : public SimulatedNetwork { + public: + SchedulableNetworkBehavior(network_behaviour::NetworkConfigSchedule schedule, + Clock& clock); + + bool EnqueuePacket(PacketInFlightInfo packet_info) override; + + private: + TimeDelta UpdateConfigAndReschedule(); + + SequenceChecker sequence_checker_; + const network_behaviour::NetworkConfigSchedule schedule_; + Timestamp first_send_time_ RTC_GUARDED_BY(&sequence_checker_) = + Timestamp::MinusInfinity(); + + Clock& clock_ RTC_GUARDED_BY(&sequence_checker_); + BuiltInNetworkBehaviorConfig config_ RTC_GUARDED_BY(&sequence_checker_); + // Index of the next schedule item to apply. + int next_schedule_index_ RTC_GUARDED_BY(&sequence_checker_) = 0; + // Total time from the first sent packet, until the last time the schedule + // repeat. + TimeDelta wrap_time_delta_ RTC_GUARDED_BY(&sequence_checker_) = + TimeDelta::Zero(); + RepeatingTaskHandle schedule_task_ RTC_GUARDED_BY(&sequence_checker_); +}; + +} // namespace webrtc + +#endif // TEST_NETWORK_SCHEDULABLE_NETWORK_BEHAVIOR_H_ diff --git a/test/network/schedulable_network_behavior_test.cc b/test/network/schedulable_network_behavior_test.cc new file mode 100644 index 0000000000..054ea69a2c --- /dev/null +++ b/test/network/schedulable_network_behavior_test.cc @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2024 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 "test/network/schedulable_network_behavior.h" + +#include +#include +#include + +#include "api/test/create_network_emulation_manager.h" +#include "api/test/network_emulation_manager.h" +#include "api/test/simulated_network.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" + +namespace webrtc { +namespace { + +using ::testing::Mock; +using ::testing::MockFunction; +using ::testing::SizeIs; + +class SchedulableNetworkBehaviorTestFixture { + public: + SchedulableNetworkBehaviorTestFixture() + : manager_(webrtc::CreateNetworkEmulationManager( + {.time_mode = TimeMode::kSimulated})) {} + + webrtc::Clock& clock() const { + return *manager_->time_controller()->GetClock(); + } + void AdvanceTime(webrtc::TimeDelta delta) { + manager_->time_controller()->AdvanceTime(delta); + } + void AdvanceTimeTo(int64_t timestamp_us) { + TimeDelta delta = Timestamp::Micros(timestamp_us) - TimeNow(); + ASSERT_GE(delta, TimeDelta::Zero()); + manager_->time_controller()->AdvanceTime(delta); + } + + webrtc::Timestamp TimeNow() const { + return manager_->time_controller()->GetClock()->CurrentTime(); + } + + private: + const std::unique_ptr manager_; +}; + +TEST(SchedulableNetworkBehaviorTest, NoSchedule) { + SchedulableNetworkBehaviorTestFixture fixture; + + network_behaviour::NetworkConfigSchedule schedule; + SchedulableNetworkBehavior network_behaviour(schedule, fixture.clock()); + webrtc::Timestamp send_time = fixture.TimeNow(); + EXPECT_TRUE(network_behaviour.EnqueuePacket({/*size=*/1000 / 8, + /*send_time_us=*/send_time.us(), + /*packet_id=*/1})); + ASSERT_TRUE(network_behaviour.NextDeliveryTimeUs().has_value()); + fixture.AdvanceTimeTo(*network_behaviour.NextDeliveryTimeUs()); + EXPECT_THAT( + network_behaviour.DequeueDeliverablePackets(fixture.TimeNow().us()), + SizeIs(1)); +} + +TEST(SchedulableNetworkBehaviorTest, ScheduleWithoutUpdates) { + SchedulableNetworkBehaviorTestFixture fixture; + + network_behaviour::NetworkConfigSchedule schedule; + auto initial_config = schedule.add_item(); + initial_config->set_link_capacity_kbps(10); + initial_config->set_queue_delay_ms(70); + + SchedulableNetworkBehavior network_behaviour(schedule, fixture.clock()); + webrtc::Timestamp send_time = fixture.TimeNow(); + EXPECT_TRUE(network_behaviour.EnqueuePacket({/*size=*/1000 / 8, + /*send_time_us=*/send_time.us(), + /*packet_id=*/1})); + + // 1000 bits, on a 10kbps link should take 100ms + 70 extra. + // The network_behaviour at the time of writing this test needs two calls + // to NextDeliveryTimeUs to before the packet is delivered (one for the link + // capacity queue and one for the queue delay). + std::vector packet_delivery_infos; + while (packet_delivery_infos.empty()) { + ASSERT_TRUE(network_behaviour.NextDeliveryTimeUs().has_value()); + fixture.AdvanceTimeTo(*network_behaviour.NextDeliveryTimeUs()); + packet_delivery_infos = + network_behaviour.DequeueDeliverablePackets(fixture.TimeNow().us()); + } + EXPECT_EQ(fixture.TimeNow(), send_time + TimeDelta::Millis(170)); + ASSERT_THAT(packet_delivery_infos, SizeIs(1)); + EXPECT_EQ(packet_delivery_infos[0].packet_id, 1u); + EXPECT_EQ(packet_delivery_infos[0].receive_time_us, send_time.us() + 170'000); +} + +TEST(SchedulableNetworkBehaviorTest, + TriggersDeliveryTimeChangedCallbackOnScheduleIfPacketInLinkCapacityQueue) { + SchedulableNetworkBehaviorTestFixture fixture; + network_behaviour::NetworkConfigSchedule schedule; + auto initial_config = schedule.add_item(); + // A packet of size 1000 bits should take 100ms to send. + initial_config->set_link_capacity_kbps(10); + initial_config->set_queue_delay_ms(10); + auto updated_capacity = schedule.add_item(); + updated_capacity->set_time_since_first_sent_packet_ms(50); + // A packet of size 1000 bits should take 10ms to send. But since "half" the + // first packet has passed the narrow section, it should take 50ms + 500/100 = + // 55ms. + updated_capacity->set_link_capacity_kbps(100); + + SchedulableNetworkBehavior network_behaviour(schedule, fixture.clock()); + MockFunction delivery_time_changed_callback; + network_behaviour.RegisterDeliveryTimeChangedCallback( + delivery_time_changed_callback.AsStdFunction()); + + webrtc::Timestamp first_packet_send_time = fixture.TimeNow(); + EXPECT_CALL(delivery_time_changed_callback, Call).WillOnce([&]() { + EXPECT_EQ(fixture.TimeNow(), + first_packet_send_time + TimeDelta::Millis(50)); + ASSERT_TRUE(network_behaviour.NextDeliveryTimeUs().has_value()); + }); + EXPECT_TRUE(network_behaviour.EnqueuePacket( + {/*size=*/1000 / 8, + /*send_time_us=*/first_packet_send_time.us(), + /*packet_id=*/1})); + fixture.AdvanceTime( + TimeDelta::Millis(updated_capacity->time_since_first_sent_packet_ms())); + Mock::VerifyAndClearExpectations(&delivery_time_changed_callback); + ASSERT_TRUE(network_behaviour.NextDeliveryTimeUs().has_value()); + fixture.AdvanceTime( + TimeDelta::Micros(*network_behaviour.NextDeliveryTimeUs())); + std::vector dequeued_packets = + network_behaviour.DequeueDeliverablePackets(fixture.TimeNow().us()); + ASSERT_FALSE(dequeued_packets.empty()); + EXPECT_EQ(dequeued_packets[0].receive_time_us, + (first_packet_send_time + TimeDelta::Millis(55) + + /*queue_delay=*/TimeDelta::Millis(10)) + .us()); +} + +TEST(SchedulableNetworkBehaviorTest, ScheduleWithRepeat) { + SchedulableNetworkBehaviorTestFixture fixture; + network_behaviour::NetworkConfigSchedule schedule; + auto initial_config = schedule.add_item(); + // A packet of size 1000 bits should take 100ms to send. + initial_config->set_link_capacity_kbps(10); + auto updated_capacity = schedule.add_item(); + updated_capacity->set_time_since_first_sent_packet_ms(150); + // A packet of size 1000 bits should take 10ms to send. + updated_capacity->set_link_capacity_kbps(100); + // A packet of size 1000 bits, scheduled 200ms after the last update to the + // config should again take 100ms to send. + schedule.set_repeat_schedule_after_last_ms(200); + + SchedulableNetworkBehavior network_behaviour(schedule, fixture.clock()); + + webrtc::Timestamp first_packet_send_time = fixture.TimeNow(); + EXPECT_TRUE(network_behaviour.EnqueuePacket( + {/*size=*/1000 / 8, + /*send_time_us=*/first_packet_send_time.us(), + /*packet_id=*/1})); + ASSERT_TRUE(network_behaviour.NextDeliveryTimeUs().has_value()); + EXPECT_EQ(*network_behaviour.NextDeliveryTimeUs(), + fixture.TimeNow().us() + TimeDelta::Millis(100).us()); + fixture.AdvanceTimeTo(*network_behaviour.NextDeliveryTimeUs()); + EXPECT_THAT( + network_behaviour.DequeueDeliverablePackets(fixture.TimeNow().us()), + SizeIs(1)); + fixture.AdvanceTime( + TimeDelta::Millis(updated_capacity->time_since_first_sent_packet_ms() + + schedule.repeat_schedule_after_last_ms() - + /*time already advanced*/ 100)); + // Schedule should be repeated. + // A packet of size 1000 bits should take 100ms to send. + EXPECT_TRUE( + network_behaviour.EnqueuePacket({/*size=*/1000 / 8, + /*send_time_us=*/fixture.TimeNow().us(), + /*packet_id=*/2})); + ASSERT_TRUE(network_behaviour.NextDeliveryTimeUs().has_value()); + EXPECT_EQ(*network_behaviour.NextDeliveryTimeUs(), + fixture.TimeNow().us() + TimeDelta::Millis(100).us()); +} + +TEST(SchedulableNetworkBehaviorTest, ScheduleWithoutRepeat) { + SchedulableNetworkBehaviorTestFixture fixture; + network_behaviour::NetworkConfigSchedule schedule; + auto initial_config = schedule.add_item(); + // A packet of size 1000 bits should take 100ms to send. + initial_config->set_link_capacity_kbps(10); + auto updated_capacity = schedule.add_item(); + updated_capacity->set_time_since_first_sent_packet_ms(150); + // A packet of size 1000 bits should take 10ms to send. + updated_capacity->set_link_capacity_kbps(100); + + SchedulableNetworkBehavior network_behaviour(schedule, fixture.clock()); + + webrtc::Timestamp first_packet_send_time = fixture.TimeNow(); + EXPECT_TRUE(network_behaviour.EnqueuePacket( + {/*size=*/1000 / 8, + /*send_time_us=*/first_packet_send_time.us(), + /*packet_id=*/1})); + ASSERT_TRUE(network_behaviour.NextDeliveryTimeUs().has_value()); + EXPECT_EQ(*network_behaviour.NextDeliveryTimeUs(), + fixture.TimeNow().us() + TimeDelta::Millis(100).us()); + fixture.AdvanceTimeTo(*network_behaviour.NextDeliveryTimeUs()); + EXPECT_THAT( + network_behaviour.DequeueDeliverablePackets(fixture.TimeNow().us()), + SizeIs(1)); + // Advance time to when the updated capacity should be in effect and add one + // minute. The updated capacity should still be in affect. + fixture.AdvanceTime( + TimeDelta::Millis(updated_capacity->time_since_first_sent_packet_ms() - + /*time already advanced*/ 100) + + TimeDelta::Minutes(1)); + + // Schedule should not be repeated. + // A packet of size 1000 bits should take 10ms to send. + EXPECT_TRUE( + network_behaviour.EnqueuePacket({/*size=*/1000 / 8, + /*send_time_us=*/fixture.TimeNow().us(), + /*packet_id=*/2})); + ASSERT_TRUE(network_behaviour.NextDeliveryTimeUs().has_value()); + EXPECT_EQ(*network_behaviour.NextDeliveryTimeUs(), + fixture.TimeNow().us() + TimeDelta::Millis(10).us()); + fixture.AdvanceTimeTo(*network_behaviour.NextDeliveryTimeUs()); + EXPECT_THAT( + network_behaviour.DequeueDeliverablePackets(fixture.TimeNow().us()), + SizeIs(1)); +} + +} // namespace +} // namespace webrtc diff --git a/test/peer_scenario/tests/BUILD.gn b/test/peer_scenario/tests/BUILD.gn index 5e72e4e177..7e457c0e34 100644 --- a/test/peer_scenario/tests/BUILD.gn +++ b/test/peer_scenario/tests/BUILD.gn @@ -19,6 +19,7 @@ if (rtc_include_tests) { ] deps = [ "..:peer_scenario", + "../../:create_frame_generator_capturer", "../../:field_trial", "../../:test_support", "../../../api:rtc_stats_api", @@ -30,5 +31,11 @@ if (rtc_include_tests) { "../../../pc:pc_test_utils", "../../../pc:session_description", ] + if (rtc_enable_protobuf) { + deps += [ + "../../../api/test/network_emulation:network_config_schedule_proto", + "../../../api/test/network_emulation:schedulable_network_node_builder", + ] + } } } diff --git a/test/peer_scenario/tests/bwe_ramp_up_test.cc b/test/peer_scenario/tests/bwe_ramp_up_test.cc index 909d98924d..9879ed5336 100644 --- a/test/peer_scenario/tests/bwe_ramp_up_test.cc +++ b/test/peer_scenario/tests/bwe_ramp_up_test.cc @@ -8,6 +8,9 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include +#include + #include "api/stats/rtcstats_objects.h" #include "api/units/data_rate.h" #include "api/units/time_delta.h" @@ -16,11 +19,16 @@ #include "modules/rtp_rtcp/source/rtp_util.h" #include "pc/media_session.h" #include "pc/test/mock_peer_connection_observers.h" +#include "test/create_frame_generator_capturer.h" #include "test/gmock.h" #include "test/gtest.h" #include "test/peer_scenario/peer_scenario.h" #include "test/peer_scenario/peer_scenario_client.h" +#if WEBRTC_ENABLE_PROTOBUF +#include "api/test/network_emulation/schedulable_network_node_builder.h" +#endif + namespace webrtc { namespace test { @@ -49,6 +57,55 @@ DataRate GetAvailableSendBitrate( return DataRate::BitsPerSec(*stats[0]->available_outgoing_bitrate); } +#if WEBRTC_ENABLE_PROTOBUF +TEST(BweRampupTest, BweRampUpWhenCapacityIncrease) { + PeerScenario s(*test_info_); + + PeerScenarioClient* caller = s.CreateClient({}); + PeerScenarioClient* callee = s.CreateClient({}); + + network_behaviour::NetworkConfigSchedule schedule; + auto initial_config = schedule.add_item(); + initial_config->set_link_capacity_kbps(500); + auto updated_capacity = schedule.add_item(); + updated_capacity->set_time_since_first_sent_packet_ms(3000); + updated_capacity->set_link_capacity_kbps(3000); + SchedulableNetworkNodeBuilder schedulable_builder(*s.net(), + std::move(schedule)); + + auto caller_node = schedulable_builder.Build(); + auto callee_node = s.net()->NodeBuilder().capacity_kbps(5000).Build().node; + s.net()->CreateRoute(caller->endpoint(), {caller_node}, callee->endpoint()); + s.net()->CreateRoute(callee->endpoint(), {callee_node}, caller->endpoint()); + + FrameGeneratorCapturerConfig::SquaresVideo video_resolution = { + .framerate = 30, .width = 1280, .height = 720}; + PeerScenarioClient::VideoSendTrack track = caller->CreateVideo( + "VIDEO", {.generator = {.squares_video = video_resolution}}); + + auto signaling = + s.ConnectSignaling(caller, callee, {caller_node}, {callee_node}); + + signaling.StartIceSignaling(); + + std::atomic offer_exchange_done(false); + signaling.NegotiateSdp([&](const SessionDescriptionInterface& answer) { + offer_exchange_done = true; + }); + // Wait for SDP negotiation. + s.WaitAndProcess(&offer_exchange_done); + + s.ProcessMessages(TimeDelta::Seconds(5)); + DataRate bwe_before_capacity_increase = + GetAvailableSendBitrate(GetStatsAndProcess(s, caller)); + EXPECT_GT(bwe_before_capacity_increase.kbps(), 300); + EXPECT_LT(bwe_before_capacity_increase.kbps(), 650); + s.ProcessMessages(TimeDelta::Seconds(15)); + EXPECT_GT(GetAvailableSendBitrate(GetStatsAndProcess(s, caller)).kbps(), + 1000); +} +#endif // WEBRTC_ENABLE_PROTOBUF + // Test that caller BWE can rampup even if callee can not demux incoming RTP // packets. TEST(BweRampupTest, RampUpWithUndemuxableRtpPackets) {