summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/pc/data_channel_integrationtest.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /third_party/libwebrtc/pc/data_channel_integrationtest.cc
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/pc/data_channel_integrationtest.cc')
-rw-r--r--third_party/libwebrtc/pc/data_channel_integrationtest.cc1167
1 files changed, 1167 insertions, 0 deletions
diff --git a/third_party/libwebrtc/pc/data_channel_integrationtest.cc b/third_party/libwebrtc/pc/data_channel_integrationtest.cc
new file mode 100644
index 0000000000..faec76d03e
--- /dev/null
+++ b/third_party/libwebrtc/pc/data_channel_integrationtest.cc
@@ -0,0 +1,1167 @@
+/*
+ * Copyright 2012 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 <stdint.h>
+
+#include <cstdlib>
+#include <iterator>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "absl/algorithm/container.h"
+#include "absl/types/optional.h"
+#include "api/data_channel_interface.h"
+#include "api/dtls_transport_interface.h"
+#include "api/peer_connection_interface.h"
+#include "api/scoped_refptr.h"
+#include "api/sctp_transport_interface.h"
+#include "api/stats/rtc_stats_report.h"
+#include "api/stats/rtcstats_objects.h"
+#include "api/units/time_delta.h"
+#include "p2p/base/transport_description.h"
+#include "p2p/base/transport_info.h"
+#include "pc/media_session.h"
+#include "pc/session_description.h"
+#include "pc/test/integration_test_helpers.h"
+#include "pc/test/mock_peer_connection_observers.h"
+#include "rtc_base/copy_on_write_buffer.h"
+#include "rtc_base/fake_clock.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/helpers.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/safe_conversions.h"
+#include "rtc_base/virtual_socket_server.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+// All tests in this file require SCTP support.
+#ifdef WEBRTC_HAVE_SCTP
+
+#if defined(WEBRTC_ANDROID)
+// Disable heavy tests running on low-end Android devices.
+#define DISABLED_ON_ANDROID(t) DISABLED_##t
+#else
+#define DISABLED_ON_ANDROID(t) t
+#endif
+
+class DataChannelIntegrationTest
+ : public PeerConnectionIntegrationBaseTest,
+ public ::testing::WithParamInterface<std::tuple<SdpSemantics, bool>> {
+ protected:
+ DataChannelIntegrationTest()
+ : PeerConnectionIntegrationBaseTest(std::get<0>(GetParam())),
+ allow_media_(std::get<1>(GetParam())) {}
+ bool allow_media() { return allow_media_; }
+
+ bool CreatePeerConnectionWrappers() {
+ if (allow_media_) {
+ return PeerConnectionIntegrationBaseTest::CreatePeerConnectionWrappers();
+ }
+ return PeerConnectionIntegrationBaseTest::
+ CreatePeerConnectionWrappersWithoutMediaEngine();
+ }
+
+ private:
+ // True if media is allowed to be added
+ const bool allow_media_;
+};
+
+// Fake clock must be set before threads are started to prevent race on
+// Set/GetClockForTesting().
+// To achieve that, multiple inheritance is used as a mixin pattern
+// where order of construction is finely controlled.
+// This also ensures peerconnection is closed before switching back to non-fake
+// clock, avoiding other races and DCHECK failures such as in rtp_sender.cc.
+class FakeClockForTest : public rtc::ScopedFakeClock {
+ protected:
+ FakeClockForTest() {
+ // Some things use a time of "0" as a special value, so we need to start out
+ // the fake clock at a nonzero time.
+ // TODO(deadbeef): Fix this.
+ AdvanceTime(webrtc::TimeDelta::Seconds(1));
+ }
+
+ // Explicit handle.
+ ScopedFakeClock& FakeClock() { return *this; }
+};
+
+class DataChannelIntegrationTestPlanB
+ : public PeerConnectionIntegrationBaseTest {
+ protected:
+ DataChannelIntegrationTestPlanB()
+ : PeerConnectionIntegrationBaseTest(SdpSemantics::kPlanB_DEPRECATED) {}
+};
+
+class DataChannelIntegrationTestUnifiedPlan
+ : public PeerConnectionIntegrationBaseTest {
+ protected:
+ DataChannelIntegrationTestUnifiedPlan()
+ : PeerConnectionIntegrationBaseTest(SdpSemantics::kUnifiedPlan) {}
+};
+
+void MakeActiveSctpOffer(cricket::SessionDescription* desc) {
+ auto& transport_infos = desc->transport_infos();
+ for (auto& transport_info : transport_infos) {
+ transport_info.description.connection_role = cricket::CONNECTIONROLE_ACTIVE;
+ }
+}
+
+// This test causes a PeerConnection to enter Disconnected state, and
+// sends data on a DataChannel while disconnected.
+// The data should be surfaced when the connection reestablishes.
+TEST_P(DataChannelIntegrationTest, DataChannelWhileDisconnected) {
+ CreatePeerConnectionWrappers();
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_observer(), kDefaultTimeout);
+ std::string data1 = "hello first";
+ caller()->data_channel()->Send(DataBuffer(data1));
+ EXPECT_EQ_WAIT(data1, callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ // Cause a network outage
+ virtual_socket_server()->set_drop_probability(1.0);
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionDisconnected,
+ caller()->standardized_ice_connection_state(),
+ kDefaultTimeout);
+ std::string data2 = "hello second";
+ caller()->data_channel()->Send(DataBuffer(data2));
+ // Remove the network outage. The connection should reestablish.
+ virtual_socket_server()->set_drop_probability(0.0);
+ EXPECT_EQ_WAIT(data2, callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+}
+
+// This test causes a PeerConnection to enter Disconnected state,
+// sends data on a DataChannel while disconnected, and then triggers
+// an ICE restart.
+// The data should be surfaced when the connection reestablishes.
+TEST_P(DataChannelIntegrationTest, DataChannelWhileDisconnectedIceRestart) {
+ CreatePeerConnectionWrappers();
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_observer(), kDefaultTimeout);
+ std::string data1 = "hello first";
+ caller()->data_channel()->Send(DataBuffer(data1));
+ EXPECT_EQ_WAIT(data1, callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ // Cause a network outage
+ virtual_socket_server()->set_drop_probability(1.0);
+ ASSERT_EQ_WAIT(PeerConnectionInterface::kIceConnectionDisconnected,
+ caller()->standardized_ice_connection_state(),
+ kDefaultTimeout);
+ std::string data2 = "hello second";
+ caller()->data_channel()->Send(DataBuffer(data2));
+
+ // Trigger an ICE restart. The signaling channel is not affected by
+ // the network outage.
+ caller()->SetOfferAnswerOptions(IceRestartOfferAnswerOptions());
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ // Remove the network outage. The connection should reestablish.
+ virtual_socket_server()->set_drop_probability(0.0);
+ EXPECT_EQ_WAIT(data2, callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+}
+
+// This test sets up a call between two parties with audio, video and an SCTP
+// data channel.
+TEST_P(DataChannelIntegrationTest, EndToEndCallWithSctpDataChannel) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ // Expect that data channel created on caller side will show up for callee as
+ // well.
+ caller()->CreateDataChannel();
+ if (allow_media()) {
+ caller()->AddAudioVideoTracks();
+ callee()->AddAudioVideoTracks();
+ }
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ if (allow_media()) {
+ // Ensure the existence of the SCTP data channel didn't impede audio/video.
+ MediaExpectations media_expectations;
+ media_expectations.ExpectBidirectionalAudioAndVideo();
+ ASSERT_TRUE(ExpectNewFrames(media_expectations));
+ }
+ // Caller data channel should already exist (it created one). Callee data
+ // channel may not exist yet, since negotiation happens in-band, not in SDP.
+ ASSERT_NE(nullptr, caller()->data_channel());
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+
+ // Ensure data can be sent in both directions.
+ std::string data = "hello world";
+ caller()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ callee()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, caller()->data_observer()->last_message(),
+ kDefaultTimeout);
+}
+
+// This test sets up a call between two parties with an SCTP
+// data channel only, and sends messages of various sizes.
+TEST_P(DataChannelIntegrationTest,
+ EndToEndCallWithSctpDataChannelVariousSizes) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ // Expect that data channel created on caller side will show up for callee as
+ // well.
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ // Caller data channel should already exist (it created one). Callee data
+ // channel may not exist yet, since negotiation happens in-band, not in SDP.
+ ASSERT_NE(nullptr, caller()->data_channel());
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+
+ for (int message_size = 1; message_size < 100000; message_size *= 2) {
+ std::string data(message_size, 'a');
+ caller()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ callee()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, caller()->data_observer()->last_message(),
+ kDefaultTimeout);
+ }
+ // Specifically probe the area around the MTU size.
+ for (int message_size = 1100; message_size < 1300; message_size += 1) {
+ std::string data(message_size, 'a');
+ caller()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ callee()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, caller()->data_observer()->last_message(),
+ kDefaultTimeout);
+ }
+}
+
+// This test sets up a call between two parties with an SCTP
+// data channel only, and sends empty messages
+TEST_P(DataChannelIntegrationTest,
+ EndToEndCallWithSctpDataChannelEmptyMessages) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ // Expect that data channel created on caller side will show up for callee as
+ // well.
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ // Caller data channel should already exist (it created one). Callee data
+ // channel may not exist yet, since negotiation happens in-band, not in SDP.
+ ASSERT_NE(nullptr, caller()->data_channel());
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+
+ // Ensure data can be sent in both directions.
+ // Sending empty string data
+ std::string data = "";
+ caller()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(1u, callee()->data_observer()->received_message_count(),
+ kDefaultTimeout);
+ EXPECT_TRUE(callee()->data_observer()->last_message().empty());
+ EXPECT_FALSE(callee()->data_observer()->messages().back().binary);
+ callee()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(1u, caller()->data_observer()->received_message_count(),
+ kDefaultTimeout);
+ EXPECT_TRUE(caller()->data_observer()->last_message().empty());
+ EXPECT_FALSE(caller()->data_observer()->messages().back().binary);
+
+ // Sending empty binary data
+ rtc::CopyOnWriteBuffer empty_buffer;
+ caller()->data_channel()->Send(DataBuffer(empty_buffer, true));
+ EXPECT_EQ_WAIT(2u, callee()->data_observer()->received_message_count(),
+ kDefaultTimeout);
+ EXPECT_TRUE(callee()->data_observer()->last_message().empty());
+ EXPECT_TRUE(callee()->data_observer()->messages().back().binary);
+ callee()->data_channel()->Send(DataBuffer(empty_buffer, true));
+ EXPECT_EQ_WAIT(2u, caller()->data_observer()->received_message_count(),
+ kDefaultTimeout);
+ EXPECT_TRUE(caller()->data_observer()->last_message().empty());
+ EXPECT_TRUE(caller()->data_observer()->messages().back().binary);
+}
+
+TEST_P(DataChannelIntegrationTest,
+ EndToEndCallWithSctpDataChannelLowestSafeMtu) {
+ // The lowest payload size limit that's tested and found safe for this
+ // application. Note that this is not the safe limit under all conditions;
+ // in particular, the default is not the largest DTLS signature, and
+ // this test does not use TURN.
+ const size_t kLowestSafePayloadSizeLimit = 1225;
+
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ // Expect that data channel created on caller side will show up for callee as
+ // well.
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ // Caller data channel should already exist (it created one). Callee data
+ // channel may not exist yet, since negotiation happens in-band, not in SDP.
+ ASSERT_NE(nullptr, caller()->data_channel());
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+
+ virtual_socket_server()->set_max_udp_payload(kLowestSafePayloadSizeLimit);
+ for (int message_size = 1140; message_size < 1240; message_size += 1) {
+ std::string data(message_size, 'a');
+ caller()->data_channel()->Send(DataBuffer(data));
+ ASSERT_EQ_WAIT(data, callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ callee()->data_channel()->Send(DataBuffer(data));
+ ASSERT_EQ_WAIT(data, caller()->data_observer()->last_message(),
+ kDefaultTimeout);
+ }
+}
+
+// This test verifies that lowering the MTU of the connection will cause
+// the datachannel to not transmit reliably.
+// The purpose of this test is to ensure that we know how a too-small MTU
+// error manifests itself.
+TEST_P(DataChannelIntegrationTest, EndToEndCallWithSctpDataChannelHarmfulMtu) {
+ // The lowest payload size limit that's tested and found safe for this
+ // application in this configuration (see test above).
+ const size_t kLowestSafePayloadSizeLimit = 1225;
+ // The size of the smallest message that fails to be delivered.
+ const size_t kMessageSizeThatIsNotDelivered = 1157;
+
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_NE(nullptr, caller()->data_channel());
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+
+ virtual_socket_server()->set_max_udp_payload(kLowestSafePayloadSizeLimit - 1);
+ // Probe for an undelivered or slowly delivered message. The exact
+ // size limit seems to be dependent on the message history, so make the
+ // code easily able to find the current value.
+ bool failure_seen = false;
+ for (size_t message_size = 1110; message_size < 1400; message_size++) {
+ const size_t message_count =
+ callee()->data_observer()->received_message_count();
+ const std::string data(message_size, 'a');
+ caller()->data_channel()->Send(DataBuffer(data));
+ // Wait a very short time for the message to be delivered.
+ // Note: Waiting only 10 ms is too short for Windows bots; they will
+ // flakily fail at a random frame.
+ WAIT(callee()->data_observer()->received_message_count() > message_count,
+ 100);
+ if (callee()->data_observer()->received_message_count() == message_count) {
+ ASSERT_EQ(kMessageSizeThatIsNotDelivered, message_size);
+ failure_seen = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(failure_seen);
+}
+
+// Ensure that when the callee closes an SCTP data channel, the closing
+// procedure results in the data channel being closed for the caller as well.
+TEST_P(DataChannelIntegrationTest, CalleeClosesSctpDataChannel) {
+ // Same procedure as above test.
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+ if (allow_media()) {
+ caller()->AddAudioVideoTracks();
+ callee()->AddAudioVideoTracks();
+ }
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_NE(nullptr, caller()->data_channel());
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ ASSERT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+
+ // Close the data channel on the callee side, and wait for it to reach the
+ // "closed" state on both sides.
+ callee()->data_channel()->Close();
+
+ DataChannelInterface::DataState expected_states[] = {
+ DataChannelInterface::DataState::kConnecting,
+ DataChannelInterface::DataState::kOpen,
+ DataChannelInterface::DataState::kClosing,
+ DataChannelInterface::DataState::kClosed};
+
+ EXPECT_EQ_WAIT(DataChannelInterface::DataState::kClosed,
+ caller()->data_observer()->state(), kDefaultTimeout);
+ EXPECT_THAT(caller()->data_observer()->states(),
+ ::testing::ElementsAreArray(expected_states));
+
+ EXPECT_EQ_WAIT(DataChannelInterface::DataState::kClosed,
+ callee()->data_observer()->state(), kDefaultTimeout);
+ EXPECT_THAT(callee()->data_observer()->states(),
+ ::testing::ElementsAreArray(expected_states));
+}
+
+TEST_P(DataChannelIntegrationTest, SctpDataChannelConfigSentToOtherSide) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ webrtc::DataChannelInit init;
+ init.id = 53;
+ init.maxRetransmits = 52;
+ caller()->CreateDataChannel("data-channel", &init);
+ if (allow_media()) {
+ caller()->AddAudioVideoTracks();
+ callee()->AddAudioVideoTracks();
+ }
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+ // Since "negotiated" is false, the "id" parameter should be ignored.
+ EXPECT_NE(init.id, callee()->data_channel()->id());
+ EXPECT_EQ("data-channel", callee()->data_channel()->label());
+ EXPECT_EQ(init.maxRetransmits, callee()->data_channel()->maxRetransmits());
+ EXPECT_FALSE(callee()->data_channel()->negotiated());
+}
+
+// Test sctp's ability to process unordered data stream, where data actually
+// arrives out of order using simulated delays. Previously there have been some
+// bugs in this area.
+TEST_P(DataChannelIntegrationTest, StressTestUnorderedSctpDataChannel) {
+ // Introduce random network delays.
+ // Otherwise it's not a true "unordered" test.
+ virtual_socket_server()->set_delay_mean(20);
+ virtual_socket_server()->set_delay_stddev(5);
+ virtual_socket_server()->UpdateDelayDistribution();
+ // Normal procedure, but with unordered data channel config.
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ webrtc::DataChannelInit init;
+ init.ordered = false;
+ caller()->CreateDataChannel(&init);
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_NE(nullptr, caller()->data_channel());
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ ASSERT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+
+ static constexpr int kNumMessages = 100;
+ // Deliberately chosen to be larger than the MTU so messages get fragmented.
+ static constexpr size_t kMaxMessageSize = 4096;
+ // Create and send random messages.
+ std::vector<std::string> sent_messages;
+ for (int i = 0; i < kNumMessages; ++i) {
+ size_t length =
+ (rand() % kMaxMessageSize) + 1; // NOLINT (rand_r instead of rand)
+ std::string message;
+ ASSERT_TRUE(rtc::CreateRandomString(length, &message));
+ caller()->data_channel()->Send(DataBuffer(message));
+ callee()->data_channel()->Send(DataBuffer(message));
+ sent_messages.push_back(message);
+ }
+
+ // Wait for all messages to be received.
+ EXPECT_EQ_WAIT(rtc::checked_cast<size_t>(kNumMessages),
+ caller()->data_observer()->received_message_count(),
+ kDefaultTimeout);
+ EXPECT_EQ_WAIT(rtc::checked_cast<size_t>(kNumMessages),
+ callee()->data_observer()->received_message_count(),
+ kDefaultTimeout);
+
+ // Sort and compare to make sure none of the messages were corrupted.
+ std::vector<std::string> caller_received_messages;
+ absl::c_transform(caller()->data_observer()->messages(),
+ std::back_inserter(caller_received_messages),
+ [](const auto& a) { return a.data; });
+
+ std::vector<std::string> callee_received_messages;
+ absl::c_transform(callee()->data_observer()->messages(),
+ std::back_inserter(callee_received_messages),
+ [](const auto& a) { return a.data; });
+
+ absl::c_sort(sent_messages);
+ absl::c_sort(caller_received_messages);
+ absl::c_sort(callee_received_messages);
+ EXPECT_EQ(sent_messages, caller_received_messages);
+ EXPECT_EQ(sent_messages, callee_received_messages);
+}
+
+// Repeatedly open and close data channels on a peer connection to check that
+// the channels are properly negotiated and SCTP stream IDs properly recycled.
+TEST_P(DataChannelIntegrationTest, StressTestOpenCloseChannelNoDelay) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+
+ int channel_id = 0;
+ const size_t kChannelCount = 8;
+ const size_t kIterations = 10;
+ bool has_negotiated = false;
+
+ webrtc::DataChannelInit init;
+ for (size_t repeats = 0; repeats < kIterations; ++repeats) {
+ RTC_LOG(LS_INFO) << "Iteration " << (repeats + 1) << "/" << kIterations;
+
+ for (size_t i = 0; i < kChannelCount; ++i) {
+ rtc::StringBuilder sb;
+ sb << "channel-" << channel_id++;
+ caller()->CreateDataChannel(sb.Release(), &init);
+ }
+ ASSERT_EQ(caller()->data_channels().size(), kChannelCount);
+
+ if (!has_negotiated) {
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ has_negotiated = true;
+ }
+
+ for (size_t i = 0; i < kChannelCount; ++i) {
+ ASSERT_EQ_WAIT(caller()->data_channels()[i]->state(),
+ DataChannelInterface::DataState::kOpen, kDefaultTimeout);
+ RTC_LOG(LS_INFO) << "Caller Channel "
+ << caller()->data_channels()[i]->label() << " with id "
+ << caller()->data_channels()[i]->id() << " is open.";
+ }
+ ASSERT_EQ_WAIT(callee()->data_channels().size(), kChannelCount,
+ kDefaultTimeout);
+ for (size_t i = 0; i < kChannelCount; ++i) {
+ ASSERT_EQ_WAIT(callee()->data_channels()[i]->state(),
+ DataChannelInterface::DataState::kOpen, kDefaultTimeout);
+ RTC_LOG(LS_INFO) << "Callee Channel "
+ << callee()->data_channels()[i]->label() << " with id "
+ << callee()->data_channels()[i]->id() << " is open.";
+ }
+
+ // Closing from both sides to attempt creating races.
+ // A real application would likely only close from one side.
+ for (size_t i = 0; i < kChannelCount; ++i) {
+ if (i % 3 == 0) {
+ callee()->data_channels()[i]->Close();
+ caller()->data_channels()[i]->Close();
+ } else {
+ caller()->data_channels()[i]->Close();
+ callee()->data_channels()[i]->Close();
+ }
+ }
+
+ for (size_t i = 0; i < kChannelCount; ++i) {
+ ASSERT_EQ_WAIT(caller()->data_channels()[i]->state(),
+ DataChannelInterface::DataState::kClosed, kDefaultTimeout);
+ ASSERT_EQ_WAIT(callee()->data_channels()[i]->state(),
+ DataChannelInterface::DataState::kClosed, kDefaultTimeout);
+ }
+
+ caller()->data_channels().clear();
+ caller()->data_observers().clear();
+ callee()->data_channels().clear();
+ callee()->data_observers().clear();
+ }
+}
+
+// Repeatedly open and close data channels on a peer connection to check that
+// the channels are properly negotiated and SCTP stream IDs properly recycled.
+// Some delay is added for better coverage.
+TEST_P(DataChannelIntegrationTest, StressTestOpenCloseChannelWithDelay) {
+ // Simulate some network delay
+ virtual_socket_server()->set_delay_mean(20);
+ virtual_socket_server()->set_delay_stddev(5);
+ virtual_socket_server()->UpdateDelayDistribution();
+
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+
+ int channel_id = 0;
+ const size_t kChannelCount = 8;
+ const size_t kIterations = 10;
+ bool has_negotiated = false;
+
+ webrtc::DataChannelInit init;
+ for (size_t repeats = 0; repeats < kIterations; ++repeats) {
+ RTC_LOG(LS_INFO) << "Iteration " << (repeats + 1) << "/" << kIterations;
+
+ for (size_t i = 0; i < kChannelCount; ++i) {
+ rtc::StringBuilder sb;
+ sb << "channel-" << channel_id++;
+ caller()->CreateDataChannel(sb.Release(), &init);
+ }
+ ASSERT_EQ(caller()->data_channels().size(), kChannelCount);
+
+ if (!has_negotiated) {
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ has_negotiated = true;
+ }
+
+ for (size_t i = 0; i < kChannelCount; ++i) {
+ ASSERT_EQ_WAIT(caller()->data_channels()[i]->state(),
+ DataChannelInterface::DataState::kOpen, kDefaultTimeout);
+ RTC_LOG(LS_INFO) << "Caller Channel "
+ << caller()->data_channels()[i]->label() << " with id "
+ << caller()->data_channels()[i]->id() << " is open.";
+ }
+ ASSERT_EQ_WAIT(callee()->data_channels().size(), kChannelCount,
+ kDefaultTimeout);
+ for (size_t i = 0; i < kChannelCount; ++i) {
+ ASSERT_EQ_WAIT(callee()->data_channels()[i]->state(),
+ DataChannelInterface::DataState::kOpen, kDefaultTimeout);
+ RTC_LOG(LS_INFO) << "Callee Channel "
+ << callee()->data_channels()[i]->label() << " with id "
+ << callee()->data_channels()[i]->id() << " is open.";
+ }
+
+ // Closing from both sides to attempt creating races.
+ // A real application would likely only close from one side.
+ for (size_t i = 0; i < kChannelCount; ++i) {
+ if (i % 3 == 0) {
+ callee()->data_channels()[i]->Close();
+ caller()->data_channels()[i]->Close();
+ } else {
+ caller()->data_channels()[i]->Close();
+ callee()->data_channels()[i]->Close();
+ }
+ }
+
+ for (size_t i = 0; i < kChannelCount; ++i) {
+ ASSERT_EQ_WAIT(caller()->data_channels()[i]->state(),
+ DataChannelInterface::DataState::kClosed, kDefaultTimeout);
+ ASSERT_EQ_WAIT(callee()->data_channels()[i]->state(),
+ DataChannelInterface::DataState::kClosed, kDefaultTimeout);
+ }
+
+ caller()->data_channels().clear();
+ caller()->data_observers().clear();
+ callee()->data_channels().clear();
+ callee()->data_observers().clear();
+ }
+}
+
+// This test sets up a call between two parties with audio, and video. When
+// audio and video are setup and flowing, an SCTP data channel is negotiated.
+TEST_P(DataChannelIntegrationTest, AddSctpDataChannelInSubsequentOffer) {
+ // This test can't be performed without media.
+ if (!allow_media()) {
+ return;
+ }
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ // Do initial offer/answer with audio/video.
+ caller()->AddAudioVideoTracks();
+ callee()->AddAudioVideoTracks();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ // Create data channel and do new offer and answer.
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ // Caller data channel should already exist (it created one). Callee data
+ // channel may not exist yet, since negotiation happens in-band, not in SDP.
+ ASSERT_NE(nullptr, caller()->data_channel());
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+ // Ensure data can be sent in both directions.
+ std::string data = "hello world";
+ caller()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ callee()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, caller()->data_observer()->last_message(),
+ kDefaultTimeout);
+}
+
+// Set up a connection initially just using SCTP data channels, later
+// upgrading to audio/video, ensuring frames are received end-to-end.
+// Effectively the inverse of the test above. This was broken in M57; see
+// https://crbug.com/711243
+TEST_P(DataChannelIntegrationTest, SctpDataChannelToAudioVideoUpgrade) {
+ // This test can't be performed without media.
+ if (!allow_media()) {
+ return;
+ }
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ // Do initial offer/answer with just data channel.
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ // Wait until data can be sent over the data channel.
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ ASSERT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+
+ // Do subsequent offer/answer with two-way audio and video. Audio and video
+ // should end up bundled on the DTLS/ICE transport already used for data.
+ caller()->AddAudioVideoTracks();
+ callee()->AddAudioVideoTracks();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ MediaExpectations media_expectations;
+ media_expectations.ExpectBidirectionalAudioAndVideo();
+ ASSERT_TRUE(ExpectNewFrames(media_expectations));
+}
+
+static void MakeSpecCompliantSctpOffer(cricket::SessionDescription* desc) {
+ cricket::SctpDataContentDescription* dcd_offer =
+ GetFirstSctpDataContentDescription(desc);
+ // See https://crbug.com/webrtc/11211 - this function is a no-op
+ ASSERT_TRUE(dcd_offer);
+ dcd_offer->set_use_sctpmap(false);
+ dcd_offer->set_protocol("UDP/DTLS/SCTP");
+}
+
+// Test that the data channel works when a spec-compliant SCTP m= section is
+// offered (using "a=sctp-port" instead of "a=sctpmap", and using
+// "UDP/DTLS/SCTP" as the protocol).
+TEST_P(DataChannelIntegrationTest,
+ DataChannelWorksWhenSpecCompliantSctpOfferReceived) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+ caller()->SetGeneratedSdpMunger(MakeSpecCompliantSctpOffer);
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+
+ // Ensure data can be sent in both directions.
+ std::string data = "hello world";
+ caller()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ callee()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, caller()->data_observer()->last_message(),
+ kDefaultTimeout);
+}
+
+// Test that after closing PeerConnections, they stop sending any packets
+// (ICE, DTLS, RTP...).
+TEST_P(DataChannelIntegrationTest, ClosingConnectionStopsPacketFlow) {
+ // This test can't be performed without media.
+ if (!allow_media()) {
+ return;
+ }
+ // Set up audio/video/data, wait for some frames to be received.
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ caller()->AddAudioVideoTracks();
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ MediaExpectations media_expectations;
+ media_expectations.CalleeExpectsSomeAudioAndVideo();
+ ASSERT_TRUE(ExpectNewFrames(media_expectations));
+ // Close PeerConnections.
+ ClosePeerConnections();
+ // Pump messages for a second, and ensure no new packets end up sent.
+ uint32_t sent_packets_a = virtual_socket_server()->sent_packets();
+ WAIT(false, 1000);
+ uint32_t sent_packets_b = virtual_socket_server()->sent_packets();
+ EXPECT_EQ(sent_packets_a, sent_packets_b);
+}
+
+TEST_P(DataChannelIntegrationTest, DtlsRoleIsSetNormally) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+ ASSERT_FALSE(caller()->pc()->GetSctpTransport());
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ ASSERT_TRUE(caller()->pc()->GetSctpTransport());
+ ASSERT_TRUE(
+ caller()->pc()->GetSctpTransport()->Information().dtls_transport());
+ EXPECT_TRUE(caller()
+ ->pc()
+ ->GetSctpTransport()
+ ->Information()
+ .dtls_transport()
+ ->Information()
+ .role());
+ EXPECT_EQ(caller()
+ ->pc()
+ ->GetSctpTransport()
+ ->Information()
+ .dtls_transport()
+ ->Information()
+ .role(),
+ DtlsTransportTlsRole::kServer);
+ EXPECT_EQ(callee()
+ ->pc()
+ ->GetSctpTransport()
+ ->Information()
+ .dtls_transport()
+ ->Information()
+ .role(),
+ DtlsTransportTlsRole::kClient);
+ // ID should be assigned according to the odd/even rule based on role;
+ // client gets even numbers, server gets odd ones. RFC 8832 section 6.
+ // TODO(hta): Test multiple channels.
+ EXPECT_EQ(caller()->data_channel()->id(), 1);
+}
+
+TEST_P(DataChannelIntegrationTest, DtlsRoleIsSetWhenReversed) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+ callee()->SetReceivedSdpMunger(MakeActiveSctpOffer);
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ EXPECT_TRUE(caller()
+ ->pc()
+ ->GetSctpTransport()
+ ->Information()
+ .dtls_transport()
+ ->Information()
+ .role());
+ EXPECT_EQ(caller()
+ ->pc()
+ ->GetSctpTransport()
+ ->Information()
+ .dtls_transport()
+ ->Information()
+ .role(),
+ DtlsTransportTlsRole::kClient);
+ EXPECT_EQ(callee()
+ ->pc()
+ ->GetSctpTransport()
+ ->Information()
+ .dtls_transport()
+ ->Information()
+ .role(),
+ DtlsTransportTlsRole::kServer);
+ // ID should be assigned according to the odd/even rule based on role;
+ // client gets even numbers, server gets odd ones. RFC 8832 section 6.
+ // TODO(hta): Test multiple channels.
+ EXPECT_EQ(caller()->data_channel()->id(), 0);
+}
+
+TEST_P(DataChannelIntegrationTest,
+ DtlsRoleIsSetWhenReversedWithChannelCollision) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+
+ callee()->SetReceivedSdpMunger([this](cricket::SessionDescription* desc) {
+ MakeActiveSctpOffer(desc);
+ callee()->CreateDataChannel();
+ });
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ ASSERT_EQ_WAIT(callee()->data_channels().size(), 2U, kDefaultTimeout);
+ ASSERT_EQ_WAIT(caller()->data_channels().size(), 2U, kDefaultTimeout);
+ EXPECT_TRUE(caller()
+ ->pc()
+ ->GetSctpTransport()
+ ->Information()
+ .dtls_transport()
+ ->Information()
+ .role());
+ EXPECT_EQ(caller()
+ ->pc()
+ ->GetSctpTransport()
+ ->Information()
+ .dtls_transport()
+ ->Information()
+ .role(),
+ DtlsTransportTlsRole::kClient);
+ EXPECT_EQ(callee()
+ ->pc()
+ ->GetSctpTransport()
+ ->Information()
+ .dtls_transport()
+ ->Information()
+ .role(),
+ DtlsTransportTlsRole::kServer);
+ // ID should be assigned according to the odd/even rule based on role;
+ // client gets even numbers, server gets odd ones. RFC 8832 section 6.
+ ASSERT_EQ(caller()->data_channels().size(), 2U);
+ ASSERT_EQ(callee()->data_channels().size(), 2U);
+ EXPECT_EQ(caller()->data_channels()[0]->id(), 0);
+ EXPECT_EQ(caller()->data_channels()[1]->id(), 1);
+ EXPECT_EQ(callee()->data_channels()[0]->id(), 1);
+ EXPECT_EQ(callee()->data_channels()[1]->id(), 0);
+}
+
+// Test that transport stats are generated by the RTCStatsCollector for a
+// connection that only involves data channels. This is a regression test for
+// crbug.com/826972.
+TEST_P(DataChannelIntegrationTest,
+ TransportStatsReportedForDataChannelOnlyConnection) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_channel(), kDefaultTimeout);
+
+ auto caller_report = caller()->NewGetStats();
+ EXPECT_EQ(1u, caller_report->GetStatsOfType<RTCTransportStats>().size());
+ auto callee_report = callee()->NewGetStats();
+ EXPECT_EQ(1u, callee_report->GetStatsOfType<RTCTransportStats>().size());
+}
+
+TEST_P(DataChannelIntegrationTest, QueuedPacketsGetDeliveredInReliableMode) {
+ CreatePeerConnectionWrappers();
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_channel(), kDefaultTimeout);
+
+ caller()->data_channel()->Send(DataBuffer("hello first"));
+ ASSERT_EQ_WAIT(1u, callee()->data_observer()->received_message_count(),
+ kDefaultTimeout);
+ // Cause a temporary network outage
+ virtual_socket_server()->set_drop_probability(1.0);
+ for (int i = 1; i <= 10; i++) {
+ caller()->data_channel()->Send(DataBuffer("Sent while blocked"));
+ }
+ // Nothing should be delivered during outage. Short wait.
+ EXPECT_EQ_WAIT(1u, callee()->data_observer()->received_message_count(), 10);
+ // Reverse outage
+ virtual_socket_server()->set_drop_probability(0.0);
+ // All packets should be delivered.
+ EXPECT_EQ_WAIT(11u, callee()->data_observer()->received_message_count(),
+ kDefaultTimeout);
+}
+
+TEST_P(DataChannelIntegrationTest, QueuedPacketsGetDroppedInUnreliableMode) {
+ CreatePeerConnectionWrappers();
+ ConnectFakeSignaling();
+ DataChannelInit init;
+ init.maxRetransmits = 0;
+ init.ordered = false;
+ caller()->CreateDataChannel(&init);
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_channel(), kDefaultTimeout);
+ caller()->data_channel()->Send(DataBuffer("hello first"));
+ ASSERT_EQ_WAIT(1u, callee()->data_observer()->received_message_count(),
+ kDefaultTimeout);
+ // Cause a temporary network outage
+ virtual_socket_server()->set_drop_probability(1.0);
+ // Send a few packets. Note that all get dropped only when all packets
+ // fit into the receiver receive window/congestion window, so that they
+ // actually get sent.
+ for (int i = 1; i <= 10; i++) {
+ caller()->data_channel()->Send(DataBuffer("Sent while blocked"));
+ }
+ // Nothing should be delivered during outage.
+ // We do a short wait to verify that delivery count is still 1.
+ WAIT(false, 10);
+ EXPECT_EQ(1u, callee()->data_observer()->received_message_count());
+ // Reverse the network outage.
+ virtual_socket_server()->set_drop_probability(0.0);
+ // Send a new packet, and wait for it to be delivered.
+ caller()->data_channel()->Send(DataBuffer("After block"));
+ EXPECT_EQ_WAIT("After block", callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ // Some messages should be lost, but first and last message should have
+ // been delivered.
+ // First, check that the protocol guarantee is preserved.
+ EXPECT_GT(11u, callee()->data_observer()->received_message_count());
+ EXPECT_LE(2u, callee()->data_observer()->received_message_count());
+ // Then, check that observed behavior (lose all messages) has not changed
+ EXPECT_EQ(2u, callee()->data_observer()->received_message_count());
+}
+
+TEST_P(DataChannelIntegrationTest,
+ QueuedPacketsGetDroppedInLifetimeLimitedMode) {
+ CreatePeerConnectionWrappers();
+ ConnectFakeSignaling();
+ DataChannelInit init;
+ init.maxRetransmitTime = 1;
+ init.ordered = false;
+ caller()->CreateDataChannel(&init);
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_channel(), kDefaultTimeout);
+ caller()->data_channel()->Send(DataBuffer("hello first"));
+ ASSERT_EQ_WAIT(1u, callee()->data_observer()->received_message_count(),
+ kDefaultTimeout);
+ // Cause a temporary network outage
+ virtual_socket_server()->set_drop_probability(1.0);
+ for (int i = 1; i <= 200; i++) {
+ caller()->data_channel()->Send(DataBuffer("Sent while blocked"));
+ }
+ // Nothing should be delivered during outage.
+ // We do a short wait to verify that delivery count is still 1,
+ // and to make sure max packet lifetime (which is in ms) is exceeded.
+ WAIT(false, 10);
+ EXPECT_EQ(1u, callee()->data_observer()->received_message_count());
+ // Reverse the network outage.
+ virtual_socket_server()->set_drop_probability(0.0);
+ // Send a new packet, and wait for it to be delivered.
+ caller()->data_channel()->Send(DataBuffer("After block"));
+ EXPECT_EQ_WAIT("After block", callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ // Some messages should be lost, but first and last message should have
+ // been delivered.
+ // First, check that the protocol guarantee is preserved.
+ EXPECT_GT(202u, callee()->data_observer()->received_message_count());
+ EXPECT_LE(2u, callee()->data_observer()->received_message_count());
+ // Then, check that observed behavior (lose some messages) has not changed
+ // DcSctp loses all messages. This is correct.
+ EXPECT_EQ(2u, callee()->data_observer()->received_message_count());
+}
+
+TEST_P(DataChannelIntegrationTest,
+ DISABLED_ON_ANDROID(SomeQueuedPacketsGetDroppedInMaxRetransmitsMode)) {
+ CreatePeerConnectionWrappers();
+ ConnectFakeSignaling();
+ DataChannelInit init;
+ init.maxRetransmits = 0;
+ init.ordered = false;
+ caller()->CreateDataChannel(&init);
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_channel(), kDefaultTimeout);
+ caller()->data_channel()->Send(DataBuffer("hello first"));
+ ASSERT_EQ_WAIT(1u, callee()->data_observer()->received_message_count(),
+ kDefaultTimeout);
+ // Cause a temporary network outage
+ virtual_socket_server()->set_drop_probability(1.0);
+ // Fill the buffer until queued data starts to build
+ size_t packet_counter = 0;
+ while (caller()->data_channel()->buffered_amount() < 1 &&
+ packet_counter < 10000) {
+ packet_counter++;
+ caller()->data_channel()->Send(DataBuffer("Sent while blocked"));
+ }
+ if (caller()->data_channel()->buffered_amount()) {
+ RTC_LOG(LS_INFO) << "Buffered data after " << packet_counter << " packets";
+ } else {
+ RTC_LOG(LS_INFO) << "No buffered data after " << packet_counter
+ << " packets";
+ }
+ // Nothing should be delivered during outage.
+ // We do a short wait to verify that delivery count is still 1.
+ WAIT(false, 10);
+ EXPECT_EQ(1u, callee()->data_observer()->received_message_count());
+ // Reverse the network outage.
+ virtual_socket_server()->set_drop_probability(0.0);
+ // Send a new packet, and wait for it to be delivered.
+ caller()->data_channel()->Send(DataBuffer("After block"));
+ EXPECT_EQ_WAIT("After block", callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ // Some messages should be lost, but first and last message should have
+ // been delivered.
+ // Due to the fact that retransmissions are only counted when the packet
+ // goes on the wire, NOT when they are stalled in queue due to
+ // congestion, we expect some of the packets to be delivered, because
+ // congestion prevented them from being sent.
+ // Citation: https://tools.ietf.org/html/rfc7496#section-3.1
+
+ // First, check that the protocol guarantee is preserved.
+ EXPECT_GT(packet_counter,
+ callee()->data_observer()->received_message_count());
+ EXPECT_LE(2u, callee()->data_observer()->received_message_count());
+ // Then, check that observed behavior (lose between 100 and 200 messages)
+ // has not changed.
+ // Usrsctp behavior is different on Android (177) and other platforms (122).
+ // Dcsctp loses 432 packets.
+ EXPECT_GT(2 + packet_counter - 100,
+ callee()->data_observer()->received_message_count());
+ EXPECT_LT(2 + packet_counter - 500,
+ callee()->data_observer()->received_message_count());
+}
+
+INSTANTIATE_TEST_SUITE_P(DataChannelIntegrationTest,
+ DataChannelIntegrationTest,
+ Combine(Values(SdpSemantics::kPlanB_DEPRECATED,
+ SdpSemantics::kUnifiedPlan),
+ testing::Bool()));
+
+TEST_F(DataChannelIntegrationTestUnifiedPlan,
+ EndToEndCallWithBundledSctpDataChannel) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+ caller()->AddAudioVideoTracks();
+ callee()->AddAudioVideoTracks();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(caller()->pc()->GetSctpTransport(), kDefaultTimeout);
+ ASSERT_EQ_WAIT(SctpTransportState::kConnected,
+ caller()->pc()->GetSctpTransport()->Information().state(),
+ kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_channel(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+}
+
+TEST_F(DataChannelIntegrationTestUnifiedPlan,
+ EndToEndCallWithDataChannelOnlyConnects) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_channel(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+ ASSERT_TRUE(caller()->data_observer()->IsOpen());
+}
+
+TEST_F(DataChannelIntegrationTestUnifiedPlan, DataChannelClosesWhenClosed) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_observer(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+ caller()->data_channel()->Close();
+ ASSERT_TRUE_WAIT(!callee()->data_observer()->IsOpen(), kDefaultTimeout);
+}
+
+TEST_F(DataChannelIntegrationTestUnifiedPlan,
+ DataChannelClosesWhenClosedReverse) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_observer(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+ callee()->data_channel()->Close();
+ ASSERT_TRUE_WAIT(!caller()->data_observer()->IsOpen(), kDefaultTimeout);
+}
+
+TEST_F(DataChannelIntegrationTestUnifiedPlan,
+ DataChannelClosesWhenPeerConnectionClosed) {
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_observer(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+ caller()->pc()->Close();
+ ASSERT_TRUE_WAIT(!callee()->data_observer()->IsOpen(), kDefaultTimeout);
+}
+
+#endif // WEBRTC_HAVE_SCTP
+
+} // namespace
+
+} // namespace webrtc