|  | /* | 
|  | *  Copyright 2019 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 "call/simulated_network.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <map> | 
|  | #include <set> | 
|  | #include <vector> | 
|  |  | 
|  | #include "absl/algorithm/container.h" | 
|  | #include "api/units/data_rate.h" | 
|  | #include "test/gtest.h" | 
|  |  | 
|  | namespace webrtc { | 
|  | namespace { | 
|  | constexpr int kNotReceived = PacketDeliveryInfo::kNotReceived; | 
|  | } | 
|  |  | 
|  | TEST(SimulatedNetworkTest, CodelDoesNothingAtCapacity) { | 
|  | const TimeDelta kRuntime = TimeDelta::seconds(30); | 
|  |  | 
|  | DataRate link_capacity = DataRate::kbps(1000); | 
|  | const DataSize packet_size = DataSize::bytes(1000); | 
|  |  | 
|  | SimulatedNetwork::Config config; | 
|  | config.codel_active_queue_management = true; | 
|  | config.queue_delay_ms = 10; | 
|  | config.link_capacity_kbps = link_capacity.kbps(); | 
|  | SimulatedNetwork network(config); | 
|  |  | 
|  | // Need to round up here as otherwise we actually will choke. | 
|  | const TimeDelta packet_inverval = | 
|  | packet_size / link_capacity + TimeDelta::ms(1); | 
|  |  | 
|  | // Send at capacity and see we get no loss. | 
|  | Timestamp start_time = Timestamp::ms(0); | 
|  | Timestamp current_time = start_time; | 
|  | Timestamp next_packet_time = start_time; | 
|  | uint64_t next_id = 0; | 
|  | std::set<uint64_t> pending; | 
|  | while (current_time - start_time < kRuntime) { | 
|  | if (current_time >= next_packet_time) { | 
|  | bool success = network.EnqueuePacket(PacketInFlightInfo{ | 
|  | packet_size.bytes<size_t>(), current_time.us(), next_id}); | 
|  | EXPECT_TRUE(success); | 
|  | pending.insert(next_id); | 
|  | ++next_id; | 
|  | next_packet_time += packet_inverval; | 
|  | } | 
|  | Timestamp next_delivery = Timestamp::PlusInfinity(); | 
|  | if (network.NextDeliveryTimeUs()) | 
|  | next_delivery = Timestamp::us(*network.NextDeliveryTimeUs()); | 
|  | current_time = std::min(next_packet_time, next_delivery); | 
|  | if (current_time >= next_delivery) { | 
|  | for (PacketDeliveryInfo packet : | 
|  | network.DequeueDeliverablePackets(current_time.us())) { | 
|  | EXPECT_NE(packet.receive_time_us, kNotReceived); | 
|  | pending.erase(packet.packet_id); | 
|  | } | 
|  | } | 
|  | } | 
|  | while (network.NextDeliveryTimeUs()) { | 
|  | for (PacketDeliveryInfo packet : | 
|  | network.DequeueDeliverablePackets(*network.NextDeliveryTimeUs())) { | 
|  | EXPECT_NE(packet.receive_time_us, kNotReceived); | 
|  | pending.erase(packet.packet_id); | 
|  | } | 
|  | } | 
|  | EXPECT_EQ(pending.size(), 0u); | 
|  | } | 
|  |  | 
|  | TEST(SimulatedNetworkTest, CodelLimitsDelayAndDropsPacketsOnOverload) { | 
|  | const TimeDelta kRuntime = TimeDelta::seconds(30); | 
|  | const TimeDelta kCheckInterval = TimeDelta::ms(2000); | 
|  |  | 
|  | DataRate link_capacity = DataRate::kbps(1000); | 
|  | const DataSize rough_packet_size = DataSize::bytes(1500); | 
|  | const double overload_rate = 1.5; | 
|  |  | 
|  | SimulatedNetwork::Config config; | 
|  | config.codel_active_queue_management = true; | 
|  | config.queue_delay_ms = 10; | 
|  | config.link_capacity_kbps = link_capacity.kbps(); | 
|  | SimulatedNetwork network(config); | 
|  |  | 
|  | const TimeDelta packet_inverval = rough_packet_size / link_capacity; | 
|  | const DataSize packet_size = overload_rate * link_capacity * packet_inverval; | 
|  | // Send above capacity and see delays are still controlled at the cost of | 
|  | // packet loss. | 
|  | Timestamp start_time = Timestamp::ms(0); | 
|  | Timestamp current_time = start_time; | 
|  | Timestamp next_packet_time = start_time; | 
|  | Timestamp last_check = start_time; | 
|  | uint64_t next_id = 1; | 
|  | std::map<uint64_t, int64_t> send_times_us; | 
|  | int lost = 0; | 
|  | std::vector<int64_t> delays_us; | 
|  | while (current_time - start_time < kRuntime) { | 
|  | if (current_time >= next_packet_time) { | 
|  | bool success = network.EnqueuePacket(PacketInFlightInfo{ | 
|  | packet_size.bytes<size_t>(), current_time.us(), next_id}); | 
|  | send_times_us.insert({next_id, current_time.us()}); | 
|  | ++next_id; | 
|  | EXPECT_TRUE(success); | 
|  | next_packet_time += packet_inverval; | 
|  | } | 
|  | Timestamp next_delivery = Timestamp::PlusInfinity(); | 
|  | if (network.NextDeliveryTimeUs()) | 
|  | next_delivery = Timestamp::us(*network.NextDeliveryTimeUs()); | 
|  | current_time = std::min(next_packet_time, next_delivery); | 
|  | if (current_time >= next_delivery) { | 
|  | for (PacketDeliveryInfo packet : | 
|  | network.DequeueDeliverablePackets(current_time.us())) { | 
|  | if (packet.receive_time_us == kNotReceived) { | 
|  | ++lost; | 
|  | } else { | 
|  | delays_us.push_back(packet.receive_time_us - | 
|  | send_times_us[packet.packet_id]); | 
|  | } | 
|  | send_times_us.erase(packet.packet_id); | 
|  | } | 
|  | } | 
|  | if (current_time > last_check + kCheckInterval) { | 
|  | last_check = current_time; | 
|  | TimeDelta average_delay = | 
|  | TimeDelta::us(absl::c_accumulate(delays_us, 0)) / delays_us.size(); | 
|  | double loss_ratio = static_cast<double>(lost) / (lost + delays_us.size()); | 
|  | EXPECT_LT(average_delay.ms(), 200) | 
|  | << "Time " << (current_time - start_time).ms() << "\n"; | 
|  | EXPECT_GT(loss_ratio, 0.5 * (overload_rate - 1)); | 
|  | } | 
|  | } | 
|  | while (network.NextDeliveryTimeUs()) { | 
|  | for (PacketDeliveryInfo packet : | 
|  | network.DequeueDeliverablePackets(*network.NextDeliveryTimeUs())) { | 
|  | send_times_us.erase(packet.packet_id); | 
|  | } | 
|  | } | 
|  | EXPECT_EQ(send_times_us.size(), 0u); | 
|  | } | 
|  | }  // namespace webrtc |