Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | #include <cstdint> |
| 11 | #include <deque> |
| 12 | #include <memory> |
Florent Castelli | 8037fc6 | 2024-08-29 13:00:40 | [diff] [blame] | 13 | #include <optional> |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 14 | #include <string> |
| 15 | #include <utility> |
| 16 | #include <vector> |
| 17 | |
| 18 | #include "absl/memory/memory.h" |
| 19 | #include "absl/strings/string_view.h" |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 20 | #include "api/array_view.h" |
Danil Chapovalov | 6ba4b63 | 2022-08-17 14:51:36 | [diff] [blame] | 21 | #include "api/task_queue/pending_task_safety_flag.h" |
Henrik Boström | b951dc6 | 2022-01-26 17:38:13 | [diff] [blame] | 22 | #include "api/task_queue/task_queue_base.h" |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 23 | #include "api/test/create_network_emulation_manager.h" |
| 24 | #include "api/test/network_emulation_manager.h" |
Per K | 5566b91 | 2024-05-15 15:53:02 | [diff] [blame] | 25 | #include "api/units/data_rate.h" |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 26 | #include "api/units/time_delta.h" |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 27 | #include "net/dcsctp/public/dcsctp_options.h" |
| 28 | #include "net/dcsctp/public/dcsctp_socket.h" |
| 29 | #include "net/dcsctp/public/types.h" |
| 30 | #include "net/dcsctp/socket/dcsctp_socket.h" |
| 31 | #include "net/dcsctp/testing/testing_macros.h" |
| 32 | #include "net/dcsctp/timer/task_queue_timeout.h" |
| 33 | #include "rtc_base/copy_on_write_buffer.h" |
| 34 | #include "rtc_base/gunit.h" |
| 35 | #include "rtc_base/logging.h" |
Dor Hen | 1921fa5 | 2024-08-13 12:43:47 | [diff] [blame] | 36 | #include "rtc_base/random.h" |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 37 | #include "rtc_base/socket_address.h" |
| 38 | #include "rtc_base/strings/string_format.h" |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 39 | #include "rtc_base/time_utils.h" |
| 40 | #include "test/gmock.h" |
| 41 | |
Victor Boivie | ec19d5e | 2021-10-28 12:45:20 | [diff] [blame] | 42 | #if !defined(WEBRTC_ANDROID) && defined(NDEBUG) && \ |
| 43 | !defined(THREAD_SANITIZER) && !defined(MEMORY_SANITIZER) |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 44 | #define DCSCTP_NDEBUG_TEST(t) t |
| 45 | #else |
Victor Boivie | ec19d5e | 2021-10-28 12:45:20 | [diff] [blame] | 46 | // In debug mode, and when MSAN or TSAN sanitizers are enabled, these tests are |
| 47 | // too expensive to run due to extensive consistency checks that iterate on all |
| 48 | // outstanding chunks. Same with low-end Android devices, which have |
| 49 | // difficulties with these tests. |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 50 | #define DCSCTP_NDEBUG_TEST(t) DISABLED_##t |
| 51 | #endif |
| 52 | |
| 53 | namespace dcsctp { |
| 54 | namespace { |
| 55 | using ::testing::AllOf; |
| 56 | using ::testing::Ge; |
| 57 | using ::testing::Le; |
| 58 | using ::testing::SizeIs; |
Per K | 5566b91 | 2024-05-15 15:53:02 | [diff] [blame] | 59 | using ::webrtc::DataRate; |
Victor Boivie | 82cbbcc | 2023-11-08 15:31:44 | [diff] [blame] | 60 | using ::webrtc::TimeDelta; |
| 61 | using ::webrtc::Timestamp; |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 62 | |
| 63 | constexpr StreamID kStreamId(1); |
| 64 | constexpr PPID kPpid(53); |
| 65 | constexpr size_t kSmallPayloadSize = 10; |
| 66 | constexpr size_t kLargePayloadSize = 10000; |
| 67 | constexpr size_t kHugePayloadSize = 262144; |
| 68 | constexpr size_t kBufferedAmountLowThreshold = kLargePayloadSize * 2; |
Danil Chapovalov | 6ba4b63 | 2022-08-17 14:51:36 | [diff] [blame] | 69 | constexpr webrtc::TimeDelta kPrintBandwidthDuration = |
| 70 | webrtc::TimeDelta::Seconds(1); |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 71 | constexpr webrtc::TimeDelta kBenchmarkRuntime(webrtc::TimeDelta::Seconds(10)); |
| 72 | constexpr webrtc::TimeDelta kAWhile(webrtc::TimeDelta::Seconds(1)); |
| 73 | |
| 74 | inline int GetUniqueSeed() { |
| 75 | static int seed = 0; |
| 76 | return ++seed; |
| 77 | } |
| 78 | |
| 79 | DcSctpOptions MakeOptionsForTest() { |
| 80 | DcSctpOptions options; |
| 81 | |
| 82 | // Throughput numbers are affected by the MTU. Ensure it's constant. |
| 83 | options.mtu = 1200; |
| 84 | |
| 85 | // By disabling the heartbeat interval, there will no timers at all running |
| 86 | // when the socket is idle, which makes it easy to just continue the test |
| 87 | // until there are no more scheduled tasks. Note that it _will_ run for longer |
| 88 | // than necessary as timers aren't cancelled when they are stopped (as that's |
| 89 | // not supported), but it's still simulated time and passes quickly. |
| 90 | options.heartbeat_interval = DurationMs(0); |
| 91 | return options; |
| 92 | } |
| 93 | |
| 94 | // When doing throughput tests, knowing what each actor should do. |
| 95 | enum class ActorMode { |
| 96 | kAtRest, |
| 97 | kThroughputSender, |
| 98 | kThroughputReceiver, |
| 99 | kLimitedRetransmissionSender, |
| 100 | }; |
| 101 | |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 102 | // An abstraction around EmulatedEndpoint, representing a bound socket that |
| 103 | // will send its packet to a given destination. |
| 104 | class BoundSocket : public webrtc::EmulatedNetworkReceiverInterface { |
| 105 | public: |
| 106 | void Bind(webrtc::EmulatedEndpoint* endpoint) { |
| 107 | endpoint_ = endpoint; |
| 108 | uint16_t port = endpoint->BindReceiver(0, this).value(); |
| 109 | source_address_ = |
Evan Shrubsole | 64b076f4 | 2025-03-12 12:56:28 | [diff] [blame] | 110 | webrtc::SocketAddress(endpoint_->GetPeerLocalAddress(), port); |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 111 | } |
| 112 | |
| 113 | void SetDestination(const BoundSocket& socket) { |
| 114 | dest_address_ = socket.source_address_; |
| 115 | } |
| 116 | |
Evan Shrubsole | 8b7780b | 2025-04-15 14:55:02 | [diff] [blame] | 117 | void SetReceiver(std::function<void(webrtc::CopyOnWriteBuffer)> receiver) { |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 118 | receiver_ = std::move(receiver); |
| 119 | } |
| 120 | |
Evan Shrubsole | 8b7780b | 2025-04-15 14:55:02 | [diff] [blame] | 121 | void SendPacket(webrtc::ArrayView<const uint8_t> data) { |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 122 | endpoint_->SendPacket(source_address_, dest_address_, |
Evan Shrubsole | 8b7780b | 2025-04-15 14:55:02 | [diff] [blame] | 123 | webrtc::CopyOnWriteBuffer(data.data(), data.size())); |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 124 | } |
| 125 | |
| 126 | private: |
| 127 | // Implementation of `webrtc::EmulatedNetworkReceiverInterface`. |
| 128 | void OnPacketReceived(webrtc::EmulatedIpPacket packet) override { |
| 129 | receiver_(std::move(packet.data)); |
| 130 | } |
| 131 | |
Evan Shrubsole | 8b7780b | 2025-04-15 14:55:02 | [diff] [blame] | 132 | std::function<void(webrtc::CopyOnWriteBuffer)> receiver_; |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 133 | webrtc::EmulatedEndpoint* endpoint_ = nullptr; |
Evan Shrubsole | 64b076f4 | 2025-03-12 12:56:28 | [diff] [blame] | 134 | webrtc::SocketAddress source_address_; |
| 135 | webrtc::SocketAddress dest_address_; |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 136 | }; |
| 137 | |
| 138 | // Sends at a constant rate but with random packet sizes. |
Danil Chapovalov | 6ba4b63 | 2022-08-17 14:51:36 | [diff] [blame] | 139 | class SctpActor : public DcSctpSocketCallbacks { |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 140 | public: |
| 141 | SctpActor(absl::string_view name, |
| 142 | BoundSocket& emulated_socket, |
| 143 | const DcSctpOptions& sctp_options) |
| 144 | : log_prefix_(std::string(name) + ": "), |
Evan Shrubsole | 6a9a1ae | 2025-03-21 12:54:15 | [diff] [blame] | 145 | thread_(webrtc::Thread::Current()), |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 146 | emulated_socket_(emulated_socket), |
| 147 | timeout_factory_( |
| 148 | *thread_, |
Victor Boivie | 82cbbcc | 2023-11-08 15:31:44 | [diff] [blame] | 149 | [this]() { return TimeMs(Now().ms()); }, |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 150 | [this](dcsctp::TimeoutID timeout_id) { |
| 151 | sctp_socket_.HandleTimeout(timeout_id); |
| 152 | }), |
| 153 | random_(GetUniqueSeed()), |
| 154 | sctp_socket_(name, *this, nullptr, sctp_options), |
Victor Boivie | 82cbbcc | 2023-11-08 15:31:44 | [diff] [blame] | 155 | last_bandwidth_printout_(Now()) { |
Evan Shrubsole | 8b7780b | 2025-04-15 14:55:02 | [diff] [blame] | 156 | emulated_socket.SetReceiver([this](webrtc::CopyOnWriteBuffer buf) { |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 157 | // The receiver will be executed on the NetworkEmulation task queue, but |
| 158 | // the dcSCTP socket is owned by `thread_` and is not thread-safe. |
Danil Chapovalov | ecf88f4 | 2022-07-06 09:05:30 | [diff] [blame] | 159 | thread_->PostTask([this, buf] { this->sctp_socket_.ReceivePacket(buf); }); |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 160 | }); |
| 161 | } |
| 162 | |
Danil Chapovalov | 6ba4b63 | 2022-08-17 14:51:36 | [diff] [blame] | 163 | void PrintBandwidth() { |
Victor Boivie | 82cbbcc | 2023-11-08 15:31:44 | [diff] [blame] | 164 | Timestamp now = Now(); |
| 165 | TimeDelta duration = now - last_bandwidth_printout_; |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 166 | |
Danil Chapovalov | 6ba4b63 | 2022-08-17 14:51:36 | [diff] [blame] | 167 | double bitrate_mbps = |
Victor Boivie | 82cbbcc | 2023-11-08 15:31:44 | [diff] [blame] | 168 | static_cast<double>(received_bytes_ * 8) / duration.ms() / 1000; |
Danil Chapovalov | 6ba4b63 | 2022-08-17 14:51:36 | [diff] [blame] | 169 | RTC_LOG(LS_INFO) << log_prefix() |
Evan Shrubsole | 418a8c2 | 2025-02-18 13:10:10 | [diff] [blame] | 170 | << webrtc::StringFormat("Received %0.2f Mbps", |
| 171 | bitrate_mbps); |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 172 | |
Danil Chapovalov | 6ba4b63 | 2022-08-17 14:51:36 | [diff] [blame] | 173 | received_bitrate_mbps_.push_back(bitrate_mbps); |
| 174 | received_bytes_ = 0; |
| 175 | last_bandwidth_printout_ = now; |
| 176 | // Print again in a second. |
| 177 | if (mode_ == ActorMode::kThroughputReceiver) { |
| 178 | thread_->PostDelayedTask( |
| 179 | SafeTask(safety_.flag(), [this] { PrintBandwidth(); }), |
| 180 | kPrintBandwidthDuration); |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 181 | } |
| 182 | } |
| 183 | |
Evan Shrubsole | 8b7780b | 2025-04-15 14:55:02 | [diff] [blame] | 184 | void SendPacket(webrtc::ArrayView<const uint8_t> data) override { |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 185 | emulated_socket_.SendPacket(data); |
| 186 | } |
| 187 | |
Henrik Boström | b951dc6 | 2022-01-26 17:38:13 | [diff] [blame] | 188 | std::unique_ptr<Timeout> CreateTimeout( |
| 189 | webrtc::TaskQueueBase::DelayPrecision precision) override { |
| 190 | return timeout_factory_.CreateTimeout(precision); |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 191 | } |
| 192 | |
Evan Shrubsole | 652c771 | 2025-03-21 09:47:35 | [diff] [blame] | 193 | Timestamp Now() override { return Timestamp::Millis(webrtc::TimeMillis()); } |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 194 | |
| 195 | uint32_t GetRandomInt(uint32_t low, uint32_t high) override { |
| 196 | return random_.Rand(low, high); |
| 197 | } |
| 198 | |
| 199 | void OnMessageReceived(DcSctpMessage message) override { |
| 200 | received_bytes_ += message.payload().size(); |
| 201 | last_received_message_ = std::move(message); |
| 202 | } |
| 203 | |
| 204 | void OnError(ErrorKind error, absl::string_view message) override { |
| 205 | RTC_LOG(LS_WARNING) << log_prefix() << "Socket error: " << ToString(error) |
| 206 | << "; " << message; |
| 207 | } |
| 208 | |
| 209 | void OnAborted(ErrorKind error, absl::string_view message) override { |
| 210 | RTC_LOG(LS_ERROR) << log_prefix() << "Socket abort: " << ToString(error) |
| 211 | << "; " << message; |
| 212 | } |
| 213 | |
| 214 | void OnConnected() override {} |
| 215 | |
| 216 | void OnClosed() override {} |
| 217 | |
| 218 | void OnConnectionRestarted() override {} |
| 219 | |
Dor Hen | da7b7ca | 2024-11-20 10:15:38 | [diff] [blame] | 220 | void OnStreamsResetFailed( |
Evan Shrubsole | 8b7780b | 2025-04-15 14:55:02 | [diff] [blame] | 221 | webrtc::ArrayView<const StreamID> /* outgoing_streams */, |
Dor Hen | da7b7ca | 2024-11-20 10:15:38 | [diff] [blame] | 222 | absl::string_view /* reason */) override {} |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 223 | |
| 224 | void OnStreamsResetPerformed( |
Evan Shrubsole | 8b7780b | 2025-04-15 14:55:02 | [diff] [blame] | 225 | webrtc::ArrayView<const StreamID> /* outgoing_streams */) override {} |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 226 | |
| 227 | void OnIncomingStreamsReset( |
Evan Shrubsole | 8b7780b | 2025-04-15 14:55:02 | [diff] [blame] | 228 | webrtc::ArrayView<const StreamID> /* incoming_streams */) override {} |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 229 | |
| 230 | void NotifyOutgoingMessageBufferEmpty() override {} |
| 231 | |
Dor Hen | da7b7ca | 2024-11-20 10:15:38 | [diff] [blame] | 232 | void OnBufferedAmountLow(StreamID /* stream_id */) override { |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 233 | if (mode_ == ActorMode::kThroughputSender) { |
| 234 | std::vector<uint8_t> payload(kHugePayloadSize); |
| 235 | sctp_socket_.Send(DcSctpMessage(kStreamId, kPpid, std::move(payload)), |
| 236 | SendOptions()); |
| 237 | |
| 238 | } else if (mode_ == ActorMode::kLimitedRetransmissionSender) { |
| 239 | while (sctp_socket_.buffered_amount(kStreamId) < |
| 240 | kBufferedAmountLowThreshold * 2) { |
| 241 | SendOptions send_options; |
| 242 | send_options.max_retransmissions = 0; |
| 243 | sctp_socket_.Send( |
| 244 | DcSctpMessage(kStreamId, kPpid, |
| 245 | std::vector<uint8_t>(kLargePayloadSize)), |
| 246 | send_options); |
| 247 | |
Florent Castelli | 8037fc6 | 2024-08-29 13:00:40 | [diff] [blame] | 248 | send_options.max_retransmissions = std::nullopt; |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 249 | sctp_socket_.Send( |
| 250 | DcSctpMessage(kStreamId, kPpid, |
| 251 | std::vector<uint8_t>(kSmallPayloadSize)), |
| 252 | send_options); |
| 253 | } |
| 254 | } |
| 255 | } |
| 256 | |
Florent Castelli | 8037fc6 | 2024-08-29 13:00:40 | [diff] [blame] | 257 | std::optional<DcSctpMessage> ConsumeReceivedMessage() { |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 258 | if (!last_received_message_.has_value()) { |
Florent Castelli | 8037fc6 | 2024-08-29 13:00:40 | [diff] [blame] | 259 | return std::nullopt; |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 260 | } |
| 261 | DcSctpMessage ret = *std::move(last_received_message_); |
Florent Castelli | 8037fc6 | 2024-08-29 13:00:40 | [diff] [blame] | 262 | last_received_message_ = std::nullopt; |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 263 | return ret; |
| 264 | } |
| 265 | |
| 266 | DcSctpSocket& sctp_socket() { return sctp_socket_; } |
| 267 | |
| 268 | void SetActorMode(ActorMode mode) { |
| 269 | mode_ = mode; |
| 270 | if (mode_ == ActorMode::kThroughputSender) { |
| 271 | sctp_socket_.SetBufferedAmountLowThreshold(kStreamId, |
| 272 | kBufferedAmountLowThreshold); |
| 273 | std::vector<uint8_t> payload(kHugePayloadSize); |
| 274 | sctp_socket_.Send(DcSctpMessage(kStreamId, kPpid, std::move(payload)), |
| 275 | SendOptions()); |
| 276 | |
| 277 | } else if (mode_ == ActorMode::kLimitedRetransmissionSender) { |
| 278 | sctp_socket_.SetBufferedAmountLowThreshold(kStreamId, |
| 279 | kBufferedAmountLowThreshold); |
| 280 | std::vector<uint8_t> payload(kHugePayloadSize); |
| 281 | sctp_socket_.Send(DcSctpMessage(kStreamId, kPpid, std::move(payload)), |
| 282 | SendOptions()); |
| 283 | |
| 284 | } else if (mode == ActorMode::kThroughputReceiver) { |
Danil Chapovalov | 6ba4b63 | 2022-08-17 14:51:36 | [diff] [blame] | 285 | thread_->PostDelayedTask( |
| 286 | SafeTask(safety_.flag(), [this] { PrintBandwidth(); }), |
| 287 | kPrintBandwidthDuration); |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 288 | } |
| 289 | } |
| 290 | |
| 291 | // Returns the average bitrate, stripping the first `remove_first_n` that |
| 292 | // represent the time it took to ramp up the congestion control algorithm. |
| 293 | double avg_received_bitrate_mbps(size_t remove_first_n = 3) const { |
| 294 | std::vector<double> bitrates = received_bitrate_mbps_; |
| 295 | bitrates.erase(bitrates.begin(), bitrates.begin() + remove_first_n); |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 296 | |
| 297 | double sum = 0; |
| 298 | for (double bitrate : bitrates) { |
| 299 | sum += bitrate; |
| 300 | } |
| 301 | |
| 302 | return sum / bitrates.size(); |
| 303 | } |
| 304 | |
| 305 | private: |
| 306 | std::string log_prefix() const { |
Evan Shrubsole | 0ebd67f | 2025-02-19 10:06:32 | [diff] [blame] | 307 | webrtc::StringBuilder sb; |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 308 | sb << log_prefix_; |
Evan Shrubsole | 652c771 | 2025-03-21 09:47:35 | [diff] [blame] | 309 | sb << webrtc::TimeMillis(); |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 310 | sb << ": "; |
| 311 | return sb.Release(); |
| 312 | } |
| 313 | |
| 314 | ActorMode mode_ = ActorMode::kAtRest; |
| 315 | const std::string log_prefix_; |
Evan Shrubsole | 6a9a1ae | 2025-03-21 12:54:15 | [diff] [blame] | 316 | webrtc::Thread* thread_; |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 317 | BoundSocket& emulated_socket_; |
| 318 | TaskQueueTimeoutFactory timeout_factory_; |
| 319 | webrtc::Random random_; |
| 320 | DcSctpSocket sctp_socket_; |
| 321 | size_t received_bytes_ = 0; |
Florent Castelli | 8037fc6 | 2024-08-29 13:00:40 | [diff] [blame] | 322 | std::optional<DcSctpMessage> last_received_message_; |
Victor Boivie | 82cbbcc | 2023-11-08 15:31:44 | [diff] [blame] | 323 | Timestamp last_bandwidth_printout_; |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 324 | // Per-second received bitrates, in Mbps |
| 325 | std::vector<double> received_bitrate_mbps_; |
Danil Chapovalov | 6ba4b63 | 2022-08-17 14:51:36 | [diff] [blame] | 326 | webrtc::ScopedTaskSafety safety_; |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 327 | }; |
| 328 | |
| 329 | class DcSctpSocketNetworkTest : public testing::Test { |
| 330 | protected: |
| 331 | DcSctpSocketNetworkTest() |
| 332 | : options_(MakeOptionsForTest()), |
| 333 | emulation_(webrtc::CreateNetworkEmulationManager( |
Sergey Sukhanov | 26a082c | 2024-05-08 11:38:00 | [diff] [blame] | 334 | {.time_mode = webrtc::TimeMode::kSimulated})) {} |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 335 | |
| 336 | void MakeNetwork(const webrtc::BuiltInNetworkBehaviorConfig& config) { |
| 337 | webrtc::EmulatedEndpoint* endpoint_a = |
| 338 | emulation_->CreateEndpoint(webrtc::EmulatedEndpointConfig()); |
| 339 | webrtc::EmulatedEndpoint* endpoint_z = |
| 340 | emulation_->CreateEndpoint(webrtc::EmulatedEndpointConfig()); |
| 341 | |
| 342 | webrtc::EmulatedNetworkNode* node1 = emulation_->CreateEmulatedNode(config); |
| 343 | webrtc::EmulatedNetworkNode* node2 = emulation_->CreateEmulatedNode(config); |
| 344 | |
| 345 | emulation_->CreateRoute(endpoint_a, {node1}, endpoint_z); |
| 346 | emulation_->CreateRoute(endpoint_z, {node2}, endpoint_a); |
| 347 | |
| 348 | emulated_socket_a_.Bind(endpoint_a); |
| 349 | emulated_socket_z_.Bind(endpoint_z); |
| 350 | |
| 351 | emulated_socket_a_.SetDestination(emulated_socket_z_); |
| 352 | emulated_socket_z_.SetDestination(emulated_socket_a_); |
| 353 | } |
| 354 | |
| 355 | void Sleep(webrtc::TimeDelta duration) { |
| 356 | // Sleep in one-millisecond increments, to let timers expire when expected. |
| 357 | for (int i = 0; i < duration.ms(); ++i) { |
| 358 | emulation_->time_controller()->AdvanceTime(webrtc::TimeDelta::Millis(1)); |
| 359 | } |
| 360 | } |
| 361 | |
| 362 | DcSctpOptions options_; |
| 363 | std::unique_ptr<webrtc::NetworkEmulationManager> emulation_; |
| 364 | BoundSocket emulated_socket_a_; |
| 365 | BoundSocket emulated_socket_z_; |
| 366 | }; |
| 367 | |
| 368 | TEST_F(DcSctpSocketNetworkTest, CanConnectAndShutdown) { |
| 369 | webrtc::BuiltInNetworkBehaviorConfig pipe_config; |
| 370 | MakeNetwork(pipe_config); |
| 371 | |
| 372 | SctpActor sender("A", emulated_socket_a_, options_); |
| 373 | SctpActor receiver("Z", emulated_socket_z_, options_); |
| 374 | EXPECT_THAT(sender.sctp_socket().state(), SocketState::kClosed); |
| 375 | |
| 376 | sender.sctp_socket().Connect(); |
| 377 | Sleep(kAWhile); |
| 378 | EXPECT_THAT(sender.sctp_socket().state(), SocketState::kConnected); |
| 379 | |
| 380 | sender.sctp_socket().Shutdown(); |
| 381 | Sleep(kAWhile); |
| 382 | EXPECT_THAT(sender.sctp_socket().state(), SocketState::kClosed); |
| 383 | } |
| 384 | |
| 385 | TEST_F(DcSctpSocketNetworkTest, CanSendLargeMessage) { |
| 386 | webrtc::BuiltInNetworkBehaviorConfig pipe_config; |
| 387 | pipe_config.queue_delay_ms = 30; |
| 388 | MakeNetwork(pipe_config); |
| 389 | |
| 390 | SctpActor sender("A", emulated_socket_a_, options_); |
| 391 | SctpActor receiver("Z", emulated_socket_z_, options_); |
| 392 | sender.sctp_socket().Connect(); |
| 393 | |
| 394 | constexpr size_t kPayloadSize = 100 * 1024; |
| 395 | |
| 396 | std::vector<uint8_t> payload(kPayloadSize); |
| 397 | sender.sctp_socket().Send(DcSctpMessage(kStreamId, kPpid, payload), |
| 398 | SendOptions()); |
| 399 | |
| 400 | Sleep(kAWhile); |
| 401 | |
| 402 | ASSERT_HAS_VALUE_AND_ASSIGN(DcSctpMessage message, |
| 403 | receiver.ConsumeReceivedMessage()); |
| 404 | |
| 405 | EXPECT_THAT(message.payload(), SizeIs(kPayloadSize)); |
| 406 | |
| 407 | sender.sctp_socket().Shutdown(); |
| 408 | Sleep(kAWhile); |
| 409 | } |
| 410 | |
| 411 | TEST_F(DcSctpSocketNetworkTest, CanSendMessagesReliablyWithLowBandwidth) { |
| 412 | webrtc::BuiltInNetworkBehaviorConfig pipe_config; |
| 413 | pipe_config.queue_delay_ms = 30; |
Per K | 5566b91 | 2024-05-15 15:53:02 | [diff] [blame] | 414 | pipe_config.link_capacity = DataRate::KilobitsPerSec(1000); |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 415 | MakeNetwork(pipe_config); |
| 416 | |
| 417 | SctpActor sender("A", emulated_socket_a_, options_); |
| 418 | SctpActor receiver("Z", emulated_socket_z_, options_); |
| 419 | sender.sctp_socket().Connect(); |
| 420 | |
| 421 | sender.SetActorMode(ActorMode::kThroughputSender); |
| 422 | receiver.SetActorMode(ActorMode::kThroughputReceiver); |
| 423 | |
| 424 | Sleep(kBenchmarkRuntime); |
| 425 | sender.SetActorMode(ActorMode::kAtRest); |
| 426 | receiver.SetActorMode(ActorMode::kAtRest); |
| 427 | |
| 428 | Sleep(kAWhile); |
| 429 | |
| 430 | sender.sctp_socket().Shutdown(); |
| 431 | |
| 432 | Sleep(kAWhile); |
| 433 | |
| 434 | // Verify that the bitrates are in the range of 0.5-1.0 Mbps. |
| 435 | double bitrate = receiver.avg_received_bitrate_mbps(); |
| 436 | EXPECT_THAT(bitrate, AllOf(Ge(0.5), Le(1.0))); |
| 437 | } |
| 438 | |
| 439 | TEST_F(DcSctpSocketNetworkTest, |
| 440 | DCSCTP_NDEBUG_TEST(CanSendMessagesReliablyWithMediumBandwidth)) { |
| 441 | webrtc::BuiltInNetworkBehaviorConfig pipe_config; |
| 442 | pipe_config.queue_delay_ms = 30; |
Per K | 5566b91 | 2024-05-15 15:53:02 | [diff] [blame] | 443 | pipe_config.link_capacity = DataRate::KilobitsPerSec(18000); |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 444 | MakeNetwork(pipe_config); |
| 445 | |
| 446 | SctpActor sender("A", emulated_socket_a_, options_); |
| 447 | SctpActor receiver("Z", emulated_socket_z_, options_); |
| 448 | sender.sctp_socket().Connect(); |
| 449 | |
| 450 | sender.SetActorMode(ActorMode::kThroughputSender); |
| 451 | receiver.SetActorMode(ActorMode::kThroughputReceiver); |
| 452 | |
| 453 | Sleep(kBenchmarkRuntime); |
| 454 | sender.SetActorMode(ActorMode::kAtRest); |
| 455 | receiver.SetActorMode(ActorMode::kAtRest); |
| 456 | |
| 457 | Sleep(kAWhile); |
| 458 | |
| 459 | sender.sctp_socket().Shutdown(); |
| 460 | |
| 461 | Sleep(kAWhile); |
| 462 | |
| 463 | // Verify that the bitrates are in the range of 16-18 Mbps. |
| 464 | double bitrate = receiver.avg_received_bitrate_mbps(); |
| 465 | EXPECT_THAT(bitrate, AllOf(Ge(16), Le(18))); |
| 466 | } |
| 467 | |
| 468 | TEST_F(DcSctpSocketNetworkTest, CanSendMessagesReliablyWithMuchPacketLoss) { |
| 469 | webrtc::BuiltInNetworkBehaviorConfig config; |
| 470 | config.queue_delay_ms = 30; |
| 471 | config.loss_percent = 1; |
| 472 | MakeNetwork(config); |
| 473 | |
| 474 | SctpActor sender("A", emulated_socket_a_, options_); |
| 475 | SctpActor receiver("Z", emulated_socket_z_, options_); |
| 476 | sender.sctp_socket().Connect(); |
| 477 | |
| 478 | sender.SetActorMode(ActorMode::kThroughputSender); |
| 479 | receiver.SetActorMode(ActorMode::kThroughputReceiver); |
| 480 | |
| 481 | Sleep(kBenchmarkRuntime); |
| 482 | sender.SetActorMode(ActorMode::kAtRest); |
| 483 | receiver.SetActorMode(ActorMode::kAtRest); |
| 484 | |
| 485 | Sleep(kAWhile); |
| 486 | |
| 487 | sender.sctp_socket().Shutdown(); |
| 488 | |
| 489 | Sleep(kAWhile); |
| 490 | |
| 491 | // TCP calculator gives: 1200 MTU, 60ms RTT and 1% packet loss -> 1.6Mbps. |
| 492 | // This test is doing slightly better (doesn't have any additional header |
| 493 | // overhead etc). Verify that the bitrates are in the range of 1.5-2.5 Mbps. |
| 494 | double bitrate = receiver.avg_received_bitrate_mbps(); |
| 495 | EXPECT_THAT(bitrate, AllOf(Ge(1.5), Le(2.5))); |
| 496 | } |
| 497 | |
| 498 | TEST_F(DcSctpSocketNetworkTest, DCSCTP_NDEBUG_TEST(HasHighBandwidth)) { |
| 499 | webrtc::BuiltInNetworkBehaviorConfig pipe_config; |
| 500 | pipe_config.queue_delay_ms = 30; |
| 501 | MakeNetwork(pipe_config); |
| 502 | |
| 503 | SctpActor sender("A", emulated_socket_a_, options_); |
| 504 | SctpActor receiver("Z", emulated_socket_z_, options_); |
| 505 | sender.sctp_socket().Connect(); |
| 506 | |
| 507 | sender.SetActorMode(ActorMode::kThroughputSender); |
| 508 | receiver.SetActorMode(ActorMode::kThroughputReceiver); |
| 509 | |
| 510 | Sleep(kBenchmarkRuntime); |
| 511 | |
| 512 | sender.SetActorMode(ActorMode::kAtRest); |
| 513 | receiver.SetActorMode(ActorMode::kAtRest); |
| 514 | Sleep(kAWhile); |
| 515 | |
| 516 | sender.sctp_socket().Shutdown(); |
| 517 | Sleep(kAWhile); |
| 518 | |
| 519 | // Verify that the bitrate is in the range of 540-640 Mbps |
| 520 | double bitrate = receiver.avg_received_bitrate_mbps(); |
Mirko Bonadei | 3b205da | 2022-08-09 08:24:17 | [diff] [blame] | 521 | EXPECT_THAT(bitrate, AllOf(Ge(520), Le(640))); |
Victor Boivie | 82ccdd3 | 2021-05-30 19:25:03 | [diff] [blame] | 522 | } |
| 523 | } // namespace |
| 524 | } // namespace dcsctp |