| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2022 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 | |
| 11 | #include "pc/data_channel_controller.h" |
| 12 | |
| Harald Alvestrand | f0d5caf | 2025-04-07 09:08:51 | [diff] [blame] | 13 | #include <cstddef> |
| Philip Eliasson | 17ddd31 | 2025-04-08 08:33:49 | [diff] [blame] | 14 | #include <cstdint> |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 15 | #include <memory> |
| Harald Alvestrand | f0d5caf | 2025-04-07 09:08:51 | [diff] [blame] | 16 | #include <optional> |
| Philip Eliasson | 17ddd31 | 2025-04-08 08:33:49 | [diff] [blame] | 17 | #include <utility> |
| 18 | #include <vector> |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 19 | |
| Philip Eliasson | 17ddd31 | 2025-04-08 08:33:49 | [diff] [blame] | 20 | #include "api/data_channel_event_observer_interface.h" |
| Harald Alvestrand | f0d5caf | 2025-04-07 09:08:51 | [diff] [blame] | 21 | #include "api/data_channel_interface.h" |
| 22 | #include "api/make_ref_counted.h" |
| Florent Castelli | 0012bfa | 2024-07-26 16:16:41 | [diff] [blame] | 23 | #include "api/priority.h" |
| Harald Alvestrand | f0d5caf | 2025-04-07 09:08:51 | [diff] [blame] | 24 | #include "api/rtc_error.h" |
| 25 | #include "api/scoped_refptr.h" |
| 26 | #include "api/transport/data_channel_transport_interface.h" |
| Philip Eliasson | 17ddd31 | 2025-04-08 08:33:49 | [diff] [blame] | 27 | #include "api/units/timestamp.h" |
| Harald Alvestrand | f0d5caf | 2025-04-07 09:08:51 | [diff] [blame] | 28 | #include "media/sctp/sctp_transport_internal.h" |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 29 | #include "pc/peer_connection_internal.h" |
| Harald Alvestrand | 9e5aeb9 | 2022-05-11 09:35:36 | [diff] [blame] | 30 | #include "pc/sctp_data_channel.h" |
| Philip Eliasson | 17ddd31 | 2025-04-08 08:33:49 | [diff] [blame] | 31 | #include "pc/sctp_utils.h" |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 32 | #include "pc/test/mock_peer_connection_internal.h" |
| Harald Alvestrand | f0d5caf | 2025-04-07 09:08:51 | [diff] [blame] | 33 | #include "rtc_base/copy_on_write_buffer.h" |
| Philip Eliasson | 17ddd31 | 2025-04-08 08:33:49 | [diff] [blame] | 34 | #include "rtc_base/fake_clock.h" |
| Tommi | 55f7280 | 2023-03-27 10:39:33 | [diff] [blame] | 35 | #include "rtc_base/null_socket_server.h" |
| Harald Alvestrand | f0d5caf | 2025-04-07 09:08:51 | [diff] [blame] | 36 | #include "rtc_base/ssl_stream_adapter.h" |
| 37 | #include "rtc_base/thread.h" |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 38 | #include "test/gmock.h" |
| 39 | #include "test/gtest.h" |
| Tommi | 2a44872 | 2023-03-09 22:52:43 | [diff] [blame] | 40 | #include "test/run_loop.h" |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 41 | |
| 42 | namespace webrtc { |
| 43 | |
| 44 | namespace { |
| 45 | |
| Philip Eliasson | 17ddd31 | 2025-04-08 08:33:49 | [diff] [blame] | 46 | using Message = DataChannelEventObserverInterface::Message; |
| 47 | using ::testing::ElementsAreArray; |
| 48 | using ::testing::IsEmpty; |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 49 | using ::testing::NiceMock; |
| 50 | using ::testing::Return; |
| Philip Eliasson | 17ddd31 | 2025-04-08 08:33:49 | [diff] [blame] | 51 | using ::testing::SizeIs; |
| 52 | |
| 53 | constexpr uint8_t kSomeData[] = {5, 4, 3, 2, 1}; |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 54 | |
| Harald Alvestrand | a654437 | 2023-11-13 09:33:56 | [diff] [blame] | 55 | class MockDataChannelTransport : public DataChannelTransportInterface { |
| Tommi | 492296c | 2023-03-12 15:59:25 | [diff] [blame] | 56 | public: |
| 57 | ~MockDataChannelTransport() override {} |
| 58 | |
| Florent Castelli | 0012bfa | 2024-07-26 16:16:41 | [diff] [blame] | 59 | MOCK_METHOD(RTCError, |
| 60 | OpenChannel, |
| 61 | (int channel_id, PriorityValue priority), |
| 62 | (override)); |
| Tommi | 492296c | 2023-03-12 15:59:25 | [diff] [blame] | 63 | MOCK_METHOD(RTCError, |
| 64 | SendData, |
| 65 | (int channel_id, |
| 66 | const SendDataParams& params, |
| Evan Shrubsole | 94adaee | 2025-05-09 10:35:15 | [diff] [blame] | 67 | const CopyOnWriteBuffer& buffer), |
| Tommi | 492296c | 2023-03-12 15:59:25 | [diff] [blame] | 68 | (override)); |
| 69 | MOCK_METHOD(RTCError, CloseChannel, (int channel_id), (override)); |
| 70 | MOCK_METHOD(void, SetDataSink, (DataChannelSink * sink), (override)); |
| 71 | MOCK_METHOD(bool, IsReadyToSend, (), (const, override)); |
| Victor Boivie | fea41f5 | 2024-03-11 15:43:31 | [diff] [blame] | 72 | MOCK_METHOD(size_t, buffered_amount, (int channel_id), (const, override)); |
| Victor Boivie | cdecc4e | 2024-03-18 12:47:34 | [diff] [blame] | 73 | MOCK_METHOD(size_t, |
| 74 | buffered_amount_low_threshold, |
| 75 | (int channel_id), |
| 76 | (const, override)); |
| 77 | MOCK_METHOD(void, |
| 78 | SetBufferedAmountLowThreshold, |
| 79 | (int channel_id, size_t bytes), |
| 80 | (override)); |
| Tommi | 492296c | 2023-03-12 15:59:25 | [diff] [blame] | 81 | }; |
| 82 | |
| Philip Eliasson | 17ddd31 | 2025-04-08 08:33:49 | [diff] [blame] | 83 | class MockDataChannelEventObserver : public DataChannelEventObserverInterface { |
| 84 | public: |
| 85 | MOCK_METHOD(void, |
| 86 | OnMessage, |
| 87 | (const DataChannelEventObserverInterface::Message& message), |
| 88 | (override)); |
| 89 | }; |
| 90 | |
| Tommi | 5271965 | 2023-04-04 09:59:55 | [diff] [blame] | 91 | // Convenience class for tests to ensure that shutdown methods for DCC |
| 92 | // are consistently called. In practice SdpOfferAnswerHandler will call |
| 93 | // TeardownDataChannelTransport_n on the network thread when destroying the |
| 94 | // data channel transport and PeerConnection calls PrepareForShutdown() from |
| 95 | // within PeerConnection::Close(). The DataChannelControllerForTest class mimics |
| 96 | // behavior by calling those methods from within its destructor. |
| 97 | class DataChannelControllerForTest : public DataChannelController { |
| 98 | public: |
| Tommi | aa3c9f2 | 2023-04-18 10:19:19 | [diff] [blame] | 99 | explicit DataChannelControllerForTest( |
| 100 | PeerConnectionInternal* pc, |
| 101 | DataChannelTransportInterface* transport = nullptr) |
| 102 | : DataChannelController(pc) { |
| 103 | if (transport) { |
| 104 | network_thread()->BlockingCall( |
| 105 | [&] { SetupDataChannelTransport_n(transport); }); |
| 106 | } |
| 107 | } |
| Tommi | 5271965 | 2023-04-04 09:59:55 | [diff] [blame] | 108 | |
| 109 | ~DataChannelControllerForTest() override { |
| Tommi | b00d63c | 2023-04-12 17:49:53 | [diff] [blame] | 110 | network_thread()->BlockingCall( |
| 111 | [&] { TeardownDataChannelTransport_n(RTCError::OK()); }); |
| Tommi | 5271965 | 2023-04-04 09:59:55 | [diff] [blame] | 112 | PrepareForShutdown(); |
| 113 | } |
| 114 | }; |
| 115 | |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 116 | class DataChannelControllerTest : public ::testing::Test { |
| 117 | protected: |
| Tommi | 55f7280 | 2023-03-27 10:39:33 | [diff] [blame] | 118 | DataChannelControllerTest() |
| Evan Shrubsole | 03b6880 | 2025-03-18 12:23:05 | [diff] [blame] | 119 | : network_thread_(std::make_unique<NullSocketServer>()) { |
| Tommi | 55f7280 | 2023-03-27 10:39:33 | [diff] [blame] | 120 | network_thread_.Start(); |
| Evan Shrubsole | e6a1f70 | 2025-04-15 14:55:42 | [diff] [blame] | 121 | pc_ = make_ref_counted<NiceMock<MockPeerConnectionInternal>>(); |
| Evan Shrubsole | 6a9a1ae | 2025-03-21 12:54:15 | [diff] [blame] | 122 | ON_CALL(*pc_, signaling_thread).WillByDefault(Return(Thread::Current())); |
| Tommi | 55f7280 | 2023-03-27 10:39:33 | [diff] [blame] | 123 | ON_CALL(*pc_, network_thread).WillByDefault(Return(&network_thread_)); |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 124 | } |
| 125 | |
| Tommi | 55f7280 | 2023-03-27 10:39:33 | [diff] [blame] | 126 | ~DataChannelControllerTest() override { |
| 127 | run_loop_.Flush(); |
| 128 | network_thread_.Stop(); |
| 129 | } |
| Tommi | 2a44872 | 2023-03-09 22:52:43 | [diff] [blame] | 130 | |
| Philip Eliasson | 17ddd31 | 2025-04-08 08:33:49 | [diff] [blame] | 131 | ScopedBaseFakeClock clock_; |
| Tommi | 2a44872 | 2023-03-09 22:52:43 | [diff] [blame] | 132 | test::RunLoop run_loop_; |
| Evan Shrubsole | 6a9a1ae | 2025-03-21 12:54:15 | [diff] [blame] | 133 | Thread network_thread_; |
| Evan Shrubsole | e6a1f70 | 2025-04-15 14:55:42 | [diff] [blame] | 134 | scoped_refptr<NiceMock<MockPeerConnectionInternal>> pc_; |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 135 | }; |
| 136 | |
| 137 | TEST_F(DataChannelControllerTest, CreateAndDestroy) { |
| Tommi | 5271965 | 2023-04-04 09:59:55 | [diff] [blame] | 138 | DataChannelControllerForTest dcc(pc_.get()); |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 139 | } |
| 140 | |
| 141 | TEST_F(DataChannelControllerTest, CreateDataChannelEarlyRelease) { |
| Tommi | 5271965 | 2023-04-04 09:59:55 | [diff] [blame] | 142 | DataChannelControllerForTest dcc(pc_.get()); |
| Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 143 | auto ret = dcc.InternalCreateDataChannelWithProxy( |
| Tommi | 335d084 | 2023-03-25 09:56:18 | [diff] [blame] | 144 | "label", InternalDataChannelInit(DataChannelInit())); |
| Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 145 | ASSERT_TRUE(ret.ok()); |
| 146 | auto channel = ret.MoveValue(); |
| 147 | // DCC still holds a reference to the channel. Release this reference early. |
| 148 | channel = nullptr; |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 149 | } |
| 150 | |
| Harald Alvestrand | 5da3eb0 | 2023-03-15 20:39:42 | [diff] [blame] | 151 | TEST_F(DataChannelControllerTest, CreateDataChannelEarlyClose) { |
| Tommi | 5271965 | 2023-04-04 09:59:55 | [diff] [blame] | 152 | DataChannelControllerForTest dcc(pc_.get()); |
| Philipp Hancke | 522380f | 2023-05-09 07:41:03 | [diff] [blame] | 153 | EXPECT_FALSE(dcc.HasDataChannels()); |
| Harald Alvestrand | 5da3eb0 | 2023-03-15 20:39:42 | [diff] [blame] | 154 | EXPECT_FALSE(dcc.HasUsedDataChannels()); |
| Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 155 | auto ret = dcc.InternalCreateDataChannelWithProxy( |
| Tommi | 335d084 | 2023-03-25 09:56:18 | [diff] [blame] | 156 | "label", InternalDataChannelInit(DataChannelInit())); |
| Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 157 | ASSERT_TRUE(ret.ok()); |
| 158 | auto channel = ret.MoveValue(); |
| Philipp Hancke | 522380f | 2023-05-09 07:41:03 | [diff] [blame] | 159 | EXPECT_TRUE(dcc.HasDataChannels()); |
| Harald Alvestrand | 5da3eb0 | 2023-03-15 20:39:42 | [diff] [blame] | 160 | EXPECT_TRUE(dcc.HasUsedDataChannels()); |
| 161 | channel->Close(); |
| Tommi | 5271965 | 2023-04-04 09:59:55 | [diff] [blame] | 162 | run_loop_.Flush(); |
| Philipp Hancke | 522380f | 2023-05-09 07:41:03 | [diff] [blame] | 163 | EXPECT_FALSE(dcc.HasDataChannels()); |
| Harald Alvestrand | 5da3eb0 | 2023-03-15 20:39:42 | [diff] [blame] | 164 | EXPECT_TRUE(dcc.HasUsedDataChannels()); |
| 165 | } |
| 166 | |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 167 | TEST_F(DataChannelControllerTest, CreateDataChannelLateRelease) { |
| Tommi | 5271965 | 2023-04-04 09:59:55 | [diff] [blame] | 168 | auto dcc = std::make_unique<DataChannelControllerForTest>(pc_.get()); |
| Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 169 | auto ret = dcc->InternalCreateDataChannelWithProxy( |
| Tommi | 335d084 | 2023-03-25 09:56:18 | [diff] [blame] | 170 | "label", InternalDataChannelInit(DataChannelInit())); |
| Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 171 | ASSERT_TRUE(ret.ok()); |
| 172 | auto channel = ret.MoveValue(); |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 173 | dcc.reset(); |
| 174 | channel = nullptr; |
| 175 | } |
| 176 | |
| Harald Alvestrand | 9e5aeb9 | 2022-05-11 09:35:36 | [diff] [blame] | 177 | TEST_F(DataChannelControllerTest, CloseAfterControllerDestroyed) { |
| Tommi | 5271965 | 2023-04-04 09:59:55 | [diff] [blame] | 178 | auto dcc = std::make_unique<DataChannelControllerForTest>(pc_.get()); |
| Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 179 | auto ret = dcc->InternalCreateDataChannelWithProxy( |
| Tommi | 335d084 | 2023-03-25 09:56:18 | [diff] [blame] | 180 | "label", InternalDataChannelInit(DataChannelInit())); |
| Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 181 | ASSERT_TRUE(ret.ok()); |
| 182 | auto channel = ret.MoveValue(); |
| Harald Alvestrand | 9e5aeb9 | 2022-05-11 09:35:36 | [diff] [blame] | 183 | dcc.reset(); |
| 184 | channel->Close(); |
| 185 | } |
| 186 | |
| Tommi | 492296c | 2023-03-12 15:59:25 | [diff] [blame] | 187 | // Allocate the maximum number of data channels and then one more. |
| 188 | // The last allocation should fail. |
| 189 | TEST_F(DataChannelControllerTest, MaxChannels) { |
| 190 | NiceMock<MockDataChannelTransport> transport; |
| 191 | int channel_id = 0; |
| 192 | |
| Tommi | 335d084 | 2023-03-25 09:56:18 | [diff] [blame] | 193 | ON_CALL(*pc_, GetSctpSslRole_n).WillByDefault([&]() { |
| Evan Shrubsole | eb835d0 | 2025-03-12 09:41:06 | [diff] [blame] | 194 | return std::optional<SSLRole>((channel_id & 1) ? SSL_SERVER : SSL_CLIENT); |
| Tommi | 492296c | 2023-03-12 15:59:25 | [diff] [blame] | 195 | }); |
| 196 | |
| Tommi | aa3c9f2 | 2023-04-18 10:19:19 | [diff] [blame] | 197 | DataChannelControllerForTest dcc(pc_.get(), &transport); |
| Tommi | 492296c | 2023-03-12 15:59:25 | [diff] [blame] | 198 | |
| 199 | // Allocate the maximum number of channels + 1. Inside the loop, the creation |
| 200 | // process will allocate a stream id for each channel. |
| Evan Shrubsole | 945e517 | 2025-04-08 14:11:45 | [diff] [blame] | 201 | for (channel_id = 0; channel_id <= kMaxSctpStreams; ++channel_id) { |
| Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 202 | auto ret = dcc.InternalCreateDataChannelWithProxy( |
| 203 | "label", InternalDataChannelInit(DataChannelInit())); |
| Evan Shrubsole | 945e517 | 2025-04-08 14:11:45 | [diff] [blame] | 204 | if (channel_id == kMaxSctpStreams) { |
| Tommi | 492296c | 2023-03-12 15:59:25 | [diff] [blame] | 205 | // We've reached the maximum and the previous call should have failed. |
| Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 206 | EXPECT_FALSE(ret.ok()); |
| Tommi | 492296c | 2023-03-12 15:59:25 | [diff] [blame] | 207 | } else { |
| 208 | // We're still working on saturating the pool. Things should be working. |
| Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 209 | EXPECT_TRUE(ret.ok()); |
| Tommi | 492296c | 2023-03-12 15:59:25 | [diff] [blame] | 210 | } |
| 211 | } |
| 212 | } |
| 213 | |
| Victor Boivie | fea41f5 | 2024-03-11 15:43:31 | [diff] [blame] | 214 | TEST_F(DataChannelControllerTest, BufferedAmountIncludesFromTransport) { |
| 215 | NiceMock<MockDataChannelTransport> transport; |
| 216 | EXPECT_CALL(transport, buffered_amount(0)).WillOnce(Return(4711)); |
| Evan Shrubsole | eb835d0 | 2025-03-12 09:41:06 | [diff] [blame] | 217 | ON_CALL(*pc_, GetSctpSslRole_n).WillByDefault([&]() { return SSL_CLIENT; }); |
| Victor Boivie | fea41f5 | 2024-03-11 15:43:31 | [diff] [blame] | 218 | |
| 219 | DataChannelControllerForTest dcc(pc_.get(), &transport); |
| 220 | auto dc = dcc.InternalCreateDataChannelWithProxy( |
| 221 | "label", InternalDataChannelInit(DataChannelInit())) |
| 222 | .MoveValue(); |
| 223 | EXPECT_EQ(dc->buffered_amount(), 4711u); |
| 224 | } |
| 225 | |
| Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 226 | // Test that while a data channel is in the `kClosing` state, its StreamId does |
| 227 | // not get re-used for new channels. Only once the state reaches `kClosed` |
| 228 | // should a StreamId be available again for allocation. |
| 229 | TEST_F(DataChannelControllerTest, NoStreamIdReuseWhileClosing) { |
| Evan Shrubsole | eb835d0 | 2025-03-12 09:41:06 | [diff] [blame] | 230 | ON_CALL(*pc_, GetSctpSslRole_n).WillByDefault([&]() { return SSL_CLIENT; }); |
| Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 231 | |
| Tommi | 5271965 | 2023-04-04 09:59:55 | [diff] [blame] | 232 | NiceMock<MockDataChannelTransport> transport; // Wider scope than `dcc`. |
| Tommi | aa3c9f2 | 2023-04-18 10:19:19 | [diff] [blame] | 233 | DataChannelControllerForTest dcc(pc_.get(), &transport); |
| Tommi | 4f7ade5 | 2023-03-29 18:46:59 | [diff] [blame] | 234 | |
| 235 | // Create the first channel and check that we got the expected, first sid. |
| 236 | auto channel1 = dcc.InternalCreateDataChannelWithProxy( |
| 237 | "label", InternalDataChannelInit(DataChannelInit())) |
| 238 | .MoveValue(); |
| 239 | ASSERT_EQ(channel1->id(), 0); |
| 240 | |
| 241 | // Start closing the channel and make sure its state is `kClosing` |
| 242 | channel1->Close(); |
| 243 | ASSERT_EQ(channel1->state(), DataChannelInterface::DataState::kClosing); |
| 244 | |
| 245 | // Create a second channel and make sure we get a new StreamId, not the same |
| 246 | // as that of channel1. |
| 247 | auto channel2 = dcc.InternalCreateDataChannelWithProxy( |
| 248 | "label2", InternalDataChannelInit(DataChannelInit())) |
| 249 | .MoveValue(); |
| 250 | ASSERT_NE(channel2->id(), channel1->id()); // In practice the id will be 2. |
| 251 | |
| 252 | // Simulate the acknowledgement of the channel closing from the transport. |
| 253 | // This completes the closing operation of channel1. |
| 254 | pc_->network_thread()->BlockingCall([&] { dcc.OnChannelClosed(0); }); |
| 255 | run_loop_.Flush(); |
| 256 | ASSERT_EQ(channel1->state(), DataChannelInterface::DataState::kClosed); |
| 257 | |
| 258 | // Now create a third channel. This time, the id of the first channel should |
| 259 | // be available again and therefore the ids of the first and third channels |
| 260 | // should be the same. |
| 261 | auto channel3 = dcc.InternalCreateDataChannelWithProxy( |
| 262 | "label3", InternalDataChannelInit(DataChannelInit())) |
| 263 | .MoveValue(); |
| 264 | EXPECT_EQ(channel3->id(), channel1->id()); |
| 265 | } |
| 266 | |
| Philip Eliasson | 17ddd31 | 2025-04-08 08:33:49 | [diff] [blame] | 267 | TEST_F(DataChannelControllerTest, ObserverNotifiedOnStringMessageSent) { |
| 268 | NiceMock<MockDataChannelTransport> transport; |
| 269 | DataChannelControllerForTest dcc(pc_.get(), &transport); |
| 270 | |
| 271 | std::vector<Message> messages; |
| 272 | auto observer = std::make_unique<NiceMock<MockDataChannelEventObserver>>(); |
| 273 | ON_CALL(*observer, OnMessage).WillByDefault([&](const Message& m) { |
| 274 | messages.push_back(m); |
| 275 | }); |
| 276 | network_thread_.BlockingCall( |
| 277 | [&]() { dcc.SetEventObserver(std::move(observer)); }); |
| 278 | |
| 279 | RTCErrorOr<scoped_refptr<DataChannelInterface>> ret = |
| 280 | dcc.InternalCreateDataChannelWithProxy( |
| 281 | "TestingSomeSendStuff", |
| 282 | InternalDataChannelInit({.negotiated = true, .id = 5})); |
| 283 | ASSERT_TRUE(ret.ok()); |
| 284 | auto channel = ret.MoveValue(); |
| 285 | |
| 286 | clock_.SetTime(Timestamp::Millis(123)); |
| 287 | network_thread_.BlockingCall([&]() { |
| 288 | dcc.SendData(StreamId(5), {.type = DataMessageType::kText}, |
| 289 | CopyOnWriteBuffer(kSomeData)); |
| 290 | }); |
| 291 | |
| 292 | channel->Close(); |
| 293 | run_loop_.Flush(); |
| 294 | |
| 295 | ASSERT_THAT(messages, SizeIs(1)); |
| 296 | EXPECT_EQ(messages[0].unix_timestamp_ms(), 123); |
| 297 | EXPECT_EQ(messages[0].datachannel_id(), 5); |
| 298 | EXPECT_EQ(messages[0].label(), "TestingSomeSendStuff"); |
| 299 | EXPECT_EQ(messages[0].direction(), Message::Direction::kSend); |
| 300 | EXPECT_EQ(messages[0].data_type(), Message::DataType::kString); |
| 301 | EXPECT_THAT(messages[0].data(), ElementsAreArray(kSomeData)); |
| 302 | } |
| 303 | |
| 304 | TEST_F(DataChannelControllerTest, ObserverNotifiedOnBinaryMessageSent) { |
| 305 | NiceMock<MockDataChannelTransport> transport; |
| 306 | DataChannelControllerForTest dcc(pc_.get(), &transport); |
| 307 | |
| 308 | std::vector<Message> messages; |
| 309 | auto observer = std::make_unique<NiceMock<MockDataChannelEventObserver>>(); |
| 310 | ON_CALL(*observer, OnMessage).WillByDefault([&](const Message& m) { |
| 311 | messages.push_back(m); |
| 312 | }); |
| 313 | network_thread_.BlockingCall( |
| 314 | [&]() { dcc.SetEventObserver(std::move(observer)); }); |
| 315 | |
| 316 | RTCErrorOr<scoped_refptr<DataChannelInterface>> ret = |
| 317 | dcc.InternalCreateDataChannelWithProxy( |
| 318 | "TestingSomeSendStuff", |
| 319 | InternalDataChannelInit({.negotiated = true, .id = 5})); |
| 320 | ASSERT_TRUE(ret.ok()); |
| 321 | auto channel = ret.MoveValue(); |
| 322 | |
| 323 | clock_.SetTime(Timestamp::Millis(123)); |
| 324 | network_thread_.BlockingCall([&]() { |
| 325 | dcc.SendData(StreamId(5), {.type = DataMessageType::kBinary}, |
| 326 | CopyOnWriteBuffer(kSomeData)); |
| 327 | }); |
| 328 | |
| 329 | channel->Close(); |
| 330 | run_loop_.Flush(); |
| 331 | |
| 332 | ASSERT_THAT(messages, SizeIs(1)); |
| 333 | EXPECT_EQ(messages[0].unix_timestamp_ms(), 123); |
| 334 | EXPECT_EQ(messages[0].datachannel_id(), 5); |
| 335 | EXPECT_EQ(messages[0].label(), "TestingSomeSendStuff"); |
| 336 | EXPECT_EQ(messages[0].direction(), Message::Direction::kSend); |
| 337 | EXPECT_EQ(messages[0].data_type(), Message::DataType::kBinary); |
| 338 | EXPECT_THAT(messages[0].data(), ElementsAreArray(kSomeData)); |
| 339 | } |
| 340 | |
| 341 | TEST_F(DataChannelControllerTest, ObserverNotNotifiedOnControlMessageSent) { |
| 342 | NiceMock<MockDataChannelTransport> transport; |
| 343 | DataChannelControllerForTest dcc(pc_.get(), &transport); |
| 344 | |
| 345 | std::vector<Message> messages; |
| 346 | auto observer = std::make_unique<NiceMock<MockDataChannelEventObserver>>(); |
| 347 | ON_CALL(*observer, OnMessage).WillByDefault([&](const Message& m) { |
| 348 | messages.push_back(m); |
| 349 | }); |
| 350 | network_thread_.BlockingCall( |
| 351 | [&]() { dcc.SetEventObserver(std::move(observer)); }); |
| 352 | |
| 353 | RTCErrorOr<scoped_refptr<DataChannelInterface>> ret = |
| 354 | dcc.InternalCreateDataChannelWithProxy( |
| 355 | "TestingSomeSendStuff", |
| 356 | InternalDataChannelInit({.negotiated = true, .id = 5})); |
| 357 | ASSERT_TRUE(ret.ok()); |
| 358 | auto channel = ret.MoveValue(); |
| 359 | |
| 360 | network_thread_.BlockingCall([&]() { |
| 361 | dcc.SendData(StreamId(5), {.type = DataMessageType::kControl}, |
| 362 | CopyOnWriteBuffer(kSomeData)); |
| 363 | }); |
| 364 | |
| 365 | channel->Close(); |
| 366 | run_loop_.Flush(); |
| 367 | |
| 368 | ASSERT_TRUE(messages.empty()); |
| 369 | } |
| 370 | |
| 371 | TEST_F(DataChannelControllerTest, ObserverNotNotifiedOnTransportFailed) { |
| 372 | NiceMock<MockDataChannelTransport> transport; |
| 373 | ON_CALL(transport, SendData) |
| 374 | .WillByDefault(Return(RTCError(RTCErrorType::INVALID_STATE))); |
| 375 | DataChannelControllerForTest dcc(pc_.get(), &transport); |
| 376 | |
| 377 | std::vector<Message> messages; |
| 378 | auto observer = std::make_unique<NiceMock<MockDataChannelEventObserver>>(); |
| 379 | ON_CALL(*observer, OnMessage).WillByDefault([&](const Message& m) { |
| 380 | messages.push_back(m); |
| 381 | }); |
| 382 | network_thread_.BlockingCall( |
| 383 | [&]() { dcc.SetEventObserver(std::move(observer)); }); |
| 384 | |
| 385 | RTCErrorOr<scoped_refptr<DataChannelInterface>> ret = |
| 386 | dcc.InternalCreateDataChannelWithProxy( |
| 387 | "TestingSomeSendStuff", |
| 388 | InternalDataChannelInit({.negotiated = true, .id = 5})); |
| 389 | ASSERT_TRUE(ret.ok()); |
| 390 | auto channel = ret.MoveValue(); |
| 391 | |
| 392 | network_thread_.BlockingCall([&]() { |
| 393 | dcc.SendData(StreamId(5), {.type = DataMessageType::kText}, |
| 394 | CopyOnWriteBuffer(kSomeData)); |
| 395 | }); |
| 396 | |
| 397 | channel->Close(); |
| 398 | run_loop_.Flush(); |
| 399 | |
| 400 | ASSERT_TRUE(messages.empty()); |
| 401 | } |
| 402 | |
| 403 | TEST_F(DataChannelControllerTest, ObserverNotifiedOnStringMessageReceived) { |
| 404 | NiceMock<MockDataChannelTransport> transport; |
| 405 | DataChannelControllerForTest dcc(pc_.get(), &transport); |
| 406 | |
| 407 | std::vector<Message> messages; |
| 408 | auto observer = std::make_unique<NiceMock<MockDataChannelEventObserver>>(); |
| 409 | ON_CALL(*observer, OnMessage).WillByDefault([&](const Message& m) { |
| 410 | messages.push_back(m); |
| 411 | }); |
| 412 | network_thread_.BlockingCall( |
| 413 | [&]() { dcc.SetEventObserver(std::move(observer)); }); |
| 414 | |
| 415 | RTCErrorOr<scoped_refptr<DataChannelInterface>> ret = |
| 416 | dcc.InternalCreateDataChannelWithProxy( |
| 417 | "TestingSomeReceiveStuff", |
| 418 | InternalDataChannelInit({.negotiated = true, .id = 5})); |
| 419 | ASSERT_TRUE(ret.ok()); |
| 420 | auto channel = ret.MoveValue(); |
| 421 | |
| 422 | clock_.SetTime(Timestamp::Millis(123)); |
| 423 | network_thread_.BlockingCall([&]() { |
| 424 | dcc.OnDataReceived(5, DataMessageType::kText, CopyOnWriteBuffer(kSomeData)); |
| 425 | }); |
| 426 | |
| 427 | channel->Close(); |
| 428 | run_loop_.Flush(); |
| 429 | |
| 430 | ASSERT_THAT(messages, SizeIs(1)); |
| 431 | EXPECT_EQ(messages[0].unix_timestamp_ms(), 123); |
| 432 | EXPECT_EQ(messages[0].datachannel_id(), 5); |
| 433 | EXPECT_EQ(messages[0].label(), "TestingSomeReceiveStuff"); |
| 434 | EXPECT_EQ(messages[0].direction(), Message::Direction::kReceive); |
| 435 | EXPECT_EQ(messages[0].data_type(), Message::DataType::kString); |
| 436 | EXPECT_THAT(messages[0].data(), ElementsAreArray(kSomeData)); |
| 437 | } |
| 438 | |
| 439 | TEST_F(DataChannelControllerTest, ObserverNotifiedOnBinaryMessageReceived) { |
| 440 | NiceMock<MockDataChannelTransport> transport; |
| 441 | DataChannelControllerForTest dcc(pc_.get(), &transport); |
| 442 | |
| 443 | std::vector<Message> messages; |
| 444 | auto observer = std::make_unique<NiceMock<MockDataChannelEventObserver>>(); |
| 445 | ON_CALL(*observer, OnMessage).WillByDefault([&](const Message& m) { |
| 446 | messages.push_back(m); |
| 447 | }); |
| 448 | network_thread_.BlockingCall( |
| 449 | [&]() { dcc.SetEventObserver(std::move(observer)); }); |
| 450 | |
| 451 | RTCErrorOr<scoped_refptr<DataChannelInterface>> ret = |
| 452 | dcc.InternalCreateDataChannelWithProxy( |
| 453 | "TestingSomeReceiveStuff", |
| 454 | InternalDataChannelInit({.negotiated = true, .id = 5})); |
| 455 | ASSERT_TRUE(ret.ok()); |
| 456 | auto channel = ret.MoveValue(); |
| 457 | |
| 458 | clock_.SetTime(Timestamp::Millis(123)); |
| 459 | network_thread_.BlockingCall([&]() { |
| 460 | dcc.OnDataReceived(5, DataMessageType::kBinary, |
| 461 | CopyOnWriteBuffer(kSomeData)); |
| 462 | }); |
| 463 | |
| 464 | channel->Close(); |
| 465 | run_loop_.Flush(); |
| 466 | |
| 467 | ASSERT_THAT(messages, SizeIs(1)); |
| 468 | EXPECT_EQ(messages[0].unix_timestamp_ms(), 123); |
| 469 | EXPECT_EQ(messages[0].datachannel_id(), 5); |
| 470 | EXPECT_EQ(messages[0].label(), "TestingSomeReceiveStuff"); |
| 471 | EXPECT_EQ(messages[0].direction(), Message::Direction::kReceive); |
| 472 | EXPECT_EQ(messages[0].data_type(), Message::DataType::kBinary); |
| 473 | EXPECT_THAT(messages[0].data(), ElementsAreArray(kSomeData)); |
| 474 | } |
| 475 | |
| 476 | TEST_F(DataChannelControllerTest, ObserverNotNotifiedOnControlMessageReceived) { |
| 477 | NiceMock<MockDataChannelTransport> transport; |
| 478 | DataChannelControllerForTest dcc(pc_.get(), &transport); |
| 479 | |
| 480 | std::vector<Message> messages; |
| 481 | auto observer = std::make_unique<NiceMock<MockDataChannelEventObserver>>(); |
| 482 | ON_CALL(*observer, OnMessage).WillByDefault([&](const Message& m) { |
| 483 | messages.push_back(m); |
| 484 | }); |
| 485 | network_thread_.BlockingCall( |
| 486 | [&]() { dcc.SetEventObserver(std::move(observer)); }); |
| 487 | |
| 488 | RTCErrorOr<scoped_refptr<DataChannelInterface>> ret = |
| 489 | dcc.InternalCreateDataChannelWithProxy( |
| 490 | "TestingSomeReceiveStuff", |
| 491 | InternalDataChannelInit({.negotiated = true, .id = 5})); |
| 492 | ASSERT_TRUE(ret.ok()); |
| 493 | auto channel = ret.MoveValue(); |
| 494 | |
| 495 | network_thread_.BlockingCall([&]() { |
| 496 | dcc.OnDataReceived(5, DataMessageType::kControl, |
| 497 | CopyOnWriteBuffer(kSomeData)); |
| 498 | }); |
| 499 | |
| 500 | channel->Close(); |
| 501 | run_loop_.Flush(); |
| 502 | |
| 503 | EXPECT_THAT(messages, IsEmpty()); |
| 504 | } |
| 505 | |
| 506 | TEST_F(DataChannelControllerTest, ObserverNotNotifiedOnUnknownId) { |
| 507 | NiceMock<MockDataChannelTransport> transport; |
| 508 | DataChannelControllerForTest dcc(pc_.get(), &transport); |
| 509 | |
| 510 | std::vector<Message> messages; |
| 511 | auto observer = std::make_unique<NiceMock<MockDataChannelEventObserver>>(); |
| 512 | ON_CALL(*observer, OnMessage).WillByDefault([&](const Message& m) { |
| 513 | messages.push_back(m); |
| 514 | }); |
| 515 | network_thread_.BlockingCall( |
| 516 | [&]() { dcc.SetEventObserver(std::move(observer)); }); |
| 517 | |
| 518 | RTCErrorOr<scoped_refptr<DataChannelInterface>> ret = |
| 519 | dcc.InternalCreateDataChannelWithProxy( |
| 520 | "TestingSomeReceiveStuff", |
| 521 | InternalDataChannelInit({.negotiated = true, .id = 5})); |
| 522 | ASSERT_TRUE(ret.ok()); |
| 523 | auto channel = ret.MoveValue(); |
| 524 | |
| 525 | network_thread_.BlockingCall([&]() { |
| 526 | dcc.OnDataReceived(3, DataMessageType::kText, CopyOnWriteBuffer(kSomeData)); |
| 527 | }); |
| 528 | |
| 529 | channel->Close(); |
| 530 | run_loop_.Flush(); |
| 531 | |
| 532 | EXPECT_THAT(messages, IsEmpty()); |
| 533 | } |
| 534 | |
| Harald Alvestrand | a45c8f4 | 2022-05-10 08:44:48 | [diff] [blame] | 535 | } // namespace |
| 536 | } // namespace webrtc |