Add SchedulableNetworkBehavior and tests.
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 <terelius@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Commit-Queue: Per Kjellander <perkj@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42390}
diff --git a/BUILD.gn b/BUILD.gn
index f090f16..7328df1 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -678,7 +678,10 @@
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 f28fd35..d4aa23b 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 0000000..0e1036c
--- /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 0000000..fd5a26c
--- /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 <memory>
+#include <utility>
+
+#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<SchedulableNetworkBehavior>(
+ 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 0000000..5e705c9
--- /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 10c0c4e..abcd110 100644
--- a/test/network/BUILD.gn
+++ b/test/network/BUILD.gn
@@ -199,6 +199,9 @@
":network_emulation_unittest",
":simulated_network_unittest",
]
+ if (rtc_enable_protobuf) {
+ deps += [ ":schedulable_network_behavior_test" ]
+ }
}
}
}
@@ -241,3 +244,49 @@
]
}
}
+
+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 0000000..32f5305
--- /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 <utility>
+
+#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 0000000..1781368
--- /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 0000000..054ea69
--- /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 <cstdint>
+#include <memory>
+#include <vector>
+
+#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<webrtc::NetworkEmulationManager> 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<webrtc::PacketDeliveryInfo> 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<void()> 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<PacketDeliveryInfo> 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 5e72e4e..7e457c0 100644
--- a/test/peer_scenario/tests/BUILD.gn
+++ b/test/peer_scenario/tests/BUILD.gn
@@ -19,6 +19,7 @@
]
deps = [
"..:peer_scenario",
+ "../../:create_frame_generator_capturer",
"../../:field_trial",
"../../:test_support",
"../../../api:rtc_stats_api",
@@ -30,5 +31,11 @@
"../../../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 909d989..9879ed5 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 <atomic>
+#include <utility>
+
#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 @@
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<bool> 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) {