| /* |
| * 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::Return; |
| using ::testing::Sequence; |
| using ::testing::SizeIs; |
| |
| constexpr uint64_t kRandomSeed = 1; |
| |
| 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, kRandomSeed, |
| 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, kRandomSeed, |
| 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, kRandomSeed, |
| 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, ScheduleStartedWhenStartConditionTrue) { |
| SchedulableNetworkBehaviorTestFixture fixture; |
| network_behaviour::NetworkConfigSchedule schedule; |
| auto initial_config = schedule.add_item(); |
| initial_config->set_link_capacity_kbps(0); |
| auto item = schedule.add_item(); |
| item->set_time_since_first_sent_packet_ms(1); |
| item->set_link_capacity_kbps(1000000); |
| |
| MockFunction<bool(Timestamp)> start_condition; |
| webrtc::Timestamp first_packet_send_time = fixture.TimeNow(); |
| webrtc::Timestamp second_packet_send_time = |
| fixture.TimeNow() + TimeDelta::Millis(100); |
| Sequence s; |
| EXPECT_CALL(start_condition, Call(first_packet_send_time)) |
| .InSequence(s) |
| .WillOnce(Return(false)); |
| // Expect schedule to start when the second packet is sent. |
| EXPECT_CALL(start_condition, Call(second_packet_send_time)) |
| .InSequence(s) |
| .WillOnce(Return(true)); |
| SchedulableNetworkBehavior network_behaviour( |
| schedule, kRandomSeed, fixture.clock(), start_condition.AsStdFunction()); |
| |
| EXPECT_TRUE(network_behaviour.EnqueuePacket( |
| {/*size=*/1000 / 8, |
| /*send_time_us=*/first_packet_send_time.us(), |
| /*packet_id=*/1})); |
| EXPECT_FALSE(network_behaviour.NextDeliveryTimeUs().has_value()); |
| // Move passed the normal schedule change time. Still dont expect a delivery |
| // time. |
| fixture.AdvanceTime(TimeDelta::Millis(100)); |
| EXPECT_FALSE(network_behaviour.NextDeliveryTimeUs().has_value()); |
| |
| EXPECT_TRUE(network_behaviour.EnqueuePacket( |
| {/*size=*/1000 / 8, |
| /*send_time_us=*/second_packet_send_time.us(), |
| /*packet_id=*/2})); |
| |
| EXPECT_FALSE(network_behaviour.NextDeliveryTimeUs().has_value()); |
| fixture.AdvanceTime(TimeDelta::Millis(1)); |
| EXPECT_TRUE(network_behaviour.NextDeliveryTimeUs().has_value()); |
| } |
| |
| 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, kRandomSeed, |
| 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, kRandomSeed, |
| 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 |