blob: 6c372b4b8982b8289921c75c07a2a69bfa2e6df4 [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 Anton6f25b092017-10-23 09:39:20 -070014#include "api/peerconnectionproxy.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 Anton6f25b092017-10-23 09:39:20 -070017#include "p2p/base/fakeportallocator.h"
18#include "p2p/base/teststunserver.h"
19#include "p2p/client/basicportallocator.h"
20#include "pc/mediasession.h"
21#include "pc/peerconnection.h"
22#include "pc/peerconnectionwrapper.h"
23#include "pc/sdputils.h"
24#ifdef WEBRTC_ANDROID
25#include "pc/test/androidtestinitializer.h"
26#endif
Karl Wiberg918f50c2018-07-05 11:40:33 +020027#include "absl/memory/memory.h"
Steve Anton6f25b092017-10-23 09:39:20 -070028#include "pc/test/fakeaudiocapturemodule.h"
29#include "rtc_base/fakenetwork.h"
30#include "rtc_base/gunit.h"
Steve Anton6f25b092017-10-23 09:39:20 -070031#include "rtc_base/virtualsocketserver.h"
32#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
54class PeerConnectionWrapperForBundleTest : public PeerConnectionWrapper {
55 public:
56 using PeerConnectionWrapper::PeerConnectionWrapper;
57
58 bool AddIceCandidateToMedia(cricket::Candidate* candidate,
59 cricket::MediaType media_type) {
60 auto* desc = pc()->remote_description()->description();
61 for (size_t i = 0; i < desc->contents().size(); i++) {
62 const auto& content = desc->contents()[i];
Steve Antonb1c1de12017-12-21 15:14:30 -080063 if (content.media_description()->type() == media_type) {
Steve Anton6f25b092017-10-23 09:39:20 -070064 candidate->set_transport_name(content.name);
Steve Anton27ab0e52018-07-23 15:11:53 -070065 std::unique_ptr<IceCandidateInterface> jsep_candidate =
66 CreateIceCandidate(content.name, i, *candidate);
67 return pc()->AddIceCandidate(jsep_candidate.get());
Steve Anton6f25b092017-10-23 09:39:20 -070068 }
69 }
70 RTC_NOTREACHED();
71 return false;
72 }
73
Zhi Huange830e682018-03-30 10:48:35 -070074 rtc::PacketTransportInternal* voice_rtp_transport() {
75 return (voice_channel() ? voice_channel()->rtp_packet_transport()
76 : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -070077 }
78
Zhi Huange830e682018-03-30 10:48:35 -070079 rtc::PacketTransportInternal* voice_rtcp_transport() {
80 return (voice_channel() ? voice_channel()->rtcp_packet_transport()
81 : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -070082 }
83
84 cricket::VoiceChannel* voice_channel() {
Steve Antonb8867112018-02-13 10:07:54 -080085 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
Steve Anton7464fca2018-01-19 11:10:37 -080086 for (auto transceiver : transceivers) {
Steve Anton69470252018-02-09 11:43:08 -080087 if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
Steve Anton7464fca2018-01-19 11:10:37 -080088 return static_cast<cricket::VoiceChannel*>(
89 transceiver->internal()->channel());
90 }
91 }
92 return nullptr;
Steve Anton6f25b092017-10-23 09:39:20 -070093 }
94
Zhi Huange830e682018-03-30 10:48:35 -070095 rtc::PacketTransportInternal* video_rtp_transport() {
96 return (video_channel() ? video_channel()->rtp_packet_transport()
97 : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -070098 }
99
Zhi Huange830e682018-03-30 10:48:35 -0700100 rtc::PacketTransportInternal* video_rtcp_transport() {
101 return (video_channel() ? video_channel()->rtcp_packet_transport()
102 : 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();
Steve Anton7464fca2018-01-19 11:10:37 -0800107 for (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.
238 auto* fake_network = new rtc::FakeNetworkManager();
239 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;
273 for (auto* candidate : candidates) {
274 components.push_back(candidate->candidate().component());
275 }
276 return components;
277}
278
279// Test that there are 2 local UDP candidates (1 RTP and 1 RTCP candidate) for
280// each media section when disabling bundling and disabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800281TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700282 TwoCandidatesForEachTransportWhenNoBundleNoRtcpMux) {
283 const SocketAddress kCallerAddress("1.1.1.1", 0);
284 const SocketAddress kCalleeAddress("2.2.2.2", 0);
285
286 RTCConfiguration config;
287 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
288 auto caller = CreatePeerConnectionWithAudioVideo(config);
289 caller->network()->AddInterface(kCallerAddress);
290 auto callee = CreatePeerConnectionWithAudioVideo(config);
291 callee->network()->AddInterface(kCalleeAddress);
292
293 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
294 RTCOfferAnswerOptions options_no_bundle;
295 options_no_bundle.use_rtp_mux = false;
296 auto answer = callee->CreateAnswer(options_no_bundle);
297 SdpContentsForEach(RemoveRtcpMux(), answer->description());
298 ASSERT_TRUE(
299 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
300 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
301
302 // Check that caller has separate RTP and RTCP candidates for each media.
303 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
304 EXPECT_THAT(
305 GetCandidateComponents(caller->observer()->GetCandidatesByMline(0)),
306 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
307 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
308 EXPECT_THAT(
309 GetCandidateComponents(caller->observer()->GetCandidatesByMline(1)),
310 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
311 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
312
313 // Check that callee has separate RTP and RTCP candidates for each media.
314 EXPECT_TRUE_WAIT(callee->IsIceGatheringDone(), kDefaultTimeout);
315 EXPECT_THAT(
316 GetCandidateComponents(callee->observer()->GetCandidatesByMline(0)),
317 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
318 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
319 EXPECT_THAT(
320 GetCandidateComponents(callee->observer()->GetCandidatesByMline(1)),
321 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
322 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
323}
324
325// Test that there is 1 local UDP candidate for both RTP and RTCP for each media
326// section when disabling bundle but enabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800327TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700328 OneCandidateForEachTransportWhenNoBundleButRtcpMux) {
329 const SocketAddress kCallerAddress("1.1.1.1", 0);
330
331 auto caller = CreatePeerConnectionWithAudioVideo();
332 caller->network()->AddInterface(kCallerAddress);
333 auto callee = CreatePeerConnectionWithAudioVideo();
334
335 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
336 RTCOfferAnswerOptions options_no_bundle;
337 options_no_bundle.use_rtp_mux = false;
338 ASSERT_TRUE(
339 caller->SetRemoteDescription(callee->CreateAnswer(options_no_bundle)));
340
341 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
342
343 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
344 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(1).size());
345}
346
347// Test that there is 1 local UDP candidate in only the first media section when
348// bundling and enabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800349TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700350 OneCandidateOnlyOnFirstTransportWhenBundleAndRtcpMux) {
351 const SocketAddress kCallerAddress("1.1.1.1", 0);
352
353 RTCConfiguration config;
354 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
355 auto caller = CreatePeerConnectionWithAudioVideo(config);
356 caller->network()->AddInterface(kCallerAddress);
357 auto callee = CreatePeerConnectionWithAudioVideo(config);
358
359 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
360 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateAnswer()));
361
362 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
363
364 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
365 EXPECT_EQ(0u, caller->observer()->GetCandidatesByMline(1).size());
366}
367
Zhi Huange830e682018-03-30 10:48:35 -0700368// It will fail if the offerer uses the mux-BUNDLE policy but the answerer
369// doesn't support BUNDLE.
370TEST_P(PeerConnectionBundleTest, MaxBundleNotSupportedInAnswer) {
371 RTCConfiguration config;
372 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
373 auto caller = CreatePeerConnectionWithAudioVideo(config);
374 auto callee = CreatePeerConnectionWithAudioVideo();
375
376 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
377 bool equal_before =
378 (caller->voice_rtp_transport() == caller->video_rtp_transport());
379 EXPECT_EQ(true, equal_before);
380 RTCOfferAnswerOptions options;
381 options.use_rtp_mux = false;
382 EXPECT_FALSE(
383 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
384}
385
Steve Anton6f25b092017-10-23 09:39:20 -0700386// The following parameterized test verifies that an offer/answer with varying
387// bundle policies and either bundle in the answer or not will produce the
388// expected RTP transports for audio and video. In particular, for bundling we
389// care about whether they are separate transports or the same.
390
391enum class BundleIncluded { kBundleInAnswer, kBundleNotInAnswer };
392std::ostream& operator<<(std::ostream& out, BundleIncluded value) {
393 switch (value) {
394 case BundleIncluded::kBundleInAnswer:
395 return out << "bundle in answer";
396 case BundleIncluded::kBundleNotInAnswer:
397 return out << "bundle not in answer";
398 }
399 return out << "unknown";
400}
401
402class PeerConnectionBundleMatrixTest
Steve Anton7464fca2018-01-19 11:10:37 -0800403 : public PeerConnectionBundleBaseTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700404 public ::testing::WithParamInterface<
Steve Anton7464fca2018-01-19 11:10:37 -0800405 std::tuple<SdpSemantics,
406 std::tuple<BundlePolicy, BundleIncluded, bool, bool>>> {
Steve Anton6f25b092017-10-23 09:39:20 -0700407 protected:
Steve Anton7464fca2018-01-19 11:10:37 -0800408 PeerConnectionBundleMatrixTest()
409 : PeerConnectionBundleBaseTest(std::get<0>(GetParam())) {
410 auto param = std::get<1>(GetParam());
411 bundle_policy_ = std::get<0>(param);
412 bundle_included_ = std::get<1>(param);
413 expected_same_before_ = std::get<2>(param);
414 expected_same_after_ = std::get<3>(param);
Steve Anton6f25b092017-10-23 09:39:20 -0700415 }
416
417 PeerConnectionInterface::BundlePolicy bundle_policy_;
418 BundleIncluded bundle_included_;
419 bool expected_same_before_;
420 bool expected_same_after_;
421};
422
423TEST_P(PeerConnectionBundleMatrixTest,
424 VerifyTransportsBeforeAndAfterSettingRemoteAnswer) {
425 RTCConfiguration config;
426 config.bundle_policy = bundle_policy_;
427 auto caller = CreatePeerConnectionWithAudioVideo(config);
428 auto callee = CreatePeerConnectionWithAudioVideo();
429
430 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
Zhi Huange830e682018-03-30 10:48:35 -0700431 bool equal_before =
432 (caller->voice_rtp_transport() == caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700433 EXPECT_EQ(expected_same_before_, equal_before);
434
435 RTCOfferAnswerOptions options;
436 options.use_rtp_mux = (bundle_included_ == BundleIncluded::kBundleInAnswer);
437 ASSERT_TRUE(
438 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
Zhi Huange830e682018-03-30 10:48:35 -0700439 bool equal_after =
440 (caller->voice_rtp_transport() == caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700441 EXPECT_EQ(expected_same_after_, equal_after);
442}
443
444// The max-bundle policy means we should anticipate bundling being negotiated,
445// and multiplex audio/video from the start.
446// For all other policies, bundling should only be enabled if negotiated by the
447// answer.
448INSTANTIATE_TEST_CASE_P(
449 PeerConnectionBundleTest,
450 PeerConnectionBundleMatrixTest,
Steve Anton7464fca2018-01-19 11:10:37 -0800451 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
452 Values(std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
453 BundleIncluded::kBundleInAnswer,
454 false,
455 true),
456 std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
457 BundleIncluded::kBundleNotInAnswer,
458 false,
459 false),
460 std::make_tuple(BundlePolicy::kBundlePolicyMaxBundle,
461 BundleIncluded::kBundleInAnswer,
462 true,
463 true),
Steve Anton7464fca2018-01-19 11:10:37 -0800464 std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
465 BundleIncluded::kBundleInAnswer,
466 false,
467 true),
468 std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
469 BundleIncluded::kBundleNotInAnswer,
470 false,
471 false))));
Steve Anton6f25b092017-10-23 09:39:20 -0700472
473// Test that the audio/video transports on the callee side are the same before
474// and after setting a local answer when max BUNDLE is enabled and an offer with
475// BUNDLE is received.
Steve Anton7464fca2018-01-19 11:10:37 -0800476TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700477 TransportsSameForMaxBundleWithBundleInRemoteOffer) {
478 auto caller = CreatePeerConnectionWithAudioVideo();
479 RTCConfiguration config;
480 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
481 auto callee = CreatePeerConnectionWithAudioVideo(config);
482
483 RTCOfferAnswerOptions options_with_bundle;
484 options_with_bundle.use_rtp_mux = true;
485 ASSERT_TRUE(callee->SetRemoteDescription(
486 caller->CreateOfferAndSetAsLocal(options_with_bundle)));
487
Zhi Huange830e682018-03-30 10:48:35 -0700488 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700489
490 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
491
Zhi Huange830e682018-03-30 10:48:35 -0700492 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700493}
494
Steve Anton7464fca2018-01-19 11:10:37 -0800495TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700496 FailToSetRemoteOfferWithNoBundleWhenBundlePolicyMaxBundle) {
497 auto caller = CreatePeerConnectionWithAudioVideo();
498 RTCConfiguration config;
499 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
500 auto callee = CreatePeerConnectionWithAudioVideo(config);
501
502 RTCOfferAnswerOptions options_no_bundle;
503 options_no_bundle.use_rtp_mux = false;
504 EXPECT_FALSE(callee->SetRemoteDescription(
505 caller->CreateOfferAndSetAsLocal(options_no_bundle)));
506}
507
508// Test that if the media section which has the bundled transport is rejected,
509// then the peers still connect and the bundled transport switches to the other
510// media section.
511// Note: This is currently failing because of the following bug:
512// https://bugs.chromium.org/p/webrtc/issues/detail?id=6280
Steve Anton7464fca2018-01-19 11:10:37 -0800513TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700514 DISABLED_SuccessfullyNegotiateMaxBundleIfBundleTransportMediaRejected) {
515 RTCConfiguration config;
516 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
517 auto caller = CreatePeerConnectionWithAudioVideo(config);
518 auto callee = CreatePeerConnection();
519 callee->AddVideoTrack("v");
520
521 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
522
523 RTCOfferAnswerOptions options;
524 options.offer_to_receive_audio = 0;
525 ASSERT_TRUE(
526 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
527
Zhi Huange830e682018-03-30 10:48:35 -0700528 EXPECT_FALSE(caller->voice_rtp_transport());
529 EXPECT_TRUE(caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700530}
531
532// When requiring RTCP multiplexing, the PeerConnection never makes RTCP
533// transport channels.
Steve Anton7464fca2018-01-19 11:10:37 -0800534TEST_P(PeerConnectionBundleTest, NeverCreateRtcpTransportWithRtcpMuxRequired) {
Steve Anton6f25b092017-10-23 09:39:20 -0700535 RTCConfiguration config;
536 config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyRequire;
537 auto caller = CreatePeerConnectionWithAudioVideo(config);
538 auto callee = CreatePeerConnectionWithAudioVideo();
539
540 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
541
Zhi Huange830e682018-03-30 10:48:35 -0700542 EXPECT_FALSE(caller->voice_rtcp_transport());
543 EXPECT_FALSE(caller->video_rtcp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700544
545 ASSERT_TRUE(
546 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
547
Zhi Huange830e682018-03-30 10:48:35 -0700548 EXPECT_FALSE(caller->voice_rtcp_transport());
549 EXPECT_FALSE(caller->video_rtcp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700550}
551
Zhi Huange830e682018-03-30 10:48:35 -0700552// When negotiating RTCP multiplexing, the PeerConnection makes RTCP transports
553// when the offer is sent, but will destroy them once the remote answer is set.
Steve Anton7464fca2018-01-19 11:10:37 -0800554TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700555 CreateRtcpTransportOnlyBeforeAnswerWithRtcpMuxNegotiate) {
556 RTCConfiguration config;
557 config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyNegotiate;
558 auto caller = CreatePeerConnectionWithAudioVideo(config);
559 auto callee = CreatePeerConnectionWithAudioVideo();
560
561 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
562
Zhi Huange830e682018-03-30 10:48:35 -0700563 EXPECT_TRUE(caller->voice_rtcp_transport());
564 EXPECT_TRUE(caller->video_rtcp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700565
566 ASSERT_TRUE(
567 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
568
Zhi Huange830e682018-03-30 10:48:35 -0700569 EXPECT_FALSE(caller->voice_rtcp_transport());
570 EXPECT_FALSE(caller->video_rtcp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700571}
572
Steve Anton7464fca2018-01-19 11:10:37 -0800573TEST_P(PeerConnectionBundleTest, FailToSetDescriptionWithBundleAndNoRtcpMux) {
Steve Anton6f25b092017-10-23 09:39:20 -0700574 auto caller = CreatePeerConnectionWithAudioVideo();
575 auto callee = CreatePeerConnectionWithAudioVideo();
576
577 RTCOfferAnswerOptions options;
578 options.use_rtp_mux = true;
579
580 auto offer = caller->CreateOffer(options);
581 SdpContentsForEach(RemoveRtcpMux(), offer->description());
582
583 std::string error;
584 EXPECT_FALSE(caller->SetLocalDescription(CloneSessionDescription(offer.get()),
585 &error));
586 EXPECT_EQ(
587 "Failed to set local offer sdp: rtcp-mux must be enabled when BUNDLE is "
588 "enabled.",
589 error);
590
591 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer), &error));
592 EXPECT_EQ(
593 "Failed to set remote offer sdp: rtcp-mux must be enabled when BUNDLE is "
594 "enabled.",
595 error);
596}
597
598// Test that candidates sent to the "video" transport do not get pushed down to
599// the "audio" transport channel when bundling.
Steve Anton7464fca2018-01-19 11:10:37 -0800600TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700601 IgnoreCandidatesForUnusedTransportWhenBundling) {
602 const SocketAddress kAudioAddress1("1.1.1.1", 1111);
603 const SocketAddress kAudioAddress2("2.2.2.2", 2222);
604 const SocketAddress kVideoAddress("3.3.3.3", 3333);
605 const SocketAddress kCallerAddress("4.4.4.4", 0);
606 const SocketAddress kCalleeAddress("5.5.5.5", 0);
607
608 auto caller = CreatePeerConnectionWithAudioVideo();
609 auto callee = CreatePeerConnectionWithAudioVideo();
610
611 caller->network()->AddInterface(kCallerAddress);
612 callee->network()->AddInterface(kCalleeAddress);
613
614 RTCOfferAnswerOptions options;
615 options.use_rtp_mux = true;
616
617 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
618 ASSERT_TRUE(
Zhi Huange830e682018-03-30 10:48:35 -0700619 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
Steve Anton6f25b092017-10-23 09:39:20 -0700620
621 // The way the *_WAIT checks work is they only wait if the condition fails,
622 // which does not help in the case where state is not changing. This is
623 // problematic in this test since we want to verify that adding a video
624 // candidate does _not_ change state. So we interleave candidates and assume
625 // that messages are executed in the order they were posted.
626
627 cricket::Candidate audio_candidate1 = CreateLocalUdpCandidate(kAudioAddress1);
628 ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate1,
629 cricket::MEDIA_TYPE_AUDIO));
630
631 cricket::Candidate video_candidate = CreateLocalUdpCandidate(kVideoAddress);
632 ASSERT_TRUE(caller->AddIceCandidateToMedia(&video_candidate,
633 cricket::MEDIA_TYPE_VIDEO));
634
635 cricket::Candidate audio_candidate2 = CreateLocalUdpCandidate(kAudioAddress2);
636 ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate2,
637 cricket::MEDIA_TYPE_AUDIO));
638
639 EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress1),
640 kDefaultTimeout);
641 EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress2),
642 kDefaultTimeout);
643 EXPECT_FALSE(caller->HasConnectionWithRemoteAddress(kVideoAddress));
644}
645
646// Test that the transport used by both audio and video is the transport
647// associated with the first MID in the answer BUNDLE group, even if it's in a
648// different order from the offer.
Steve Anton7464fca2018-01-19 11:10:37 -0800649TEST_P(PeerConnectionBundleTest, BundleOnFirstMidInAnswer) {
Steve Anton6f25b092017-10-23 09:39:20 -0700650 auto caller = CreatePeerConnectionWithAudioVideo();
651 auto callee = CreatePeerConnectionWithAudioVideo();
652
653 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
654
Zhi Huange830e682018-03-30 10:48:35 -0700655 auto* old_video_transport = caller->video_rtp_transport();
Steve Anton6f25b092017-10-23 09:39:20 -0700656
657 auto answer = callee->CreateAnswer();
658 auto* old_bundle_group =
659 answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
Steve Anton7464fca2018-01-19 11:10:37 -0800660 std::string first_mid = old_bundle_group->content_names()[0];
661 std::string second_mid = old_bundle_group->content_names()[1];
Steve Anton6f25b092017-10-23 09:39:20 -0700662 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
663
664 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
Steve Anton7464fca2018-01-19 11:10:37 -0800665 new_bundle_group.AddContentName(second_mid);
666 new_bundle_group.AddContentName(first_mid);
Steve Anton6f25b092017-10-23 09:39:20 -0700667 answer->description()->AddGroup(new_bundle_group);
668
669 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
670
Zhi Huange830e682018-03-30 10:48:35 -0700671 EXPECT_EQ(old_video_transport, caller->video_rtp_transport());
672 EXPECT_EQ(caller->voice_rtp_transport(), caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700673}
674
Zhi Huang365381f2018-04-13 16:44:34 -0700675// This tests that applying description with conflicted RTP demuxing criteria
676// will fail.
677TEST_P(PeerConnectionBundleTest,
678 ApplyDescriptionWithConflictedDemuxCriteriaFail) {
679 auto caller = CreatePeerConnectionWithAudioVideo();
680 auto callee = CreatePeerConnectionWithAudioVideo();
681
682 RTCOfferAnswerOptions options;
683 options.use_rtp_mux = false;
684 auto offer = caller->CreateOffer(options);
685 // Modified the SDP to make two m= sections have the same SSRC.
686 ASSERT_GE(offer->description()->contents().size(), 2U);
687 offer->description()
688 ->contents()[0]
689 .description->mutable_streams()[0]
690 .ssrcs[0] = 1111222;
691 offer->description()
692 ->contents()[1]
693 .description->mutable_streams()[0]
694 .ssrcs[0] = 1111222;
695 EXPECT_TRUE(
696 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
697 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
698 EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal(options));
699
700 // Enable BUNDLE in subsequent offer/answer exchange and two m= sections are
701 // expectd to use one RtpTransport underneath.
702 options.use_rtp_mux = true;
703 EXPECT_TRUE(
704 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal(options)));
705 auto answer = callee->CreateAnswer(options);
706 // When BUNDLE is enabled, applying the description is expected to fail
707 // because the demuxing criteria is conflicted.
708 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
709}
710
Zhi Huangd2248f82018-04-10 14:41:03 -0700711// This tests that changing the pre-negotiated BUNDLE tag is not supported.
712TEST_P(PeerConnectionBundleTest, RejectDescriptionChangingBundleTag) {
713 RTCConfiguration config;
714 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
715 auto caller = CreatePeerConnectionWithAudioVideo(config);
716 auto callee = CreatePeerConnectionWithAudioVideo(config);
717
718 RTCOfferAnswerOptions options;
719 options.use_rtp_mux = true;
720 auto offer = caller->CreateOfferAndSetAsLocal(options);
721
722 // Create a new bundle-group with different bundled_mid.
723 auto* old_bundle_group =
724 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
725 std::string first_mid = old_bundle_group->content_names()[0];
726 std::string second_mid = old_bundle_group->content_names()[1];
727 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
728 new_bundle_group.AddContentName(second_mid);
729
730 auto re_offer = CloneSessionDescription(offer.get());
731 callee->SetRemoteDescription(std::move(offer));
732 auto answer = callee->CreateAnswer(options);
733 // Reject the first MID.
734 answer->description()->contents()[0].rejected = true;
735 // Remove the first MID from the bundle group.
736 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
737 answer->description()->AddGroup(new_bundle_group);
738 // The answer is expected to be rejected.
739 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
740
741 // Do the same thing for re-offer.
742 re_offer->description()->contents()[0].rejected = true;
743 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
744 re_offer->description()->AddGroup(new_bundle_group);
745 // The re-offer is expected to be rejected.
746 EXPECT_FALSE(caller->SetLocalDescription(std::move(re_offer)));
747}
748
749// This tests that removing contents from BUNDLE group and reject the whole
750// BUNDLE group could work. This is a regression test for
751// (https://bugs.chromium.org/p/chromium/issues/detail?id=827917)
752TEST_P(PeerConnectionBundleTest, RemovingContentAndRejectBundleGroup) {
753 RTCConfiguration config;
754#ifndef HAVE_SCTP
755 config.enable_rtp_data_channel = true;
756#endif
757 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
758 auto caller = CreatePeerConnectionWithAudioVideo(config);
759 caller->CreateDataChannel("dc");
760
761 auto offer = caller->CreateOfferAndSetAsLocal();
762 auto re_offer = CloneSessionDescription(offer.get());
763
764 // Removing the second MID from the BUNDLE group.
765 auto* old_bundle_group =
766 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
767 std::string first_mid = old_bundle_group->content_names()[0];
768 std::string third_mid = old_bundle_group->content_names()[2];
769 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
770 new_bundle_group.AddContentName(first_mid);
771 new_bundle_group.AddContentName(third_mid);
772
773 // Reject the entire new bundle group.
774 re_offer->description()->contents()[0].rejected = true;
775 re_offer->description()->contents()[2].rejected = true;
776 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
777 re_offer->description()->AddGroup(new_bundle_group);
778
779 EXPECT_TRUE(caller->SetLocalDescription(std::move(re_offer)));
780}
781
782// This tests that the BUNDLE group in answer should be a subset of the offered
783// group.
784TEST_P(PeerConnectionBundleTest, AddContentToBundleGroupInAnswerNotSupported) {
785 auto caller = CreatePeerConnectionWithAudioVideo();
786 auto callee = CreatePeerConnectionWithAudioVideo();
787
788 auto offer = caller->CreateOffer();
789 std::string first_mid = offer->description()->contents()[0].name;
790 std::string second_mid = offer->description()->contents()[1].name;
791
792 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
793 bundle_group.AddContentName(first_mid);
794 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
795 offer->description()->AddGroup(bundle_group);
796 EXPECT_TRUE(
797 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
798 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
799
800 auto answer = callee->CreateAnswer();
801 bundle_group.AddContentName(second_mid);
802 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
803 answer->description()->AddGroup(bundle_group);
804
805 // The answer is expected to be rejected because second mid is not in the
806 // offered BUNDLE group.
807 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
808}
809
810// This tests that the BUNDLE group with non-existing MID should be rejectd.
811TEST_P(PeerConnectionBundleTest, RejectBundleGroupWithNonExistingMid) {
812 auto caller = CreatePeerConnectionWithAudioVideo();
813 auto callee = CreatePeerConnectionWithAudioVideo();
814
815 auto offer = caller->CreateOffer();
816 auto invalid_bundle_group =
817 *offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
818 invalid_bundle_group.AddContentName("non-existing-MID");
819 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
820 offer->description()->AddGroup(invalid_bundle_group);
821
822 EXPECT_FALSE(
823 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
824 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
825}
826
827// This tests that an answer shouldn't be able to remove an m= section from an
828// established group without rejecting it.
829TEST_P(PeerConnectionBundleTest, RemoveContentFromBundleGroup) {
830 auto caller = CreatePeerConnectionWithAudioVideo();
831 auto callee = CreatePeerConnectionWithAudioVideo();
832
833 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
834 EXPECT_TRUE(
835 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
836
837 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
838 auto answer = callee->CreateAnswer();
839 std::string second_mid = answer->description()->contents()[1].name;
840
841 auto invalid_bundle_group =
842 *answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
843 invalid_bundle_group.RemoveContentName(second_mid);
844 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
845 answer->description()->AddGroup(invalid_bundle_group);
846
847 EXPECT_FALSE(
848 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
849}
850
Steve Anton7464fca2018-01-19 11:10:37 -0800851INSTANTIATE_TEST_CASE_P(PeerConnectionBundleTest,
852 PeerConnectionBundleTest,
853 Values(SdpSemantics::kPlanB,
854 SdpSemantics::kUnifiedPlan));
855
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700856// According to RFC5888, if an endpoint understands the semantics of an
857// "a=group", it MUST return an answer with that group. So, an empty BUNDLE
858// group is valid when the answerer rejects all m= sections (by stopping all
859// transceivers), meaning there's nothing to bundle.
860//
861// Only writing this test for Unified Plan mode, since there's no way to reject
862// m= sections in answers for Plan B without SDP munging.
863TEST_F(PeerConnectionBundleTestUnifiedPlan,
864 EmptyBundleGroupCreatedInAnswerWhenAppropriate) {
865 auto caller = CreatePeerConnectionWithAudioVideo();
866 auto callee = CreatePeerConnection();
867
868 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
869
870 // Stop all transceivers, causing all m= sections to be rejected.
871 for (const auto& transceiver : callee->pc()->GetTransceivers()) {
872 transceiver->Stop();
873 }
874 EXPECT_TRUE(
875 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
876
877 // Verify that the answer actually contained an empty bundle group.
878 const SessionDescriptionInterface* desc = callee->pc()->local_description();
879 ASSERT_NE(nullptr, desc);
880 const cricket::ContentGroup* bundle_group =
881 desc->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
882 ASSERT_NE(nullptr, bundle_group);
883 EXPECT_TRUE(bundle_group->content_names().empty());
884}
885
Steve Anton6f25b092017-10-23 09:39:20 -0700886} // namespace webrtc