blob: 7780ac644256547c4a8b7e4d1c1801cde5aa65c1 [file] [log] [blame]
Steve Anton6f25b092017-10-23 09:39:20 -07001/*
2 * Copyright 2017 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
Karl Wiberg32df86e2017-11-03 10:24:27 +010011#include "api/audio_codecs/builtin_audio_decoder_factory.h"
12#include "api/audio_codecs/builtin_audio_encoder_factory.h"
Mirko Bonadei2ff3f492018-11-22 09:00:13 +010013#include "api/create_peerconnection_factory.h"
Steve Anton10542f22019-01-11 09:11:00 -080014#include "api/peer_connection_proxy.h"
Anders Carlsson67537952018-05-03 11:28:29 +020015#include "api/video_codecs/builtin_video_decoder_factory.h"
16#include "api/video_codecs/builtin_video_encoder_factory.h"
Steve Anton10542f22019-01-11 09:11:00 -080017#include "p2p/base/fake_port_allocator.h"
18#include "p2p/base/test_stun_server.h"
19#include "p2p/client/basic_port_allocator.h"
20#include "pc/media_session.h"
21#include "pc/peer_connection.h"
22#include "pc/peer_connection_wrapper.h"
23#include "pc/sdp_utils.h"
Steve Anton6f25b092017-10-23 09:39:20 -070024#ifdef WEBRTC_ANDROID
Steve Anton10542f22019-01-11 09:11:00 -080025#include "pc/test/android_test_initializer.h"
Steve Anton6f25b092017-10-23 09:39:20 -070026#endif
Karl Wiberg918f50c2018-07-05 11:40:33 +020027#include "absl/memory/memory.h"
Steve Anton10542f22019-01-11 09:11:00 -080028#include "pc/test/fake_audio_capture_module.h"
29#include "rtc_base/fake_network.h"
Steve Anton6f25b092017-10-23 09:39:20 -070030#include "rtc_base/gunit.h"
Steve Anton10542f22019-01-11 09:11:00 -080031#include "rtc_base/virtual_socket_server.h"
Steve Anton6f25b092017-10-23 09:39:20 -070032#include "test/gmock.h"
33
34namespace webrtc {
35
36using BundlePolicy = PeerConnectionInterface::BundlePolicy;
37using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
38using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
39using RtcpMuxPolicy = PeerConnectionInterface::RtcpMuxPolicy;
40using rtc::SocketAddress;
Steve Anton7464fca2018-01-19 11:10:37 -080041using ::testing::Combine;
Steve Anton6f25b092017-10-23 09:39:20 -070042using ::testing::ElementsAre;
43using ::testing::UnorderedElementsAre;
44using ::testing::Values;
45
46constexpr int kDefaultTimeout = 10000;
47
48// TODO(steveanton): These tests should be rewritten to use the standard
49// RtpSenderInterface/DtlsTransportInterface objects once they're available in
50// the API. The RtpSender can be used to determine which transport a given media
51// will use: https://www.w3.org/TR/webrtc/#dom-rtcrtpsender-transport
Steve Anton7464fca2018-01-19 11:10:37 -080052// Should also be able to remove GetTransceiversForTesting at that point.
Steve Anton6f25b092017-10-23 09:39:20 -070053
Harald Alvestrandad88c882018-11-28 16:47:46 +010054class FakeNetworkManagerWithNoAnyNetwork : public rtc::FakeNetworkManager {
55 public:
56 void GetAnyAddressNetworks(NetworkList* networks) override {
57 // This function allocates networks that are owned by the
58 // NetworkManager. But some tests assume that they can release
59 // all networks independent of the network manager.
60 // In order to prevent use-after-free issues, don't allow this
61 // function to have any effect when run in tests.
62 RTC_LOG(LS_INFO) << "FakeNetworkManager::GetAnyAddressNetworks ignored";
63 }
64};
65
Steve Anton6f25b092017-10-23 09:39:20 -070066class PeerConnectionWrapperForBundleTest : public PeerConnectionWrapper {
67 public:
68 using PeerConnectionWrapper::PeerConnectionWrapper;
69
70 bool AddIceCandidateToMedia(cricket::Candidate* candidate,
71 cricket::MediaType media_type) {
72 auto* desc = pc()->remote_description()->description();
73 for (size_t i = 0; i < desc->contents().size(); i++) {
74 const auto& content = desc->contents()[i];
Steve Antonb1c1de12017-12-21 15:14:30 -080075 if (content.media_description()->type() == media_type) {
Steve Anton6f25b092017-10-23 09:39:20 -070076 candidate->set_transport_name(content.name);
Steve Anton27ab0e52018-07-23 15:11:53 -070077 std::unique_ptr<IceCandidateInterface> jsep_candidate =
78 CreateIceCandidate(content.name, i, *candidate);
79 return pc()->AddIceCandidate(jsep_candidate.get());
Steve Anton6f25b092017-10-23 09:39:20 -070080 }
81 }
82 RTC_NOTREACHED();
83 return false;
84 }
85
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -070086 RtpTransportInternal* voice_rtp_transport() {
87 return (voice_channel() ? voice_channel()->rtp_transport() : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -070088 }
89
90 cricket::VoiceChannel* voice_channel() {
Steve Antonb8867112018-02-13 10:07:54 -080091 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
Mirko Bonadei739baf02019-01-27 17:29:42 +010092 for (const auto& transceiver : transceivers) {
Steve Anton69470252018-02-09 11:43:08 -080093 if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
Steve Anton7464fca2018-01-19 11:10:37 -080094 return static_cast<cricket::VoiceChannel*>(
95 transceiver->internal()->channel());
96 }
97 }
98 return nullptr;
Steve Anton6f25b092017-10-23 09:39:20 -070099 }
100
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700101 RtpTransportInternal* video_rtp_transport() {
102 return (video_channel() ? video_channel()->rtp_transport() : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -0700103 }
104
105 cricket::VideoChannel* video_channel() {
Steve Antonb8867112018-02-13 10:07:54 -0800106 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
Mirko Bonadei739baf02019-01-27 17:29:42 +0100107 for (const auto& transceiver : transceivers) {
Steve Anton69470252018-02-09 11:43:08 -0800108 if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) {
Steve Anton7464fca2018-01-19 11:10:37 -0800109 return static_cast<cricket::VideoChannel*>(
110 transceiver->internal()->channel());
111 }
112 }
113 return nullptr;
Steve Anton6f25b092017-10-23 09:39:20 -0700114 }
115
116 PeerConnection* GetInternalPeerConnection() {
Mirko Bonadeie97de912017-12-13 11:29:34 +0100117 auto* pci =
118 static_cast<PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(
119 pc());
120 return static_cast<PeerConnection*>(pci->internal());
Steve Anton6f25b092017-10-23 09:39:20 -0700121 }
122
123 // Returns true if the stats indicate that an ICE connection is either in
124 // progress or established with the given remote address.
125 bool HasConnectionWithRemoteAddress(const SocketAddress& address) {
126 auto report = GetStats();
127 if (!report) {
128 return false;
129 }
130 std::string matching_candidate_id;
131 for (auto* ice_candidate_stats :
132 report->GetStatsOfType<RTCRemoteIceCandidateStats>()) {
133 if (*ice_candidate_stats->ip == address.HostAsURIString() &&
134 *ice_candidate_stats->port == address.port()) {
135 matching_candidate_id = ice_candidate_stats->id();
136 break;
137 }
138 }
139 if (matching_candidate_id.empty()) {
140 return false;
141 }
142 for (auto* pair_stats :
143 report->GetStatsOfType<RTCIceCandidatePairStats>()) {
144 if (*pair_stats->remote_candidate_id == matching_candidate_id) {
145 if (*pair_stats->state == RTCStatsIceCandidatePairState::kInProgress ||
146 *pair_stats->state == RTCStatsIceCandidatePairState::kSucceeded) {
147 return true;
148 }
149 }
150 }
151 return false;
152 }
153
154 rtc::FakeNetworkManager* network() { return network_; }
155
156 void set_network(rtc::FakeNetworkManager* network) { network_ = network; }
157
158 private:
159 rtc::FakeNetworkManager* network_;
160};
161
Steve Anton7464fca2018-01-19 11:10:37 -0800162class PeerConnectionBundleBaseTest : public ::testing::Test {
Steve Anton6f25b092017-10-23 09:39:20 -0700163 protected:
164 typedef std::unique_ptr<PeerConnectionWrapperForBundleTest> WrapperPtr;
165
Steve Anton7464fca2018-01-19 11:10:37 -0800166 explicit PeerConnectionBundleBaseTest(SdpSemantics sdp_semantics)
167 : vss_(new rtc::VirtualSocketServer()),
168 main_(vss_.get()),
169 sdp_semantics_(sdp_semantics) {
Steve Anton6f25b092017-10-23 09:39:20 -0700170#ifdef WEBRTC_ANDROID
171 InitializeAndroidObjects();
172#endif
173 pc_factory_ = CreatePeerConnectionFactory(
174 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Anders Carlsson67537952018-05-03 11:28:29 +0200175 rtc::scoped_refptr<AudioDeviceModule>(FakeAudioCaptureModule::Create()),
176 CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(),
177 CreateBuiltinVideoEncoderFactory(), CreateBuiltinVideoDecoderFactory(),
178 nullptr /* audio_mixer */, nullptr /* audio_processing */);
Steve Anton6f25b092017-10-23 09:39:20 -0700179 }
180
181 WrapperPtr CreatePeerConnection() {
182 return CreatePeerConnection(RTCConfiguration());
183 }
184
185 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
186 auto* fake_network = NewFakeNetwork();
187 auto port_allocator =
Karl Wiberg918f50c2018-07-05 11:40:33 +0200188 absl::make_unique<cricket::BasicPortAllocator>(fake_network);
Steve Anton6f25b092017-10-23 09:39:20 -0700189 port_allocator->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
190 cricket::PORTALLOCATOR_DISABLE_RELAY);
191 port_allocator->set_step_delay(cricket::kMinimumStepDelay);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200192 auto observer = absl::make_unique<MockPeerConnectionObserver>();
Steve Anton7464fca2018-01-19 11:10:37 -0800193 RTCConfiguration modified_config = config;
194 modified_config.sdp_semantics = sdp_semantics_;
Steve Anton6f25b092017-10-23 09:39:20 -0700195 auto pc = pc_factory_->CreatePeerConnection(
Steve Anton7464fca2018-01-19 11:10:37 -0800196 modified_config, std::move(port_allocator), nullptr, observer.get());
Steve Anton6f25b092017-10-23 09:39:20 -0700197 if (!pc) {
198 return nullptr;
199 }
200
Karl Wiberg918f50c2018-07-05 11:40:33 +0200201 auto wrapper = absl::make_unique<PeerConnectionWrapperForBundleTest>(
Steve Anton6f25b092017-10-23 09:39:20 -0700202 pc_factory_, pc, std::move(observer));
203 wrapper->set_network(fake_network);
204 return wrapper;
205 }
206
207 // Accepts the same arguments as CreatePeerConnection and adds default audio
208 // and video tracks.
209 template <typename... Args>
210 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
211 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
212 if (!wrapper) {
213 return nullptr;
214 }
215 wrapper->AddAudioTrack("a");
216 wrapper->AddVideoTrack("v");
217 return wrapper;
218 }
219
220 cricket::Candidate CreateLocalUdpCandidate(
221 const rtc::SocketAddress& address) {
222 cricket::Candidate candidate;
223 candidate.set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);
224 candidate.set_protocol(cricket::UDP_PROTOCOL_NAME);
225 candidate.set_address(address);
226 candidate.set_type(cricket::LOCAL_PORT_TYPE);
227 return candidate;
228 }
229
230 rtc::FakeNetworkManager* NewFakeNetwork() {
231 // The PeerConnection's port allocator is tied to the PeerConnection's
232 // lifetime and expects the underlying NetworkManager to outlive it. If
233 // PeerConnectionWrapper owned the NetworkManager, it would be destroyed
234 // before the PeerConnection (since subclass members are destroyed before
235 // base class members). Therefore, the test fixture will own all the fake
236 // networks even though tests should access the fake network through the
237 // PeerConnectionWrapper.
Harald Alvestrandad88c882018-11-28 16:47:46 +0100238 auto* fake_network = new FakeNetworkManagerWithNoAnyNetwork();
Steve Anton6f25b092017-10-23 09:39:20 -0700239 fake_networks_.emplace_back(fake_network);
240 return fake_network;
241 }
242
243 std::unique_ptr<rtc::VirtualSocketServer> vss_;
244 rtc::AutoSocketServerThread main_;
245 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
246 std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_networks_;
Steve Anton7464fca2018-01-19 11:10:37 -0800247 const SdpSemantics sdp_semantics_;
248};
249
250class PeerConnectionBundleTest
251 : public PeerConnectionBundleBaseTest,
252 public ::testing::WithParamInterface<SdpSemantics> {
253 protected:
254 PeerConnectionBundleTest() : PeerConnectionBundleBaseTest(GetParam()) {}
Steve Anton6f25b092017-10-23 09:39:20 -0700255};
256
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700257class PeerConnectionBundleTestUnifiedPlan
258 : public PeerConnectionBundleBaseTest {
259 protected:
260 PeerConnectionBundleTestUnifiedPlan()
261 : PeerConnectionBundleBaseTest(SdpSemantics::kUnifiedPlan) {}
262};
263
Steve Anton6f25b092017-10-23 09:39:20 -0700264SdpContentMutator RemoveRtcpMux() {
265 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800266 content->media_description()->set_rtcp_mux(false);
Steve Anton6f25b092017-10-23 09:39:20 -0700267 };
268}
269
270std::vector<int> GetCandidateComponents(
271 const std::vector<IceCandidateInterface*> candidates) {
272 std::vector<int> components;
Mirko Bonadei649a4c22019-01-29 10:11:53 +0100273 components.reserve(candidates.size());
Steve Anton6f25b092017-10-23 09:39:20 -0700274 for (auto* candidate : candidates) {
275 components.push_back(candidate->candidate().component());
276 }
277 return components;
278}
279
280// Test that there are 2 local UDP candidates (1 RTP and 1 RTCP candidate) for
281// each media section when disabling bundling and disabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800282TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700283 TwoCandidatesForEachTransportWhenNoBundleNoRtcpMux) {
284 const SocketAddress kCallerAddress("1.1.1.1", 0);
285 const SocketAddress kCalleeAddress("2.2.2.2", 0);
286
287 RTCConfiguration config;
288 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
289 auto caller = CreatePeerConnectionWithAudioVideo(config);
290 caller->network()->AddInterface(kCallerAddress);
291 auto callee = CreatePeerConnectionWithAudioVideo(config);
292 callee->network()->AddInterface(kCalleeAddress);
293
294 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
295 RTCOfferAnswerOptions options_no_bundle;
296 options_no_bundle.use_rtp_mux = false;
297 auto answer = callee->CreateAnswer(options_no_bundle);
298 SdpContentsForEach(RemoveRtcpMux(), answer->description());
299 ASSERT_TRUE(
300 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
301 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
302
303 // Check that caller has separate RTP and RTCP candidates for each media.
304 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
305 EXPECT_THAT(
306 GetCandidateComponents(caller->observer()->GetCandidatesByMline(0)),
307 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
308 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
309 EXPECT_THAT(
310 GetCandidateComponents(caller->observer()->GetCandidatesByMline(1)),
311 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
312 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
313
314 // Check that callee has separate RTP and RTCP candidates for each media.
315 EXPECT_TRUE_WAIT(callee->IsIceGatheringDone(), kDefaultTimeout);
316 EXPECT_THAT(
317 GetCandidateComponents(callee->observer()->GetCandidatesByMline(0)),
318 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
319 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
320 EXPECT_THAT(
321 GetCandidateComponents(callee->observer()->GetCandidatesByMline(1)),
322 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
323 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
324}
325
326// Test that there is 1 local UDP candidate for both RTP and RTCP for each media
327// section when disabling bundle but enabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800328TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700329 OneCandidateForEachTransportWhenNoBundleButRtcpMux) {
330 const SocketAddress kCallerAddress("1.1.1.1", 0);
331
332 auto caller = CreatePeerConnectionWithAudioVideo();
333 caller->network()->AddInterface(kCallerAddress);
334 auto callee = CreatePeerConnectionWithAudioVideo();
335
336 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
337 RTCOfferAnswerOptions options_no_bundle;
338 options_no_bundle.use_rtp_mux = false;
339 ASSERT_TRUE(
340 caller->SetRemoteDescription(callee->CreateAnswer(options_no_bundle)));
341
342 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
343
344 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
345 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(1).size());
346}
347
348// Test that there is 1 local UDP candidate in only the first media section when
349// bundling and enabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800350TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700351 OneCandidateOnlyOnFirstTransportWhenBundleAndRtcpMux) {
352 const SocketAddress kCallerAddress("1.1.1.1", 0);
353
354 RTCConfiguration config;
355 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
356 auto caller = CreatePeerConnectionWithAudioVideo(config);
357 caller->network()->AddInterface(kCallerAddress);
358 auto callee = CreatePeerConnectionWithAudioVideo(config);
359
360 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
361 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateAnswer()));
362
363 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
364
365 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
366 EXPECT_EQ(0u, caller->observer()->GetCandidatesByMline(1).size());
367}
368
Zhi Huange830e682018-03-30 10:48:35 -0700369// It will fail if the offerer uses the mux-BUNDLE policy but the answerer
370// doesn't support BUNDLE.
371TEST_P(PeerConnectionBundleTest, MaxBundleNotSupportedInAnswer) {
372 RTCConfiguration config;
373 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
374 auto caller = CreatePeerConnectionWithAudioVideo(config);
375 auto callee = CreatePeerConnectionWithAudioVideo();
376
377 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
378 bool equal_before =
379 (caller->voice_rtp_transport() == caller->video_rtp_transport());
380 EXPECT_EQ(true, equal_before);
381 RTCOfferAnswerOptions options;
382 options.use_rtp_mux = false;
383 EXPECT_FALSE(
384 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
385}
386
Steve Anton6f25b092017-10-23 09:39:20 -0700387// The following parameterized test verifies that an offer/answer with varying
388// bundle policies and either bundle in the answer or not will produce the
389// expected RTP transports for audio and video. In particular, for bundling we
390// care about whether they are separate transports or the same.
391
392enum class BundleIncluded { kBundleInAnswer, kBundleNotInAnswer };
393std::ostream& operator<<(std::ostream& out, BundleIncluded value) {
394 switch (value) {
395 case BundleIncluded::kBundleInAnswer:
396 return out << "bundle in answer";
397 case BundleIncluded::kBundleNotInAnswer:
398 return out << "bundle not in answer";
399 }
400 return out << "unknown";
401}
402
403class PeerConnectionBundleMatrixTest
Steve Anton7464fca2018-01-19 11:10:37 -0800404 : public PeerConnectionBundleBaseTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700405 public ::testing::WithParamInterface<
Steve Anton7464fca2018-01-19 11:10:37 -0800406 std::tuple<SdpSemantics,
407 std::tuple<BundlePolicy, BundleIncluded, bool, bool>>> {
Steve Anton6f25b092017-10-23 09:39:20 -0700408 protected:
Steve Anton7464fca2018-01-19 11:10:37 -0800409 PeerConnectionBundleMatrixTest()
410 : PeerConnectionBundleBaseTest(std::get<0>(GetParam())) {
411 auto param = std::get<1>(GetParam());
412 bundle_policy_ = std::get<0>(param);
413 bundle_included_ = std::get<1>(param);
414 expected_same_before_ = std::get<2>(param);
415 expected_same_after_ = std::get<3>(param);
Steve Anton6f25b092017-10-23 09:39:20 -0700416 }
417
418 PeerConnectionInterface::BundlePolicy bundle_policy_;
419 BundleIncluded bundle_included_;
420 bool expected_same_before_;
421 bool expected_same_after_;
422};
423
424TEST_P(PeerConnectionBundleMatrixTest,
425 VerifyTransportsBeforeAndAfterSettingRemoteAnswer) {
426 RTCConfiguration config;
427 config.bundle_policy = bundle_policy_;
428 auto caller = CreatePeerConnectionWithAudioVideo(config);
429 auto callee = CreatePeerConnectionWithAudioVideo();
430
431 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
Zhi Huange830e682018-03-30 10:48:35 -0700432 bool equal_before =
433 (caller->voice_rtp_transport() == caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700434 EXPECT_EQ(expected_same_before_, equal_before);
435
436 RTCOfferAnswerOptions options;
437 options.use_rtp_mux = (bundle_included_ == BundleIncluded::kBundleInAnswer);
438 ASSERT_TRUE(
439 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
Zhi Huange830e682018-03-30 10:48:35 -0700440 bool equal_after =
441 (caller->voice_rtp_transport() == caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700442 EXPECT_EQ(expected_same_after_, equal_after);
443}
444
445// The max-bundle policy means we should anticipate bundling being negotiated,
446// and multiplex audio/video from the start.
447// For all other policies, bundling should only be enabled if negotiated by the
448// answer.
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100449INSTANTIATE_TEST_SUITE_P(
Steve Anton6f25b092017-10-23 09:39:20 -0700450 PeerConnectionBundleTest,
451 PeerConnectionBundleMatrixTest,
Steve Anton7464fca2018-01-19 11:10:37 -0800452 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
453 Values(std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
454 BundleIncluded::kBundleInAnswer,
455 false,
456 true),
457 std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
458 BundleIncluded::kBundleNotInAnswer,
459 false,
460 false),
461 std::make_tuple(BundlePolicy::kBundlePolicyMaxBundle,
462 BundleIncluded::kBundleInAnswer,
463 true,
464 true),
Steve Anton7464fca2018-01-19 11:10:37 -0800465 std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
466 BundleIncluded::kBundleInAnswer,
467 false,
468 true),
469 std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
470 BundleIncluded::kBundleNotInAnswer,
471 false,
472 false))));
Steve Anton6f25b092017-10-23 09:39:20 -0700473
474// Test that the audio/video transports on the callee side are the same before
475// and after setting a local answer when max BUNDLE is enabled and an offer with
476// BUNDLE is received.
Steve Anton7464fca2018-01-19 11:10:37 -0800477TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700478 TransportsSameForMaxBundleWithBundleInRemoteOffer) {
479 auto caller = CreatePeerConnectionWithAudioVideo();
480 RTCConfiguration config;
481 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
482 auto callee = CreatePeerConnectionWithAudioVideo(config);
483
484 RTCOfferAnswerOptions options_with_bundle;
485 options_with_bundle.use_rtp_mux = true;
486 ASSERT_TRUE(callee->SetRemoteDescription(
487 caller->CreateOfferAndSetAsLocal(options_with_bundle)));
488
Zhi Huange830e682018-03-30 10:48:35 -0700489 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700490
491 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
492
Zhi Huange830e682018-03-30 10:48:35 -0700493 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700494}
495
Steve Anton7464fca2018-01-19 11:10:37 -0800496TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700497 FailToSetRemoteOfferWithNoBundleWhenBundlePolicyMaxBundle) {
498 auto caller = CreatePeerConnectionWithAudioVideo();
499 RTCConfiguration config;
500 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
501 auto callee = CreatePeerConnectionWithAudioVideo(config);
502
503 RTCOfferAnswerOptions options_no_bundle;
504 options_no_bundle.use_rtp_mux = false;
505 EXPECT_FALSE(callee->SetRemoteDescription(
506 caller->CreateOfferAndSetAsLocal(options_no_bundle)));
507}
508
509// Test that if the media section which has the bundled transport is rejected,
510// then the peers still connect and the bundled transport switches to the other
511// media section.
512// Note: This is currently failing because of the following bug:
513// https://bugs.chromium.org/p/webrtc/issues/detail?id=6280
Steve Anton7464fca2018-01-19 11:10:37 -0800514TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700515 DISABLED_SuccessfullyNegotiateMaxBundleIfBundleTransportMediaRejected) {
516 RTCConfiguration config;
517 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
518 auto caller = CreatePeerConnectionWithAudioVideo(config);
519 auto callee = CreatePeerConnection();
520 callee->AddVideoTrack("v");
521
522 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
523
524 RTCOfferAnswerOptions options;
525 options.offer_to_receive_audio = 0;
526 ASSERT_TRUE(
527 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
528
Zhi Huange830e682018-03-30 10:48:35 -0700529 EXPECT_FALSE(caller->voice_rtp_transport());
530 EXPECT_TRUE(caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700531}
532
533// When requiring RTCP multiplexing, the PeerConnection never makes RTCP
534// transport channels.
Steve Anton7464fca2018-01-19 11:10:37 -0800535TEST_P(PeerConnectionBundleTest, NeverCreateRtcpTransportWithRtcpMuxRequired) {
Steve Anton6f25b092017-10-23 09:39:20 -0700536 RTCConfiguration config;
537 config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyRequire;
538 auto caller = CreatePeerConnectionWithAudioVideo(config);
539 auto callee = CreatePeerConnectionWithAudioVideo();
540
541 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
542
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700543 EXPECT_FALSE(caller->voice_rtp_transport()->rtcp_mux_enabled());
544 EXPECT_FALSE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700545
546 ASSERT_TRUE(
547 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
548
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700549 EXPECT_TRUE(caller->voice_rtp_transport()->rtcp_mux_enabled());
550 EXPECT_TRUE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700551}
552
Zhi Huange830e682018-03-30 10:48:35 -0700553// When negotiating RTCP multiplexing, the PeerConnection makes RTCP transports
554// when the offer is sent, but will destroy them once the remote answer is set.
Steve Anton7464fca2018-01-19 11:10:37 -0800555TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700556 CreateRtcpTransportOnlyBeforeAnswerWithRtcpMuxNegotiate) {
557 RTCConfiguration config;
558 config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyNegotiate;
559 auto caller = CreatePeerConnectionWithAudioVideo(config);
560 auto callee = CreatePeerConnectionWithAudioVideo();
561
562 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
563
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700564 EXPECT_FALSE(caller->voice_rtp_transport()->rtcp_mux_enabled());
565 EXPECT_FALSE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700566
567 ASSERT_TRUE(
568 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
569
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700570 EXPECT_TRUE(caller->voice_rtp_transport()->rtcp_mux_enabled());
571 EXPECT_TRUE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700572}
573
Steve Anton7464fca2018-01-19 11:10:37 -0800574TEST_P(PeerConnectionBundleTest, FailToSetDescriptionWithBundleAndNoRtcpMux) {
Steve Anton6f25b092017-10-23 09:39:20 -0700575 auto caller = CreatePeerConnectionWithAudioVideo();
576 auto callee = CreatePeerConnectionWithAudioVideo();
577
578 RTCOfferAnswerOptions options;
579 options.use_rtp_mux = true;
580
581 auto offer = caller->CreateOffer(options);
582 SdpContentsForEach(RemoveRtcpMux(), offer->description());
583
584 std::string error;
585 EXPECT_FALSE(caller->SetLocalDescription(CloneSessionDescription(offer.get()),
586 &error));
587 EXPECT_EQ(
588 "Failed to set local offer sdp: rtcp-mux must be enabled when BUNDLE is "
589 "enabled.",
590 error);
591
592 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer), &error));
593 EXPECT_EQ(
594 "Failed to set remote offer sdp: rtcp-mux must be enabled when BUNDLE is "
595 "enabled.",
596 error);
597}
598
599// Test that candidates sent to the "video" transport do not get pushed down to
600// the "audio" transport channel when bundling.
Steve Anton7464fca2018-01-19 11:10:37 -0800601TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700602 IgnoreCandidatesForUnusedTransportWhenBundling) {
603 const SocketAddress kAudioAddress1("1.1.1.1", 1111);
604 const SocketAddress kAudioAddress2("2.2.2.2", 2222);
605 const SocketAddress kVideoAddress("3.3.3.3", 3333);
606 const SocketAddress kCallerAddress("4.4.4.4", 0);
607 const SocketAddress kCalleeAddress("5.5.5.5", 0);
608
609 auto caller = CreatePeerConnectionWithAudioVideo();
610 auto callee = CreatePeerConnectionWithAudioVideo();
611
612 caller->network()->AddInterface(kCallerAddress);
613 callee->network()->AddInterface(kCalleeAddress);
614
615 RTCOfferAnswerOptions options;
616 options.use_rtp_mux = true;
617
618 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
619 ASSERT_TRUE(
Zhi Huange830e682018-03-30 10:48:35 -0700620 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
Steve Anton6f25b092017-10-23 09:39:20 -0700621
622 // The way the *_WAIT checks work is they only wait if the condition fails,
623 // which does not help in the case where state is not changing. This is
624 // problematic in this test since we want to verify that adding a video
625 // candidate does _not_ change state. So we interleave candidates and assume
626 // that messages are executed in the order they were posted.
627
628 cricket::Candidate audio_candidate1 = CreateLocalUdpCandidate(kAudioAddress1);
629 ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate1,
630 cricket::MEDIA_TYPE_AUDIO));
631
632 cricket::Candidate video_candidate = CreateLocalUdpCandidate(kVideoAddress);
633 ASSERT_TRUE(caller->AddIceCandidateToMedia(&video_candidate,
634 cricket::MEDIA_TYPE_VIDEO));
635
636 cricket::Candidate audio_candidate2 = CreateLocalUdpCandidate(kAudioAddress2);
637 ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate2,
638 cricket::MEDIA_TYPE_AUDIO));
639
640 EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress1),
641 kDefaultTimeout);
642 EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress2),
643 kDefaultTimeout);
644 EXPECT_FALSE(caller->HasConnectionWithRemoteAddress(kVideoAddress));
645}
646
647// Test that the transport used by both audio and video is the transport
648// associated with the first MID in the answer BUNDLE group, even if it's in a
649// different order from the offer.
Steve Anton7464fca2018-01-19 11:10:37 -0800650TEST_P(PeerConnectionBundleTest, BundleOnFirstMidInAnswer) {
Steve Anton6f25b092017-10-23 09:39:20 -0700651 auto caller = CreatePeerConnectionWithAudioVideo();
652 auto callee = CreatePeerConnectionWithAudioVideo();
653
654 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
655
Zhi Huange830e682018-03-30 10:48:35 -0700656 auto* old_video_transport = caller->video_rtp_transport();
Steve Anton6f25b092017-10-23 09:39:20 -0700657
658 auto answer = callee->CreateAnswer();
659 auto* old_bundle_group =
660 answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
Steve Anton7464fca2018-01-19 11:10:37 -0800661 std::string first_mid = old_bundle_group->content_names()[0];
662 std::string second_mid = old_bundle_group->content_names()[1];
Steve Anton6f25b092017-10-23 09:39:20 -0700663 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
664
665 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
Steve Anton7464fca2018-01-19 11:10:37 -0800666 new_bundle_group.AddContentName(second_mid);
667 new_bundle_group.AddContentName(first_mid);
Steve Anton6f25b092017-10-23 09:39:20 -0700668 answer->description()->AddGroup(new_bundle_group);
669
670 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
671
Zhi Huange830e682018-03-30 10:48:35 -0700672 EXPECT_EQ(old_video_transport, caller->video_rtp_transport());
673 EXPECT_EQ(caller->voice_rtp_transport(), caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700674}
675
Zhi Huang365381f2018-04-13 16:44:34 -0700676// This tests that applying description with conflicted RTP demuxing criteria
677// will fail.
678TEST_P(PeerConnectionBundleTest,
679 ApplyDescriptionWithConflictedDemuxCriteriaFail) {
680 auto caller = CreatePeerConnectionWithAudioVideo();
681 auto callee = CreatePeerConnectionWithAudioVideo();
682
683 RTCOfferAnswerOptions options;
684 options.use_rtp_mux = false;
685 auto offer = caller->CreateOffer(options);
686 // Modified the SDP to make two m= sections have the same SSRC.
687 ASSERT_GE(offer->description()->contents().size(), 2U);
688 offer->description()
689 ->contents()[0]
Harald Alvestrand1716d392019-06-03 20:35:45 +0200690 .media_description()
691 ->mutable_streams()[0]
Zhi Huang365381f2018-04-13 16:44:34 -0700692 .ssrcs[0] = 1111222;
693 offer->description()
694 ->contents()[1]
Harald Alvestrand1716d392019-06-03 20:35:45 +0200695 .media_description()
696 ->mutable_streams()[0]
Zhi Huang365381f2018-04-13 16:44:34 -0700697 .ssrcs[0] = 1111222;
698 EXPECT_TRUE(
699 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
700 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
701 EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal(options));
702
703 // Enable BUNDLE in subsequent offer/answer exchange and two m= sections are
704 // expectd to use one RtpTransport underneath.
705 options.use_rtp_mux = true;
706 EXPECT_TRUE(
707 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal(options)));
708 auto answer = callee->CreateAnswer(options);
709 // When BUNDLE is enabled, applying the description is expected to fail
710 // because the demuxing criteria is conflicted.
711 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
712}
713
Zhi Huangd2248f82018-04-10 14:41:03 -0700714// This tests that changing the pre-negotiated BUNDLE tag is not supported.
715TEST_P(PeerConnectionBundleTest, RejectDescriptionChangingBundleTag) {
716 RTCConfiguration config;
717 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
718 auto caller = CreatePeerConnectionWithAudioVideo(config);
719 auto callee = CreatePeerConnectionWithAudioVideo(config);
720
721 RTCOfferAnswerOptions options;
722 options.use_rtp_mux = true;
723 auto offer = caller->CreateOfferAndSetAsLocal(options);
724
725 // Create a new bundle-group with different bundled_mid.
726 auto* old_bundle_group =
727 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
728 std::string first_mid = old_bundle_group->content_names()[0];
729 std::string second_mid = old_bundle_group->content_names()[1];
730 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
731 new_bundle_group.AddContentName(second_mid);
732
733 auto re_offer = CloneSessionDescription(offer.get());
734 callee->SetRemoteDescription(std::move(offer));
735 auto answer = callee->CreateAnswer(options);
736 // Reject the first MID.
737 answer->description()->contents()[0].rejected = true;
738 // Remove the first MID from the bundle group.
739 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
740 answer->description()->AddGroup(new_bundle_group);
741 // The answer is expected to be rejected.
742 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
743
744 // Do the same thing for re-offer.
745 re_offer->description()->contents()[0].rejected = true;
746 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
747 re_offer->description()->AddGroup(new_bundle_group);
748 // The re-offer is expected to be rejected.
749 EXPECT_FALSE(caller->SetLocalDescription(std::move(re_offer)));
750}
751
752// This tests that removing contents from BUNDLE group and reject the whole
753// BUNDLE group could work. This is a regression test for
754// (https://bugs.chromium.org/p/chromium/issues/detail?id=827917)
755TEST_P(PeerConnectionBundleTest, RemovingContentAndRejectBundleGroup) {
756 RTCConfiguration config;
757#ifndef HAVE_SCTP
758 config.enable_rtp_data_channel = true;
759#endif
760 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
761 auto caller = CreatePeerConnectionWithAudioVideo(config);
762 caller->CreateDataChannel("dc");
763
764 auto offer = caller->CreateOfferAndSetAsLocal();
765 auto re_offer = CloneSessionDescription(offer.get());
766
767 // Removing the second MID from the BUNDLE group.
768 auto* old_bundle_group =
769 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
770 std::string first_mid = old_bundle_group->content_names()[0];
771 std::string third_mid = old_bundle_group->content_names()[2];
772 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
773 new_bundle_group.AddContentName(first_mid);
774 new_bundle_group.AddContentName(third_mid);
775
776 // Reject the entire new bundle group.
777 re_offer->description()->contents()[0].rejected = true;
778 re_offer->description()->contents()[2].rejected = true;
779 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
780 re_offer->description()->AddGroup(new_bundle_group);
781
782 EXPECT_TRUE(caller->SetLocalDescription(std::move(re_offer)));
783}
784
785// This tests that the BUNDLE group in answer should be a subset of the offered
786// group.
787TEST_P(PeerConnectionBundleTest, AddContentToBundleGroupInAnswerNotSupported) {
788 auto caller = CreatePeerConnectionWithAudioVideo();
789 auto callee = CreatePeerConnectionWithAudioVideo();
790
791 auto offer = caller->CreateOffer();
792 std::string first_mid = offer->description()->contents()[0].name;
793 std::string second_mid = offer->description()->contents()[1].name;
794
795 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
796 bundle_group.AddContentName(first_mid);
797 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
798 offer->description()->AddGroup(bundle_group);
799 EXPECT_TRUE(
800 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
801 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
802
803 auto answer = callee->CreateAnswer();
804 bundle_group.AddContentName(second_mid);
805 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
806 answer->description()->AddGroup(bundle_group);
807
808 // The answer is expected to be rejected because second mid is not in the
809 // offered BUNDLE group.
810 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
811}
812
813// This tests that the BUNDLE group with non-existing MID should be rejectd.
814TEST_P(PeerConnectionBundleTest, RejectBundleGroupWithNonExistingMid) {
815 auto caller = CreatePeerConnectionWithAudioVideo();
816 auto callee = CreatePeerConnectionWithAudioVideo();
817
818 auto offer = caller->CreateOffer();
819 auto invalid_bundle_group =
820 *offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
821 invalid_bundle_group.AddContentName("non-existing-MID");
822 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
823 offer->description()->AddGroup(invalid_bundle_group);
824
825 EXPECT_FALSE(
826 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
827 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
828}
829
830// This tests that an answer shouldn't be able to remove an m= section from an
831// established group without rejecting it.
832TEST_P(PeerConnectionBundleTest, RemoveContentFromBundleGroup) {
833 auto caller = CreatePeerConnectionWithAudioVideo();
834 auto callee = CreatePeerConnectionWithAudioVideo();
835
836 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
837 EXPECT_TRUE(
838 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
839
840 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
841 auto answer = callee->CreateAnswer();
842 std::string second_mid = answer->description()->contents()[1].name;
843
844 auto invalid_bundle_group =
845 *answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
846 invalid_bundle_group.RemoveContentName(second_mid);
847 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
848 answer->description()->AddGroup(invalid_bundle_group);
849
850 EXPECT_FALSE(
851 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
852}
853
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100854INSTANTIATE_TEST_SUITE_P(PeerConnectionBundleTest,
855 PeerConnectionBundleTest,
856 Values(SdpSemantics::kPlanB,
857 SdpSemantics::kUnifiedPlan));
Steve Anton7464fca2018-01-19 11:10:37 -0800858
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700859// According to RFC5888, if an endpoint understands the semantics of an
860// "a=group", it MUST return an answer with that group. So, an empty BUNDLE
861// group is valid when the answerer rejects all m= sections (by stopping all
862// transceivers), meaning there's nothing to bundle.
863//
864// Only writing this test for Unified Plan mode, since there's no way to reject
865// m= sections in answers for Plan B without SDP munging.
866TEST_F(PeerConnectionBundleTestUnifiedPlan,
867 EmptyBundleGroupCreatedInAnswerWhenAppropriate) {
868 auto caller = CreatePeerConnectionWithAudioVideo();
869 auto callee = CreatePeerConnection();
870
871 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
872
873 // Stop all transceivers, causing all m= sections to be rejected.
874 for (const auto& transceiver : callee->pc()->GetTransceivers()) {
875 transceiver->Stop();
876 }
877 EXPECT_TRUE(
878 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
879
880 // Verify that the answer actually contained an empty bundle group.
881 const SessionDescriptionInterface* desc = callee->pc()->local_description();
882 ASSERT_NE(nullptr, desc);
883 const cricket::ContentGroup* bundle_group =
884 desc->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
885 ASSERT_NE(nullptr, bundle_group);
886 EXPECT_TRUE(bundle_group->content_names().empty());
887}
888
Steve Anton6f25b092017-10-23 09:39:20 -0700889} // namespace webrtc