summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/pc/data_channel_controller_unittest.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/pc/data_channel_controller_unittest.cc
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/pc/data_channel_controller_unittest.cc')
-rw-r--r--third_party/libwebrtc/pc/data_channel_controller_unittest.cc214
1 files changed, 214 insertions, 0 deletions
diff --git a/third_party/libwebrtc/pc/data_channel_controller_unittest.cc b/third_party/libwebrtc/pc/data_channel_controller_unittest.cc
new file mode 100644
index 0000000000..3b8adb6819
--- /dev/null
+++ b/third_party/libwebrtc/pc/data_channel_controller_unittest.cc
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2022 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 "pc/data_channel_controller.h"
+
+#include <memory>
+
+#include "pc/peer_connection_internal.h"
+#include "pc/sctp_data_channel.h"
+#include "pc/test/mock_peer_connection_internal.h"
+#include "rtc_base/null_socket_server.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/run_loop.h"
+
+namespace webrtc {
+
+namespace {
+
+using ::testing::NiceMock;
+using ::testing::Return;
+
+class MockDataChannelTransport : public webrtc::DataChannelTransportInterface {
+ public:
+ ~MockDataChannelTransport() override {}
+
+ MOCK_METHOD(RTCError, OpenChannel, (int channel_id), (override));
+ MOCK_METHOD(RTCError,
+ SendData,
+ (int channel_id,
+ const SendDataParams& params,
+ const rtc::CopyOnWriteBuffer& buffer),
+ (override));
+ MOCK_METHOD(RTCError, CloseChannel, (int channel_id), (override));
+ MOCK_METHOD(void, SetDataSink, (DataChannelSink * sink), (override));
+ MOCK_METHOD(bool, IsReadyToSend, (), (const, override));
+};
+
+// Convenience class for tests to ensure that shutdown methods for DCC
+// are consistently called. In practice SdpOfferAnswerHandler will call
+// TeardownDataChannelTransport_n on the network thread when destroying the
+// data channel transport and PeerConnection calls PrepareForShutdown() from
+// within PeerConnection::Close(). The DataChannelControllerForTest class mimics
+// behavior by calling those methods from within its destructor.
+class DataChannelControllerForTest : public DataChannelController {
+ public:
+ explicit DataChannelControllerForTest(
+ PeerConnectionInternal* pc,
+ DataChannelTransportInterface* transport = nullptr)
+ : DataChannelController(pc) {
+ if (transport) {
+ network_thread()->BlockingCall(
+ [&] { SetupDataChannelTransport_n(transport); });
+ }
+ }
+
+ ~DataChannelControllerForTest() override {
+ network_thread()->BlockingCall(
+ [&] { TeardownDataChannelTransport_n(RTCError::OK()); });
+ PrepareForShutdown();
+ }
+};
+
+class DataChannelControllerTest : public ::testing::Test {
+ protected:
+ DataChannelControllerTest()
+ : network_thread_(std::make_unique<rtc::NullSocketServer>()) {
+ network_thread_.Start();
+ pc_ = rtc::make_ref_counted<NiceMock<MockPeerConnectionInternal>>();
+ ON_CALL(*pc_, signaling_thread)
+ .WillByDefault(Return(rtc::Thread::Current()));
+ ON_CALL(*pc_, network_thread).WillByDefault(Return(&network_thread_));
+ }
+
+ ~DataChannelControllerTest() override {
+ run_loop_.Flush();
+ network_thread_.Stop();
+ }
+
+ test::RunLoop run_loop_;
+ rtc::Thread network_thread_;
+ rtc::scoped_refptr<NiceMock<MockPeerConnectionInternal>> pc_;
+};
+
+TEST_F(DataChannelControllerTest, CreateAndDestroy) {
+ DataChannelControllerForTest dcc(pc_.get());
+}
+
+TEST_F(DataChannelControllerTest, CreateDataChannelEarlyRelease) {
+ DataChannelControllerForTest dcc(pc_.get());
+ auto ret = dcc.InternalCreateDataChannelWithProxy(
+ "label", InternalDataChannelInit(DataChannelInit()));
+ ASSERT_TRUE(ret.ok());
+ auto channel = ret.MoveValue();
+ // DCC still holds a reference to the channel. Release this reference early.
+ channel = nullptr;
+}
+
+TEST_F(DataChannelControllerTest, CreateDataChannelEarlyClose) {
+ DataChannelControllerForTest dcc(pc_.get());
+ EXPECT_FALSE(dcc.HasDataChannels());
+ EXPECT_FALSE(dcc.HasUsedDataChannels());
+ auto ret = dcc.InternalCreateDataChannelWithProxy(
+ "label", InternalDataChannelInit(DataChannelInit()));
+ ASSERT_TRUE(ret.ok());
+ auto channel = ret.MoveValue();
+ EXPECT_TRUE(dcc.HasDataChannels());
+ EXPECT_TRUE(dcc.HasUsedDataChannels());
+ channel->Close();
+ run_loop_.Flush();
+ EXPECT_FALSE(dcc.HasDataChannels());
+ EXPECT_TRUE(dcc.HasUsedDataChannels());
+}
+
+TEST_F(DataChannelControllerTest, CreateDataChannelLateRelease) {
+ auto dcc = std::make_unique<DataChannelControllerForTest>(pc_.get());
+ auto ret = dcc->InternalCreateDataChannelWithProxy(
+ "label", InternalDataChannelInit(DataChannelInit()));
+ ASSERT_TRUE(ret.ok());
+ auto channel = ret.MoveValue();
+ dcc.reset();
+ channel = nullptr;
+}
+
+TEST_F(DataChannelControllerTest, CloseAfterControllerDestroyed) {
+ auto dcc = std::make_unique<DataChannelControllerForTest>(pc_.get());
+ auto ret = dcc->InternalCreateDataChannelWithProxy(
+ "label", InternalDataChannelInit(DataChannelInit()));
+ ASSERT_TRUE(ret.ok());
+ auto channel = ret.MoveValue();
+ dcc.reset();
+ channel->Close();
+}
+
+// Allocate the maximum number of data channels and then one more.
+// The last allocation should fail.
+TEST_F(DataChannelControllerTest, MaxChannels) {
+ NiceMock<MockDataChannelTransport> transport;
+ int channel_id = 0;
+
+ ON_CALL(*pc_, GetSctpSslRole_n).WillByDefault([&]() {
+ return absl::optional<rtc::SSLRole>((channel_id & 1) ? rtc::SSL_SERVER
+ : rtc::SSL_CLIENT);
+ });
+
+ DataChannelControllerForTest dcc(pc_.get(), &transport);
+
+ // Allocate the maximum number of channels + 1. Inside the loop, the creation
+ // process will allocate a stream id for each channel.
+ for (channel_id = 0; channel_id <= cricket::kMaxSctpStreams; ++channel_id) {
+ auto ret = dcc.InternalCreateDataChannelWithProxy(
+ "label", InternalDataChannelInit(DataChannelInit()));
+ if (channel_id == cricket::kMaxSctpStreams) {
+ // We've reached the maximum and the previous call should have failed.
+ EXPECT_FALSE(ret.ok());
+ } else {
+ // We're still working on saturating the pool. Things should be working.
+ EXPECT_TRUE(ret.ok());
+ }
+ }
+}
+
+// Test that while a data channel is in the `kClosing` state, its StreamId does
+// not get re-used for new channels. Only once the state reaches `kClosed`
+// should a StreamId be available again for allocation.
+TEST_F(DataChannelControllerTest, NoStreamIdReuseWhileClosing) {
+ ON_CALL(*pc_, GetSctpSslRole_n).WillByDefault([&]() {
+ return rtc::SSL_CLIENT;
+ });
+
+ NiceMock<MockDataChannelTransport> transport; // Wider scope than `dcc`.
+ DataChannelControllerForTest dcc(pc_.get(), &transport);
+
+ // Create the first channel and check that we got the expected, first sid.
+ auto channel1 = dcc.InternalCreateDataChannelWithProxy(
+ "label", InternalDataChannelInit(DataChannelInit()))
+ .MoveValue();
+ ASSERT_EQ(channel1->id(), 0);
+
+ // Start closing the channel and make sure its state is `kClosing`
+ channel1->Close();
+ ASSERT_EQ(channel1->state(), DataChannelInterface::DataState::kClosing);
+
+ // Create a second channel and make sure we get a new StreamId, not the same
+ // as that of channel1.
+ auto channel2 = dcc.InternalCreateDataChannelWithProxy(
+ "label2", InternalDataChannelInit(DataChannelInit()))
+ .MoveValue();
+ ASSERT_NE(channel2->id(), channel1->id()); // In practice the id will be 2.
+
+ // Simulate the acknowledgement of the channel closing from the transport.
+ // This completes the closing operation of channel1.
+ pc_->network_thread()->BlockingCall([&] { dcc.OnChannelClosed(0); });
+ run_loop_.Flush();
+ ASSERT_EQ(channel1->state(), DataChannelInterface::DataState::kClosed);
+
+ // Now create a third channel. This time, the id of the first channel should
+ // be available again and therefore the ids of the first and third channels
+ // should be the same.
+ auto channel3 = dcc.InternalCreateDataChannelWithProxy(
+ "label3", InternalDataChannelInit(DataChannelInit()))
+ .MoveValue();
+ EXPECT_EQ(channel3->id(), channel1->id());
+}
+
+} // namespace
+} // namespace webrtc