blob: 543c9be81a8ec0eeda5b6a30fbf0b387508baffc [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
Mirko Bonadei317a1f02019-09-17 17:06:18 +020011#include <memory>
12
Karl Wiberg32df86e2017-11-03 10:24:27 +010013#include "api/audio_codecs/builtin_audio_decoder_factory.h"
14#include "api/audio_codecs/builtin_audio_encoder_factory.h"
Mirko Bonadei2ff3f492018-11-22 09:00:13 +010015#include "api/create_peerconnection_factory.h"
Steve Anton10542f22019-01-11 09:11:00 -080016#include "api/peer_connection_proxy.h"
Anders Carlsson67537952018-05-03 11:28:29 +020017#include "api/video_codecs/builtin_video_decoder_factory.h"
18#include "api/video_codecs/builtin_video_encoder_factory.h"
Steve Anton10542f22019-01-11 09:11:00 -080019#include "p2p/base/fake_port_allocator.h"
20#include "p2p/base/test_stun_server.h"
21#include "p2p/client/basic_port_allocator.h"
22#include "pc/media_session.h"
23#include "pc/peer_connection.h"
24#include "pc/peer_connection_wrapper.h"
25#include "pc/sdp_utils.h"
Steve Anton6f25b092017-10-23 09:39:20 -070026#ifdef WEBRTC_ANDROID
Steve Anton10542f22019-01-11 09:11:00 -080027#include "pc/test/android_test_initializer.h"
Steve Anton6f25b092017-10-23 09:39:20 -070028#endif
Steve Anton10542f22019-01-11 09:11:00 -080029#include "pc/test/fake_audio_capture_module.h"
30#include "rtc_base/fake_network.h"
Steve Anton6f25b092017-10-23 09:39:20 -070031#include "rtc_base/gunit.h"
Steve Anton10542f22019-01-11 09:11:00 -080032#include "rtc_base/virtual_socket_server.h"
Steve Anton6f25b092017-10-23 09:39:20 -070033#include "test/gmock.h"
34
35namespace webrtc {
36
37using BundlePolicy = PeerConnectionInterface::BundlePolicy;
38using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
39using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
40using RtcpMuxPolicy = PeerConnectionInterface::RtcpMuxPolicy;
41using rtc::SocketAddress;
Steve Anton7464fca2018-01-19 11:10:37 -080042using ::testing::Combine;
Steve Anton6f25b092017-10-23 09:39:20 -070043using ::testing::ElementsAre;
44using ::testing::UnorderedElementsAre;
45using ::testing::Values;
46
47constexpr int kDefaultTimeout = 10000;
48
49// TODO(steveanton): These tests should be rewritten to use the standard
50// RtpSenderInterface/DtlsTransportInterface objects once they're available in
51// the API. The RtpSender can be used to determine which transport a given media
52// will use: https://www.w3.org/TR/webrtc/#dom-rtcrtpsender-transport
Steve Anton7464fca2018-01-19 11:10:37 -080053// Should also be able to remove GetTransceiversForTesting at that point.
Steve Anton6f25b092017-10-23 09:39:20 -070054
Harald Alvestrandad88c882018-11-28 16:47:46 +010055class FakeNetworkManagerWithNoAnyNetwork : public rtc::FakeNetworkManager {
56 public:
57 void GetAnyAddressNetworks(NetworkList* networks) override {
58 // This function allocates networks that are owned by the
59 // NetworkManager. But some tests assume that they can release
60 // all networks independent of the network manager.
61 // In order to prevent use-after-free issues, don't allow this
62 // function to have any effect when run in tests.
63 RTC_LOG(LS_INFO) << "FakeNetworkManager::GetAnyAddressNetworks ignored";
64 }
65};
66
Steve Anton6f25b092017-10-23 09:39:20 -070067class PeerConnectionWrapperForBundleTest : public PeerConnectionWrapper {
68 public:
69 using PeerConnectionWrapper::PeerConnectionWrapper;
70
71 bool AddIceCandidateToMedia(cricket::Candidate* candidate,
72 cricket::MediaType media_type) {
73 auto* desc = pc()->remote_description()->description();
74 for (size_t i = 0; i < desc->contents().size(); i++) {
75 const auto& content = desc->contents()[i];
Steve Antonb1c1de12017-12-21 15:14:30 -080076 if (content.media_description()->type() == media_type) {
Steve Anton6f25b092017-10-23 09:39:20 -070077 candidate->set_transport_name(content.name);
Steve Anton27ab0e52018-07-23 15:11:53 -070078 std::unique_ptr<IceCandidateInterface> jsep_candidate =
79 CreateIceCandidate(content.name, i, *candidate);
80 return pc()->AddIceCandidate(jsep_candidate.get());
Steve Anton6f25b092017-10-23 09:39:20 -070081 }
82 }
83 RTC_NOTREACHED();
84 return false;
85 }
86
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -070087 RtpTransportInternal* voice_rtp_transport() {
88 return (voice_channel() ? voice_channel()->rtp_transport() : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -070089 }
90
91 cricket::VoiceChannel* voice_channel() {
Steve Antonb8867112018-02-13 10:07:54 -080092 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
Mirko Bonadei739baf02019-01-27 17:29:42 +010093 for (const auto& transceiver : transceivers) {
Steve Anton69470252018-02-09 11:43:08 -080094 if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
Steve Anton7464fca2018-01-19 11:10:37 -080095 return static_cast<cricket::VoiceChannel*>(
96 transceiver->internal()->channel());
97 }
98 }
99 return nullptr;
Steve Anton6f25b092017-10-23 09:39:20 -0700100 }
101
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700102 RtpTransportInternal* video_rtp_transport() {
103 return (video_channel() ? video_channel()->rtp_transport() : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -0700104 }
105
106 cricket::VideoChannel* video_channel() {
Steve Antonb8867112018-02-13 10:07:54 -0800107 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
Mirko Bonadei739baf02019-01-27 17:29:42 +0100108 for (const auto& transceiver : transceivers) {
Steve Anton69470252018-02-09 11:43:08 -0800109 if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) {
Steve Anton7464fca2018-01-19 11:10:37 -0800110 return static_cast<cricket::VideoChannel*>(
111 transceiver->internal()->channel());
112 }
113 }
114 return nullptr;
Steve Anton6f25b092017-10-23 09:39:20 -0700115 }
116
117 PeerConnection* GetInternalPeerConnection() {
Mirko Bonadeie97de912017-12-13 11:29:34 +0100118 auto* pci =
119 static_cast<PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(
120 pc());
121 return static_cast<PeerConnection*>(pci->internal());
Steve Anton6f25b092017-10-23 09:39:20 -0700122 }
123
124 // Returns true if the stats indicate that an ICE connection is either in
125 // progress or established with the given remote address.
126 bool HasConnectionWithRemoteAddress(const SocketAddress& address) {
127 auto report = GetStats();
128 if (!report) {
129 return false;
130 }
131 std::string matching_candidate_id;
132 for (auto* ice_candidate_stats :
133 report->GetStatsOfType<RTCRemoteIceCandidateStats>()) {
134 if (*ice_candidate_stats->ip == address.HostAsURIString() &&
135 *ice_candidate_stats->port == address.port()) {
136 matching_candidate_id = ice_candidate_stats->id();
137 break;
138 }
139 }
140 if (matching_candidate_id.empty()) {
141 return false;
142 }
143 for (auto* pair_stats :
144 report->GetStatsOfType<RTCIceCandidatePairStats>()) {
145 if (*pair_stats->remote_candidate_id == matching_candidate_id) {
146 if (*pair_stats->state == RTCStatsIceCandidatePairState::kInProgress ||
147 *pair_stats->state == RTCStatsIceCandidatePairState::kSucceeded) {
148 return true;
149 }
150 }
151 }
152 return false;
153 }
154
155 rtc::FakeNetworkManager* network() { return network_; }
156
157 void set_network(rtc::FakeNetworkManager* network) { network_ = network; }
158
159 private:
160 rtc::FakeNetworkManager* network_;
161};
162
Steve Anton7464fca2018-01-19 11:10:37 -0800163class PeerConnectionBundleBaseTest : public ::testing::Test {
Steve Anton6f25b092017-10-23 09:39:20 -0700164 protected:
165 typedef std::unique_ptr<PeerConnectionWrapperForBundleTest> WrapperPtr;
166
Steve Anton7464fca2018-01-19 11:10:37 -0800167 explicit PeerConnectionBundleBaseTest(SdpSemantics sdp_semantics)
168 : vss_(new rtc::VirtualSocketServer()),
169 main_(vss_.get()),
170 sdp_semantics_(sdp_semantics) {
Steve Anton6f25b092017-10-23 09:39:20 -0700171#ifdef WEBRTC_ANDROID
172 InitializeAndroidObjects();
173#endif
174 pc_factory_ = CreatePeerConnectionFactory(
175 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Anders Carlsson67537952018-05-03 11:28:29 +0200176 rtc::scoped_refptr<AudioDeviceModule>(FakeAudioCaptureModule::Create()),
177 CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(),
178 CreateBuiltinVideoEncoderFactory(), CreateBuiltinVideoDecoderFactory(),
179 nullptr /* audio_mixer */, nullptr /* audio_processing */);
Steve Anton6f25b092017-10-23 09:39:20 -0700180 }
181
182 WrapperPtr CreatePeerConnection() {
183 return CreatePeerConnection(RTCConfiguration());
184 }
185
186 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
187 auto* fake_network = NewFakeNetwork();
188 auto port_allocator =
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200189 std::make_unique<cricket::BasicPortAllocator>(fake_network);
Steve Anton6f25b092017-10-23 09:39:20 -0700190 port_allocator->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
191 cricket::PORTALLOCATOR_DISABLE_RELAY);
192 port_allocator->set_step_delay(cricket::kMinimumStepDelay);
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200193 auto observer = std::make_unique<MockPeerConnectionObserver>();
Steve Anton7464fca2018-01-19 11:10:37 -0800194 RTCConfiguration modified_config = config;
195 modified_config.sdp_semantics = sdp_semantics_;
Steve Anton6f25b092017-10-23 09:39:20 -0700196 auto pc = pc_factory_->CreatePeerConnection(
Steve Anton7464fca2018-01-19 11:10:37 -0800197 modified_config, std::move(port_allocator), nullptr, observer.get());
Steve Anton6f25b092017-10-23 09:39:20 -0700198 if (!pc) {
199 return nullptr;
200 }
201
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200202 auto wrapper = std::make_unique<PeerConnectionWrapperForBundleTest>(
Steve Anton6f25b092017-10-23 09:39:20 -0700203 pc_factory_, pc, std::move(observer));
204 wrapper->set_network(fake_network);
205 return wrapper;
206 }
207
208 // Accepts the same arguments as CreatePeerConnection and adds default audio
209 // and video tracks.
210 template <typename... Args>
211 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
212 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
213 if (!wrapper) {
214 return nullptr;
215 }
216 wrapper->AddAudioTrack("a");
217 wrapper->AddVideoTrack("v");
218 return wrapper;
219 }
220
221 cricket::Candidate CreateLocalUdpCandidate(
222 const rtc::SocketAddress& address) {
223 cricket::Candidate candidate;
224 candidate.set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);
225 candidate.set_protocol(cricket::UDP_PROTOCOL_NAME);
226 candidate.set_address(address);
227 candidate.set_type(cricket::LOCAL_PORT_TYPE);
228 return candidate;
229 }
230
231 rtc::FakeNetworkManager* NewFakeNetwork() {
232 // The PeerConnection's port allocator is tied to the PeerConnection's
233 // lifetime and expects the underlying NetworkManager to outlive it. If
234 // PeerConnectionWrapper owned the NetworkManager, it would be destroyed
235 // before the PeerConnection (since subclass members are destroyed before
236 // base class members). Therefore, the test fixture will own all the fake
237 // networks even though tests should access the fake network through the
238 // PeerConnectionWrapper.
Harald Alvestrandad88c882018-11-28 16:47:46 +0100239 auto* fake_network = new FakeNetworkManagerWithNoAnyNetwork();
Steve Anton6f25b092017-10-23 09:39:20 -0700240 fake_networks_.emplace_back(fake_network);
241 return fake_network;
242 }
243
244 std::unique_ptr<rtc::VirtualSocketServer> vss_;
245 rtc::AutoSocketServerThread main_;
246 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
247 std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_networks_;
Steve Anton7464fca2018-01-19 11:10:37 -0800248 const SdpSemantics sdp_semantics_;
249};
250
251class PeerConnectionBundleTest
252 : public PeerConnectionBundleBaseTest,
253 public ::testing::WithParamInterface<SdpSemantics> {
254 protected:
255 PeerConnectionBundleTest() : PeerConnectionBundleBaseTest(GetParam()) {}
Steve Anton6f25b092017-10-23 09:39:20 -0700256};
257
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700258class PeerConnectionBundleTestUnifiedPlan
259 : public PeerConnectionBundleBaseTest {
260 protected:
261 PeerConnectionBundleTestUnifiedPlan()
262 : PeerConnectionBundleBaseTest(SdpSemantics::kUnifiedPlan) {}
263};
264
Steve Anton6f25b092017-10-23 09:39:20 -0700265SdpContentMutator RemoveRtcpMux() {
266 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800267 content->media_description()->set_rtcp_mux(false);
Steve Anton6f25b092017-10-23 09:39:20 -0700268 };
269}
270
271std::vector<int> GetCandidateComponents(
272 const std::vector<IceCandidateInterface*> candidates) {
273 std::vector<int> components;
Mirko Bonadei649a4c22019-01-29 10:11:53 +0100274 components.reserve(candidates.size());
Steve Anton6f25b092017-10-23 09:39:20 -0700275 for (auto* candidate : candidates) {
276 components.push_back(candidate->candidate().component());
277 }
278 return components;
279}
280
281// Test that there are 2 local UDP candidates (1 RTP and 1 RTCP candidate) for
282// each media section when disabling bundling and disabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800283TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700284 TwoCandidatesForEachTransportWhenNoBundleNoRtcpMux) {
285 const SocketAddress kCallerAddress("1.1.1.1", 0);
286 const SocketAddress kCalleeAddress("2.2.2.2", 0);
287
288 RTCConfiguration config;
289 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
290 auto caller = CreatePeerConnectionWithAudioVideo(config);
291 caller->network()->AddInterface(kCallerAddress);
292 auto callee = CreatePeerConnectionWithAudioVideo(config);
293 callee->network()->AddInterface(kCalleeAddress);
294
295 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
296 RTCOfferAnswerOptions options_no_bundle;
297 options_no_bundle.use_rtp_mux = false;
298 auto answer = callee->CreateAnswer(options_no_bundle);
299 SdpContentsForEach(RemoveRtcpMux(), answer->description());
300 ASSERT_TRUE(
301 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
302 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
303
304 // Check that caller has separate RTP and RTCP candidates for each media.
305 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
306 EXPECT_THAT(
307 GetCandidateComponents(caller->observer()->GetCandidatesByMline(0)),
308 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
309 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
310 EXPECT_THAT(
311 GetCandidateComponents(caller->observer()->GetCandidatesByMline(1)),
312 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
313 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
314
315 // Check that callee has separate RTP and RTCP candidates for each media.
316 EXPECT_TRUE_WAIT(callee->IsIceGatheringDone(), kDefaultTimeout);
317 EXPECT_THAT(
318 GetCandidateComponents(callee->observer()->GetCandidatesByMline(0)),
319 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
320 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
321 EXPECT_THAT(
322 GetCandidateComponents(callee->observer()->GetCandidatesByMline(1)),
323 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
324 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
325}
326
327// Test that there is 1 local UDP candidate for both RTP and RTCP for each media
328// section when disabling bundle but enabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800329TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700330 OneCandidateForEachTransportWhenNoBundleButRtcpMux) {
331 const SocketAddress kCallerAddress("1.1.1.1", 0);
332
333 auto caller = CreatePeerConnectionWithAudioVideo();
334 caller->network()->AddInterface(kCallerAddress);
335 auto callee = CreatePeerConnectionWithAudioVideo();
336
337 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
338 RTCOfferAnswerOptions options_no_bundle;
339 options_no_bundle.use_rtp_mux = false;
340 ASSERT_TRUE(
341 caller->SetRemoteDescription(callee->CreateAnswer(options_no_bundle)));
342
343 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
344
345 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
346 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(1).size());
347}
348
349// Test that there is 1 local UDP candidate in only the first media section when
350// bundling and enabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800351TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700352 OneCandidateOnlyOnFirstTransportWhenBundleAndRtcpMux) {
353 const SocketAddress kCallerAddress("1.1.1.1", 0);
354
355 RTCConfiguration config;
356 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
357 auto caller = CreatePeerConnectionWithAudioVideo(config);
358 caller->network()->AddInterface(kCallerAddress);
359 auto callee = CreatePeerConnectionWithAudioVideo(config);
360
361 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
362 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateAnswer()));
363
364 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
365
366 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
367 EXPECT_EQ(0u, caller->observer()->GetCandidatesByMline(1).size());
368}
369
Zhi Huange830e682018-03-30 10:48:35 -0700370// It will fail if the offerer uses the mux-BUNDLE policy but the answerer
371// doesn't support BUNDLE.
372TEST_P(PeerConnectionBundleTest, MaxBundleNotSupportedInAnswer) {
373 RTCConfiguration config;
374 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
375 auto caller = CreatePeerConnectionWithAudioVideo(config);
376 auto callee = CreatePeerConnectionWithAudioVideo();
377
378 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
379 bool equal_before =
380 (caller->voice_rtp_transport() == caller->video_rtp_transport());
381 EXPECT_EQ(true, equal_before);
382 RTCOfferAnswerOptions options;
383 options.use_rtp_mux = false;
384 EXPECT_FALSE(
385 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
386}
387
Steve Anton6f25b092017-10-23 09:39:20 -0700388// The following parameterized test verifies that an offer/answer with varying
389// bundle policies and either bundle in the answer or not will produce the
390// expected RTP transports for audio and video. In particular, for bundling we
391// care about whether they are separate transports or the same.
392
393enum class BundleIncluded { kBundleInAnswer, kBundleNotInAnswer };
394std::ostream& operator<<(std::ostream& out, BundleIncluded value) {
395 switch (value) {
396 case BundleIncluded::kBundleInAnswer:
397 return out << "bundle in answer";
398 case BundleIncluded::kBundleNotInAnswer:
399 return out << "bundle not in answer";
400 }
401 return out << "unknown";
402}
403
404class PeerConnectionBundleMatrixTest
Steve Anton7464fca2018-01-19 11:10:37 -0800405 : public PeerConnectionBundleBaseTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700406 public ::testing::WithParamInterface<
Steve Anton7464fca2018-01-19 11:10:37 -0800407 std::tuple<SdpSemantics,
408 std::tuple<BundlePolicy, BundleIncluded, bool, bool>>> {
Steve Anton6f25b092017-10-23 09:39:20 -0700409 protected:
Steve Anton7464fca2018-01-19 11:10:37 -0800410 PeerConnectionBundleMatrixTest()
411 : PeerConnectionBundleBaseTest(std::get<0>(GetParam())) {
412 auto param = std::get<1>(GetParam());
413 bundle_policy_ = std::get<0>(param);
414 bundle_included_ = std::get<1>(param);
415 expected_same_before_ = std::get<2>(param);
416 expected_same_after_ = std::get<3>(param);
Steve Anton6f25b092017-10-23 09:39:20 -0700417 }
418
419 PeerConnectionInterface::BundlePolicy bundle_policy_;
420 BundleIncluded bundle_included_;
421 bool expected_same_before_;
422 bool expected_same_after_;
423};
424
425TEST_P(PeerConnectionBundleMatrixTest,
426 VerifyTransportsBeforeAndAfterSettingRemoteAnswer) {
427 RTCConfiguration config;
428 config.bundle_policy = bundle_policy_;
429 auto caller = CreatePeerConnectionWithAudioVideo(config);
430 auto callee = CreatePeerConnectionWithAudioVideo();
431
432 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
Zhi Huange830e682018-03-30 10:48:35 -0700433 bool equal_before =
434 (caller->voice_rtp_transport() == caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700435 EXPECT_EQ(expected_same_before_, equal_before);
436
437 RTCOfferAnswerOptions options;
438 options.use_rtp_mux = (bundle_included_ == BundleIncluded::kBundleInAnswer);
439 ASSERT_TRUE(
440 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
Zhi Huange830e682018-03-30 10:48:35 -0700441 bool equal_after =
442 (caller->voice_rtp_transport() == caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700443 EXPECT_EQ(expected_same_after_, equal_after);
444}
445
446// The max-bundle policy means we should anticipate bundling being negotiated,
447// and multiplex audio/video from the start.
448// For all other policies, bundling should only be enabled if negotiated by the
449// answer.
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100450INSTANTIATE_TEST_SUITE_P(
Steve Anton6f25b092017-10-23 09:39:20 -0700451 PeerConnectionBundleTest,
452 PeerConnectionBundleMatrixTest,
Steve Anton7464fca2018-01-19 11:10:37 -0800453 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
454 Values(std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
455 BundleIncluded::kBundleInAnswer,
456 false,
457 true),
458 std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
459 BundleIncluded::kBundleNotInAnswer,
460 false,
461 false),
462 std::make_tuple(BundlePolicy::kBundlePolicyMaxBundle,
463 BundleIncluded::kBundleInAnswer,
464 true,
465 true),
Steve Anton7464fca2018-01-19 11:10:37 -0800466 std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
467 BundleIncluded::kBundleInAnswer,
468 false,
469 true),
470 std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
471 BundleIncluded::kBundleNotInAnswer,
472 false,
473 false))));
Steve Anton6f25b092017-10-23 09:39:20 -0700474
475// Test that the audio/video transports on the callee side are the same before
476// and after setting a local answer when max BUNDLE is enabled and an offer with
477// BUNDLE is received.
Steve Anton7464fca2018-01-19 11:10:37 -0800478TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700479 TransportsSameForMaxBundleWithBundleInRemoteOffer) {
480 auto caller = CreatePeerConnectionWithAudioVideo();
481 RTCConfiguration config;
482 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
483 auto callee = CreatePeerConnectionWithAudioVideo(config);
484
485 RTCOfferAnswerOptions options_with_bundle;
486 options_with_bundle.use_rtp_mux = true;
487 ASSERT_TRUE(callee->SetRemoteDescription(
488 caller->CreateOfferAndSetAsLocal(options_with_bundle)));
489
Zhi Huange830e682018-03-30 10:48:35 -0700490 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700491
492 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
493
Zhi Huange830e682018-03-30 10:48:35 -0700494 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700495}
496
Steve Anton7464fca2018-01-19 11:10:37 -0800497TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700498 FailToSetRemoteOfferWithNoBundleWhenBundlePolicyMaxBundle) {
499 auto caller = CreatePeerConnectionWithAudioVideo();
500 RTCConfiguration config;
501 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
502 auto callee = CreatePeerConnectionWithAudioVideo(config);
503
504 RTCOfferAnswerOptions options_no_bundle;
505 options_no_bundle.use_rtp_mux = false;
506 EXPECT_FALSE(callee->SetRemoteDescription(
507 caller->CreateOfferAndSetAsLocal(options_no_bundle)));
508}
509
510// Test that if the media section which has the bundled transport is rejected,
511// then the peers still connect and the bundled transport switches to the other
512// media section.
513// Note: This is currently failing because of the following bug:
514// https://bugs.chromium.org/p/webrtc/issues/detail?id=6280
Steve Anton7464fca2018-01-19 11:10:37 -0800515TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700516 DISABLED_SuccessfullyNegotiateMaxBundleIfBundleTransportMediaRejected) {
517 RTCConfiguration config;
518 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
519 auto caller = CreatePeerConnectionWithAudioVideo(config);
520 auto callee = CreatePeerConnection();
521 callee->AddVideoTrack("v");
522
523 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
524
525 RTCOfferAnswerOptions options;
526 options.offer_to_receive_audio = 0;
527 ASSERT_TRUE(
528 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
529
Zhi Huange830e682018-03-30 10:48:35 -0700530 EXPECT_FALSE(caller->voice_rtp_transport());
531 EXPECT_TRUE(caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700532}
533
534// When requiring RTCP multiplexing, the PeerConnection never makes RTCP
535// transport channels.
Steve Anton7464fca2018-01-19 11:10:37 -0800536TEST_P(PeerConnectionBundleTest, NeverCreateRtcpTransportWithRtcpMuxRequired) {
Steve Anton6f25b092017-10-23 09:39:20 -0700537 RTCConfiguration config;
538 config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyRequire;
539 auto caller = CreatePeerConnectionWithAudioVideo(config);
540 auto callee = CreatePeerConnectionWithAudioVideo();
541
542 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
543
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700544 EXPECT_FALSE(caller->voice_rtp_transport()->rtcp_mux_enabled());
545 EXPECT_FALSE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700546
547 ASSERT_TRUE(
548 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
549
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700550 EXPECT_TRUE(caller->voice_rtp_transport()->rtcp_mux_enabled());
551 EXPECT_TRUE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700552}
553
Zhi Huange830e682018-03-30 10:48:35 -0700554// When negotiating RTCP multiplexing, the PeerConnection makes RTCP transports
555// when the offer is sent, but will destroy them once the remote answer is set.
Steve Anton7464fca2018-01-19 11:10:37 -0800556TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700557 CreateRtcpTransportOnlyBeforeAnswerWithRtcpMuxNegotiate) {
558 RTCConfiguration config;
559 config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyNegotiate;
560 auto caller = CreatePeerConnectionWithAudioVideo(config);
561 auto callee = CreatePeerConnectionWithAudioVideo();
562
563 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
564
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700565 EXPECT_FALSE(caller->voice_rtp_transport()->rtcp_mux_enabled());
566 EXPECT_FALSE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700567
568 ASSERT_TRUE(
569 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
570
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700571 EXPECT_TRUE(caller->voice_rtp_transport()->rtcp_mux_enabled());
572 EXPECT_TRUE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700573}
574
Steve Anton7464fca2018-01-19 11:10:37 -0800575TEST_P(PeerConnectionBundleTest, FailToSetDescriptionWithBundleAndNoRtcpMux) {
Steve Anton6f25b092017-10-23 09:39:20 -0700576 auto caller = CreatePeerConnectionWithAudioVideo();
577 auto callee = CreatePeerConnectionWithAudioVideo();
578
579 RTCOfferAnswerOptions options;
580 options.use_rtp_mux = true;
581
582 auto offer = caller->CreateOffer(options);
583 SdpContentsForEach(RemoveRtcpMux(), offer->description());
584
585 std::string error;
586 EXPECT_FALSE(caller->SetLocalDescription(CloneSessionDescription(offer.get()),
587 &error));
588 EXPECT_EQ(
589 "Failed to set local offer sdp: rtcp-mux must be enabled when BUNDLE is "
590 "enabled.",
591 error);
592
593 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer), &error));
594 EXPECT_EQ(
595 "Failed to set remote offer sdp: rtcp-mux must be enabled when BUNDLE is "
596 "enabled.",
597 error);
598}
599
600// Test that candidates sent to the "video" transport do not get pushed down to
601// the "audio" transport channel when bundling.
Steve Anton7464fca2018-01-19 11:10:37 -0800602TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700603 IgnoreCandidatesForUnusedTransportWhenBundling) {
604 const SocketAddress kAudioAddress1("1.1.1.1", 1111);
605 const SocketAddress kAudioAddress2("2.2.2.2", 2222);
606 const SocketAddress kVideoAddress("3.3.3.3", 3333);
607 const SocketAddress kCallerAddress("4.4.4.4", 0);
608 const SocketAddress kCalleeAddress("5.5.5.5", 0);
609
610 auto caller = CreatePeerConnectionWithAudioVideo();
611 auto callee = CreatePeerConnectionWithAudioVideo();
612
613 caller->network()->AddInterface(kCallerAddress);
614 callee->network()->AddInterface(kCalleeAddress);
615
616 RTCOfferAnswerOptions options;
617 options.use_rtp_mux = true;
618
619 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
620 ASSERT_TRUE(
Zhi Huange830e682018-03-30 10:48:35 -0700621 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
Steve Anton6f25b092017-10-23 09:39:20 -0700622
623 // The way the *_WAIT checks work is they only wait if the condition fails,
624 // which does not help in the case where state is not changing. This is
625 // problematic in this test since we want to verify that adding a video
626 // candidate does _not_ change state. So we interleave candidates and assume
627 // that messages are executed in the order they were posted.
628
629 cricket::Candidate audio_candidate1 = CreateLocalUdpCandidate(kAudioAddress1);
630 ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate1,
631 cricket::MEDIA_TYPE_AUDIO));
632
633 cricket::Candidate video_candidate = CreateLocalUdpCandidate(kVideoAddress);
634 ASSERT_TRUE(caller->AddIceCandidateToMedia(&video_candidate,
635 cricket::MEDIA_TYPE_VIDEO));
636
637 cricket::Candidate audio_candidate2 = CreateLocalUdpCandidate(kAudioAddress2);
638 ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate2,
639 cricket::MEDIA_TYPE_AUDIO));
640
641 EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress1),
642 kDefaultTimeout);
643 EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress2),
644 kDefaultTimeout);
645 EXPECT_FALSE(caller->HasConnectionWithRemoteAddress(kVideoAddress));
646}
647
648// Test that the transport used by both audio and video is the transport
649// associated with the first MID in the answer BUNDLE group, even if it's in a
650// different order from the offer.
Steve Anton7464fca2018-01-19 11:10:37 -0800651TEST_P(PeerConnectionBundleTest, BundleOnFirstMidInAnswer) {
Steve Anton6f25b092017-10-23 09:39:20 -0700652 auto caller = CreatePeerConnectionWithAudioVideo();
653 auto callee = CreatePeerConnectionWithAudioVideo();
654
655 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
656
Zhi Huange830e682018-03-30 10:48:35 -0700657 auto* old_video_transport = caller->video_rtp_transport();
Steve Anton6f25b092017-10-23 09:39:20 -0700658
659 auto answer = callee->CreateAnswer();
660 auto* old_bundle_group =
661 answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
Steve Anton7464fca2018-01-19 11:10:37 -0800662 std::string first_mid = old_bundle_group->content_names()[0];
663 std::string second_mid = old_bundle_group->content_names()[1];
Steve Anton6f25b092017-10-23 09:39:20 -0700664 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
665
666 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
Steve Anton7464fca2018-01-19 11:10:37 -0800667 new_bundle_group.AddContentName(second_mid);
668 new_bundle_group.AddContentName(first_mid);
Steve Anton6f25b092017-10-23 09:39:20 -0700669 answer->description()->AddGroup(new_bundle_group);
670
671 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
672
Zhi Huange830e682018-03-30 10:48:35 -0700673 EXPECT_EQ(old_video_transport, caller->video_rtp_transport());
674 EXPECT_EQ(caller->voice_rtp_transport(), caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700675}
676
Zhi Huang365381f2018-04-13 16:44:34 -0700677// This tests that applying description with conflicted RTP demuxing criteria
678// will fail.
679TEST_P(PeerConnectionBundleTest,
680 ApplyDescriptionWithConflictedDemuxCriteriaFail) {
681 auto caller = CreatePeerConnectionWithAudioVideo();
682 auto callee = CreatePeerConnectionWithAudioVideo();
683
684 RTCOfferAnswerOptions options;
685 options.use_rtp_mux = false;
686 auto offer = caller->CreateOffer(options);
687 // Modified the SDP to make two m= sections have the same SSRC.
688 ASSERT_GE(offer->description()->contents().size(), 2U);
689 offer->description()
690 ->contents()[0]
Harald Alvestrand1716d392019-06-03 20:35:45 +0200691 .media_description()
692 ->mutable_streams()[0]
Zhi Huang365381f2018-04-13 16:44:34 -0700693 .ssrcs[0] = 1111222;
694 offer->description()
695 ->contents()[1]
Harald Alvestrand1716d392019-06-03 20:35:45 +0200696 .media_description()
697 ->mutable_streams()[0]
Zhi Huang365381f2018-04-13 16:44:34 -0700698 .ssrcs[0] = 1111222;
699 EXPECT_TRUE(
700 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
701 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
702 EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal(options));
703
704 // Enable BUNDLE in subsequent offer/answer exchange and two m= sections are
705 // expectd to use one RtpTransport underneath.
706 options.use_rtp_mux = true;
707 EXPECT_TRUE(
708 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal(options)));
709 auto answer = callee->CreateAnswer(options);
710 // When BUNDLE is enabled, applying the description is expected to fail
711 // because the demuxing criteria is conflicted.
712 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
713}
714
Zhi Huangd2248f82018-04-10 14:41:03 -0700715// This tests that changing the pre-negotiated BUNDLE tag is not supported.
716TEST_P(PeerConnectionBundleTest, RejectDescriptionChangingBundleTag) {
717 RTCConfiguration config;
718 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
719 auto caller = CreatePeerConnectionWithAudioVideo(config);
720 auto callee = CreatePeerConnectionWithAudioVideo(config);
721
722 RTCOfferAnswerOptions options;
723 options.use_rtp_mux = true;
724 auto offer = caller->CreateOfferAndSetAsLocal(options);
725
726 // Create a new bundle-group with different bundled_mid.
727 auto* old_bundle_group =
728 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
729 std::string first_mid = old_bundle_group->content_names()[0];
730 std::string second_mid = old_bundle_group->content_names()[1];
731 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
732 new_bundle_group.AddContentName(second_mid);
733
734 auto re_offer = CloneSessionDescription(offer.get());
735 callee->SetRemoteDescription(std::move(offer));
736 auto answer = callee->CreateAnswer(options);
737 // Reject the first MID.
738 answer->description()->contents()[0].rejected = true;
739 // Remove the first MID from the bundle group.
740 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
741 answer->description()->AddGroup(new_bundle_group);
742 // The answer is expected to be rejected.
743 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
744
745 // Do the same thing for re-offer.
746 re_offer->description()->contents()[0].rejected = true;
747 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
748 re_offer->description()->AddGroup(new_bundle_group);
749 // The re-offer is expected to be rejected.
750 EXPECT_FALSE(caller->SetLocalDescription(std::move(re_offer)));
751}
752
753// This tests that removing contents from BUNDLE group and reject the whole
754// BUNDLE group could work. This is a regression test for
755// (https://bugs.chromium.org/p/chromium/issues/detail?id=827917)
756TEST_P(PeerConnectionBundleTest, RemovingContentAndRejectBundleGroup) {
757 RTCConfiguration config;
758#ifndef HAVE_SCTP
759 config.enable_rtp_data_channel = true;
760#endif
761 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
762 auto caller = CreatePeerConnectionWithAudioVideo(config);
763 caller->CreateDataChannel("dc");
764
765 auto offer = caller->CreateOfferAndSetAsLocal();
766 auto re_offer = CloneSessionDescription(offer.get());
767
768 // Removing the second MID from the BUNDLE group.
769 auto* old_bundle_group =
770 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
771 std::string first_mid = old_bundle_group->content_names()[0];
772 std::string third_mid = old_bundle_group->content_names()[2];
773 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
774 new_bundle_group.AddContentName(first_mid);
775 new_bundle_group.AddContentName(third_mid);
776
777 // Reject the entire new bundle group.
778 re_offer->description()->contents()[0].rejected = true;
779 re_offer->description()->contents()[2].rejected = true;
780 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
781 re_offer->description()->AddGroup(new_bundle_group);
782
783 EXPECT_TRUE(caller->SetLocalDescription(std::move(re_offer)));
784}
785
786// This tests that the BUNDLE group in answer should be a subset of the offered
787// group.
788TEST_P(PeerConnectionBundleTest, AddContentToBundleGroupInAnswerNotSupported) {
789 auto caller = CreatePeerConnectionWithAudioVideo();
790 auto callee = CreatePeerConnectionWithAudioVideo();
791
792 auto offer = caller->CreateOffer();
793 std::string first_mid = offer->description()->contents()[0].name;
794 std::string second_mid = offer->description()->contents()[1].name;
795
796 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
797 bundle_group.AddContentName(first_mid);
798 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
799 offer->description()->AddGroup(bundle_group);
800 EXPECT_TRUE(
801 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
802 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
803
804 auto answer = callee->CreateAnswer();
805 bundle_group.AddContentName(second_mid);
806 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
807 answer->description()->AddGroup(bundle_group);
808
809 // The answer is expected to be rejected because second mid is not in the
810 // offered BUNDLE group.
811 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
812}
813
814// This tests that the BUNDLE group with non-existing MID should be rejectd.
815TEST_P(PeerConnectionBundleTest, RejectBundleGroupWithNonExistingMid) {
816 auto caller = CreatePeerConnectionWithAudioVideo();
817 auto callee = CreatePeerConnectionWithAudioVideo();
818
819 auto offer = caller->CreateOffer();
820 auto invalid_bundle_group =
821 *offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
822 invalid_bundle_group.AddContentName("non-existing-MID");
823 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
824 offer->description()->AddGroup(invalid_bundle_group);
825
826 EXPECT_FALSE(
827 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
828 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
829}
830
831// This tests that an answer shouldn't be able to remove an m= section from an
832// established group without rejecting it.
833TEST_P(PeerConnectionBundleTest, RemoveContentFromBundleGroup) {
834 auto caller = CreatePeerConnectionWithAudioVideo();
835 auto callee = CreatePeerConnectionWithAudioVideo();
836
837 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
838 EXPECT_TRUE(
839 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
840
841 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
842 auto answer = callee->CreateAnswer();
843 std::string second_mid = answer->description()->contents()[1].name;
844
845 auto invalid_bundle_group =
846 *answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
847 invalid_bundle_group.RemoveContentName(second_mid);
848 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
849 answer->description()->AddGroup(invalid_bundle_group);
850
851 EXPECT_FALSE(
852 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
853}
854
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100855INSTANTIATE_TEST_SUITE_P(PeerConnectionBundleTest,
856 PeerConnectionBundleTest,
857 Values(SdpSemantics::kPlanB,
858 SdpSemantics::kUnifiedPlan));
Steve Anton7464fca2018-01-19 11:10:37 -0800859
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700860// According to RFC5888, if an endpoint understands the semantics of an
861// "a=group", it MUST return an answer with that group. So, an empty BUNDLE
862// group is valid when the answerer rejects all m= sections (by stopping all
863// transceivers), meaning there's nothing to bundle.
864//
865// Only writing this test for Unified Plan mode, since there's no way to reject
866// m= sections in answers for Plan B without SDP munging.
867TEST_F(PeerConnectionBundleTestUnifiedPlan,
868 EmptyBundleGroupCreatedInAnswerWhenAppropriate) {
869 auto caller = CreatePeerConnectionWithAudioVideo();
870 auto callee = CreatePeerConnection();
871
872 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
873
874 // Stop all transceivers, causing all m= sections to be rejected.
875 for (const auto& transceiver : callee->pc()->GetTransceivers()) {
876 transceiver->Stop();
877 }
878 EXPECT_TRUE(
879 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
880
881 // Verify that the answer actually contained an empty bundle group.
882 const SessionDescriptionInterface* desc = callee->pc()->local_description();
883 ASSERT_NE(nullptr, desc);
884 const cricket::ContentGroup* bundle_group =
885 desc->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
886 ASSERT_NE(nullptr, bundle_group);
887 EXPECT_TRUE(bundle_group->content_names().empty());
888}
889
Steve Anton6f25b092017-10-23 09:39:20 -0700890} // namespace webrtc