blob: 907669b5ad30c4866aeaac83b3c626b4076f2c42 [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"
Steve Anton6f25b092017-10-23 09:39:20 -070013#include "api/peerconnectionproxy.h"
Anders Carlsson67537952018-05-03 11:28:29 +020014#include "api/video_codecs/builtin_video_decoder_factory.h"
15#include "api/video_codecs/builtin_video_encoder_factory.h"
Steve Anton6f25b092017-10-23 09:39:20 -070016#include "p2p/base/fakeportallocator.h"
17#include "p2p/base/teststunserver.h"
18#include "p2p/client/basicportallocator.h"
19#include "pc/mediasession.h"
20#include "pc/peerconnection.h"
21#include "pc/peerconnectionwrapper.h"
22#include "pc/sdputils.h"
23#ifdef WEBRTC_ANDROID
24#include "pc/test/androidtestinitializer.h"
25#endif
Karl Wiberg918f50c2018-07-05 11:40:33 +020026#include "absl/memory/memory.h"
Steve Anton6f25b092017-10-23 09:39:20 -070027#include "pc/test/fakeaudiocapturemodule.h"
28#include "rtc_base/fakenetwork.h"
29#include "rtc_base/gunit.h"
Steve Anton6f25b092017-10-23 09:39:20 -070030#include "rtc_base/virtualsocketserver.h"
31#include "test/gmock.h"
32
33namespace webrtc {
34
35using BundlePolicy = PeerConnectionInterface::BundlePolicy;
36using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
37using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
38using RtcpMuxPolicy = PeerConnectionInterface::RtcpMuxPolicy;
39using rtc::SocketAddress;
Steve Anton7464fca2018-01-19 11:10:37 -080040using ::testing::Combine;
Steve Anton6f25b092017-10-23 09:39:20 -070041using ::testing::ElementsAre;
42using ::testing::UnorderedElementsAre;
43using ::testing::Values;
44
45constexpr int kDefaultTimeout = 10000;
46
47// TODO(steveanton): These tests should be rewritten to use the standard
48// RtpSenderInterface/DtlsTransportInterface objects once they're available in
49// the API. The RtpSender can be used to determine which transport a given media
50// will use: https://www.w3.org/TR/webrtc/#dom-rtcrtpsender-transport
Steve Anton7464fca2018-01-19 11:10:37 -080051// Should also be able to remove GetTransceiversForTesting at that point.
Steve Anton6f25b092017-10-23 09:39:20 -070052
53class PeerConnectionWrapperForBundleTest : public PeerConnectionWrapper {
54 public:
55 using PeerConnectionWrapper::PeerConnectionWrapper;
56
57 bool AddIceCandidateToMedia(cricket::Candidate* candidate,
58 cricket::MediaType media_type) {
59 auto* desc = pc()->remote_description()->description();
60 for (size_t i = 0; i < desc->contents().size(); i++) {
61 const auto& content = desc->contents()[i];
Steve Antonb1c1de12017-12-21 15:14:30 -080062 if (content.media_description()->type() == media_type) {
Steve Anton6f25b092017-10-23 09:39:20 -070063 candidate->set_transport_name(content.name);
Steve Anton27ab0e52018-07-23 15:11:53 -070064 std::unique_ptr<IceCandidateInterface> jsep_candidate =
65 CreateIceCandidate(content.name, i, *candidate);
66 return pc()->AddIceCandidate(jsep_candidate.get());
Steve Anton6f25b092017-10-23 09:39:20 -070067 }
68 }
69 RTC_NOTREACHED();
70 return false;
71 }
72
Zhi Huange830e682018-03-30 10:48:35 -070073 rtc::PacketTransportInternal* voice_rtp_transport() {
74 return (voice_channel() ? voice_channel()->rtp_packet_transport()
75 : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -070076 }
77
Zhi Huange830e682018-03-30 10:48:35 -070078 rtc::PacketTransportInternal* voice_rtcp_transport() {
79 return (voice_channel() ? voice_channel()->rtcp_packet_transport()
80 : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -070081 }
82
83 cricket::VoiceChannel* voice_channel() {
Steve Antonb8867112018-02-13 10:07:54 -080084 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
Steve Anton7464fca2018-01-19 11:10:37 -080085 for (auto transceiver : transceivers) {
Steve Anton69470252018-02-09 11:43:08 -080086 if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
Steve Anton7464fca2018-01-19 11:10:37 -080087 return static_cast<cricket::VoiceChannel*>(
88 transceiver->internal()->channel());
89 }
90 }
91 return nullptr;
Steve Anton6f25b092017-10-23 09:39:20 -070092 }
93
Zhi Huange830e682018-03-30 10:48:35 -070094 rtc::PacketTransportInternal* video_rtp_transport() {
95 return (video_channel() ? video_channel()->rtp_packet_transport()
96 : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -070097 }
98
Zhi Huange830e682018-03-30 10:48:35 -070099 rtc::PacketTransportInternal* video_rtcp_transport() {
100 return (video_channel() ? video_channel()->rtcp_packet_transport()
101 : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -0700102 }
103
104 cricket::VideoChannel* video_channel() {
Steve Antonb8867112018-02-13 10:07:54 -0800105 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
Steve Anton7464fca2018-01-19 11:10:37 -0800106 for (auto transceiver : transceivers) {
Steve Anton69470252018-02-09 11:43:08 -0800107 if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) {
Steve Anton7464fca2018-01-19 11:10:37 -0800108 return static_cast<cricket::VideoChannel*>(
109 transceiver->internal()->channel());
110 }
111 }
112 return nullptr;
Steve Anton6f25b092017-10-23 09:39:20 -0700113 }
114
115 PeerConnection* GetInternalPeerConnection() {
Mirko Bonadeie97de912017-12-13 11:29:34 +0100116 auto* pci =
117 static_cast<PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(
118 pc());
119 return static_cast<PeerConnection*>(pci->internal());
Steve Anton6f25b092017-10-23 09:39:20 -0700120 }
121
122 // Returns true if the stats indicate that an ICE connection is either in
123 // progress or established with the given remote address.
124 bool HasConnectionWithRemoteAddress(const SocketAddress& address) {
125 auto report = GetStats();
126 if (!report) {
127 return false;
128 }
129 std::string matching_candidate_id;
130 for (auto* ice_candidate_stats :
131 report->GetStatsOfType<RTCRemoteIceCandidateStats>()) {
132 if (*ice_candidate_stats->ip == address.HostAsURIString() &&
133 *ice_candidate_stats->port == address.port()) {
134 matching_candidate_id = ice_candidate_stats->id();
135 break;
136 }
137 }
138 if (matching_candidate_id.empty()) {
139 return false;
140 }
141 for (auto* pair_stats :
142 report->GetStatsOfType<RTCIceCandidatePairStats>()) {
143 if (*pair_stats->remote_candidate_id == matching_candidate_id) {
144 if (*pair_stats->state == RTCStatsIceCandidatePairState::kInProgress ||
145 *pair_stats->state == RTCStatsIceCandidatePairState::kSucceeded) {
146 return true;
147 }
148 }
149 }
150 return false;
151 }
152
153 rtc::FakeNetworkManager* network() { return network_; }
154
155 void set_network(rtc::FakeNetworkManager* network) { network_ = network; }
156
157 private:
158 rtc::FakeNetworkManager* network_;
159};
160
Steve Anton7464fca2018-01-19 11:10:37 -0800161class PeerConnectionBundleBaseTest : public ::testing::Test {
Steve Anton6f25b092017-10-23 09:39:20 -0700162 protected:
163 typedef std::unique_ptr<PeerConnectionWrapperForBundleTest> WrapperPtr;
164
Steve Anton7464fca2018-01-19 11:10:37 -0800165 explicit PeerConnectionBundleBaseTest(SdpSemantics sdp_semantics)
166 : vss_(new rtc::VirtualSocketServer()),
167 main_(vss_.get()),
168 sdp_semantics_(sdp_semantics) {
Steve Anton6f25b092017-10-23 09:39:20 -0700169#ifdef WEBRTC_ANDROID
170 InitializeAndroidObjects();
171#endif
172 pc_factory_ = CreatePeerConnectionFactory(
173 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Anders Carlsson67537952018-05-03 11:28:29 +0200174 rtc::scoped_refptr<AudioDeviceModule>(FakeAudioCaptureModule::Create()),
175 CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(),
176 CreateBuiltinVideoEncoderFactory(), CreateBuiltinVideoDecoderFactory(),
177 nullptr /* audio_mixer */, nullptr /* audio_processing */);
Steve Anton6f25b092017-10-23 09:39:20 -0700178 }
179
180 WrapperPtr CreatePeerConnection() {
181 return CreatePeerConnection(RTCConfiguration());
182 }
183
184 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
185 auto* fake_network = NewFakeNetwork();
186 auto port_allocator =
Karl Wiberg918f50c2018-07-05 11:40:33 +0200187 absl::make_unique<cricket::BasicPortAllocator>(fake_network);
Steve Anton6f25b092017-10-23 09:39:20 -0700188 port_allocator->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
189 cricket::PORTALLOCATOR_DISABLE_RELAY);
190 port_allocator->set_step_delay(cricket::kMinimumStepDelay);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200191 auto observer = absl::make_unique<MockPeerConnectionObserver>();
Steve Anton7464fca2018-01-19 11:10:37 -0800192 RTCConfiguration modified_config = config;
193 modified_config.sdp_semantics = sdp_semantics_;
Steve Anton6f25b092017-10-23 09:39:20 -0700194 auto pc = pc_factory_->CreatePeerConnection(
Steve Anton7464fca2018-01-19 11:10:37 -0800195 modified_config, std::move(port_allocator), nullptr, observer.get());
Steve Anton6f25b092017-10-23 09:39:20 -0700196 if (!pc) {
197 return nullptr;
198 }
199
Karl Wiberg918f50c2018-07-05 11:40:33 +0200200 auto wrapper = absl::make_unique<PeerConnectionWrapperForBundleTest>(
Steve Anton6f25b092017-10-23 09:39:20 -0700201 pc_factory_, pc, std::move(observer));
202 wrapper->set_network(fake_network);
203 return wrapper;
204 }
205
206 // Accepts the same arguments as CreatePeerConnection and adds default audio
207 // and video tracks.
208 template <typename... Args>
209 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
210 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
211 if (!wrapper) {
212 return nullptr;
213 }
214 wrapper->AddAudioTrack("a");
215 wrapper->AddVideoTrack("v");
216 return wrapper;
217 }
218
219 cricket::Candidate CreateLocalUdpCandidate(
220 const rtc::SocketAddress& address) {
221 cricket::Candidate candidate;
222 candidate.set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);
223 candidate.set_protocol(cricket::UDP_PROTOCOL_NAME);
224 candidate.set_address(address);
225 candidate.set_type(cricket::LOCAL_PORT_TYPE);
226 return candidate;
227 }
228
229 rtc::FakeNetworkManager* NewFakeNetwork() {
230 // The PeerConnection's port allocator is tied to the PeerConnection's
231 // lifetime and expects the underlying NetworkManager to outlive it. If
232 // PeerConnectionWrapper owned the NetworkManager, it would be destroyed
233 // before the PeerConnection (since subclass members are destroyed before
234 // base class members). Therefore, the test fixture will own all the fake
235 // networks even though tests should access the fake network through the
236 // PeerConnectionWrapper.
237 auto* fake_network = new rtc::FakeNetworkManager();
238 fake_networks_.emplace_back(fake_network);
239 return fake_network;
240 }
241
242 std::unique_ptr<rtc::VirtualSocketServer> vss_;
243 rtc::AutoSocketServerThread main_;
244 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
245 std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_networks_;
Steve Anton7464fca2018-01-19 11:10:37 -0800246 const SdpSemantics sdp_semantics_;
247};
248
249class PeerConnectionBundleTest
250 : public PeerConnectionBundleBaseTest,
251 public ::testing::WithParamInterface<SdpSemantics> {
252 protected:
253 PeerConnectionBundleTest() : PeerConnectionBundleBaseTest(GetParam()) {}
Steve Anton6f25b092017-10-23 09:39:20 -0700254};
255
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700256class PeerConnectionBundleTestUnifiedPlan
257 : public PeerConnectionBundleBaseTest {
258 protected:
259 PeerConnectionBundleTestUnifiedPlan()
260 : PeerConnectionBundleBaseTest(SdpSemantics::kUnifiedPlan) {}
261};
262
Steve Anton6f25b092017-10-23 09:39:20 -0700263SdpContentMutator RemoveRtcpMux() {
264 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800265 content->media_description()->set_rtcp_mux(false);
Steve Anton6f25b092017-10-23 09:39:20 -0700266 };
267}
268
269std::vector<int> GetCandidateComponents(
270 const std::vector<IceCandidateInterface*> candidates) {
271 std::vector<int> components;
272 for (auto* candidate : candidates) {
273 components.push_back(candidate->candidate().component());
274 }
275 return components;
276}
277
278// Test that there are 2 local UDP candidates (1 RTP and 1 RTCP candidate) for
279// each media section when disabling bundling and disabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800280TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700281 TwoCandidatesForEachTransportWhenNoBundleNoRtcpMux) {
282 const SocketAddress kCallerAddress("1.1.1.1", 0);
283 const SocketAddress kCalleeAddress("2.2.2.2", 0);
284
285 RTCConfiguration config;
286 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
287 auto caller = CreatePeerConnectionWithAudioVideo(config);
288 caller->network()->AddInterface(kCallerAddress);
289 auto callee = CreatePeerConnectionWithAudioVideo(config);
290 callee->network()->AddInterface(kCalleeAddress);
291
292 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
293 RTCOfferAnswerOptions options_no_bundle;
294 options_no_bundle.use_rtp_mux = false;
295 auto answer = callee->CreateAnswer(options_no_bundle);
296 SdpContentsForEach(RemoveRtcpMux(), answer->description());
297 ASSERT_TRUE(
298 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
299 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
300
301 // Check that caller has separate RTP and RTCP candidates for each media.
302 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
303 EXPECT_THAT(
304 GetCandidateComponents(caller->observer()->GetCandidatesByMline(0)),
305 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
306 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
307 EXPECT_THAT(
308 GetCandidateComponents(caller->observer()->GetCandidatesByMline(1)),
309 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
310 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
311
312 // Check that callee has separate RTP and RTCP candidates for each media.
313 EXPECT_TRUE_WAIT(callee->IsIceGatheringDone(), kDefaultTimeout);
314 EXPECT_THAT(
315 GetCandidateComponents(callee->observer()->GetCandidatesByMline(0)),
316 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
317 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
318 EXPECT_THAT(
319 GetCandidateComponents(callee->observer()->GetCandidatesByMline(1)),
320 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
321 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
322}
323
324// Test that there is 1 local UDP candidate for both RTP and RTCP for each media
325// section when disabling bundle but enabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800326TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700327 OneCandidateForEachTransportWhenNoBundleButRtcpMux) {
328 const SocketAddress kCallerAddress("1.1.1.1", 0);
329
330 auto caller = CreatePeerConnectionWithAudioVideo();
331 caller->network()->AddInterface(kCallerAddress);
332 auto callee = CreatePeerConnectionWithAudioVideo();
333
334 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
335 RTCOfferAnswerOptions options_no_bundle;
336 options_no_bundle.use_rtp_mux = false;
337 ASSERT_TRUE(
338 caller->SetRemoteDescription(callee->CreateAnswer(options_no_bundle)));
339
340 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
341
342 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
343 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(1).size());
344}
345
346// Test that there is 1 local UDP candidate in only the first media section when
347// bundling and enabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800348TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700349 OneCandidateOnlyOnFirstTransportWhenBundleAndRtcpMux) {
350 const SocketAddress kCallerAddress("1.1.1.1", 0);
351
352 RTCConfiguration config;
353 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
354 auto caller = CreatePeerConnectionWithAudioVideo(config);
355 caller->network()->AddInterface(kCallerAddress);
356 auto callee = CreatePeerConnectionWithAudioVideo(config);
357
358 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
359 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateAnswer()));
360
361 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
362
363 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
364 EXPECT_EQ(0u, caller->observer()->GetCandidatesByMline(1).size());
365}
366
Zhi Huange830e682018-03-30 10:48:35 -0700367// It will fail if the offerer uses the mux-BUNDLE policy but the answerer
368// doesn't support BUNDLE.
369TEST_P(PeerConnectionBundleTest, MaxBundleNotSupportedInAnswer) {
370 RTCConfiguration config;
371 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
372 auto caller = CreatePeerConnectionWithAudioVideo(config);
373 auto callee = CreatePeerConnectionWithAudioVideo();
374
375 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
376 bool equal_before =
377 (caller->voice_rtp_transport() == caller->video_rtp_transport());
378 EXPECT_EQ(true, equal_before);
379 RTCOfferAnswerOptions options;
380 options.use_rtp_mux = false;
381 EXPECT_FALSE(
382 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
383}
384
Steve Anton6f25b092017-10-23 09:39:20 -0700385// The following parameterized test verifies that an offer/answer with varying
386// bundle policies and either bundle in the answer or not will produce the
387// expected RTP transports for audio and video. In particular, for bundling we
388// care about whether they are separate transports or the same.
389
390enum class BundleIncluded { kBundleInAnswer, kBundleNotInAnswer };
391std::ostream& operator<<(std::ostream& out, BundleIncluded value) {
392 switch (value) {
393 case BundleIncluded::kBundleInAnswer:
394 return out << "bundle in answer";
395 case BundleIncluded::kBundleNotInAnswer:
396 return out << "bundle not in answer";
397 }
398 return out << "unknown";
399}
400
401class PeerConnectionBundleMatrixTest
Steve Anton7464fca2018-01-19 11:10:37 -0800402 : public PeerConnectionBundleBaseTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700403 public ::testing::WithParamInterface<
Steve Anton7464fca2018-01-19 11:10:37 -0800404 std::tuple<SdpSemantics,
405 std::tuple<BundlePolicy, BundleIncluded, bool, bool>>> {
Steve Anton6f25b092017-10-23 09:39:20 -0700406 protected:
Steve Anton7464fca2018-01-19 11:10:37 -0800407 PeerConnectionBundleMatrixTest()
408 : PeerConnectionBundleBaseTest(std::get<0>(GetParam())) {
409 auto param = std::get<1>(GetParam());
410 bundle_policy_ = std::get<0>(param);
411 bundle_included_ = std::get<1>(param);
412 expected_same_before_ = std::get<2>(param);
413 expected_same_after_ = std::get<3>(param);
Steve Anton6f25b092017-10-23 09:39:20 -0700414 }
415
416 PeerConnectionInterface::BundlePolicy bundle_policy_;
417 BundleIncluded bundle_included_;
418 bool expected_same_before_;
419 bool expected_same_after_;
420};
421
422TEST_P(PeerConnectionBundleMatrixTest,
423 VerifyTransportsBeforeAndAfterSettingRemoteAnswer) {
424 RTCConfiguration config;
425 config.bundle_policy = bundle_policy_;
426 auto caller = CreatePeerConnectionWithAudioVideo(config);
427 auto callee = CreatePeerConnectionWithAudioVideo();
428
429 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
Zhi Huange830e682018-03-30 10:48:35 -0700430 bool equal_before =
431 (caller->voice_rtp_transport() == caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700432 EXPECT_EQ(expected_same_before_, equal_before);
433
434 RTCOfferAnswerOptions options;
435 options.use_rtp_mux = (bundle_included_ == BundleIncluded::kBundleInAnswer);
436 ASSERT_TRUE(
437 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
Zhi Huange830e682018-03-30 10:48:35 -0700438 bool equal_after =
439 (caller->voice_rtp_transport() == caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700440 EXPECT_EQ(expected_same_after_, equal_after);
441}
442
443// The max-bundle policy means we should anticipate bundling being negotiated,
444// and multiplex audio/video from the start.
445// For all other policies, bundling should only be enabled if negotiated by the
446// answer.
447INSTANTIATE_TEST_CASE_P(
448 PeerConnectionBundleTest,
449 PeerConnectionBundleMatrixTest,
Steve Anton7464fca2018-01-19 11:10:37 -0800450 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
451 Values(std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
452 BundleIncluded::kBundleInAnswer,
453 false,
454 true),
455 std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
456 BundleIncluded::kBundleNotInAnswer,
457 false,
458 false),
459 std::make_tuple(BundlePolicy::kBundlePolicyMaxBundle,
460 BundleIncluded::kBundleInAnswer,
461 true,
462 true),
Steve Anton7464fca2018-01-19 11:10:37 -0800463 std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
464 BundleIncluded::kBundleInAnswer,
465 false,
466 true),
467 std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
468 BundleIncluded::kBundleNotInAnswer,
469 false,
470 false))));
Steve Anton6f25b092017-10-23 09:39:20 -0700471
472// Test that the audio/video transports on the callee side are the same before
473// and after setting a local answer when max BUNDLE is enabled and an offer with
474// BUNDLE is received.
Steve Anton7464fca2018-01-19 11:10:37 -0800475TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700476 TransportsSameForMaxBundleWithBundleInRemoteOffer) {
477 auto caller = CreatePeerConnectionWithAudioVideo();
478 RTCConfiguration config;
479 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
480 auto callee = CreatePeerConnectionWithAudioVideo(config);
481
482 RTCOfferAnswerOptions options_with_bundle;
483 options_with_bundle.use_rtp_mux = true;
484 ASSERT_TRUE(callee->SetRemoteDescription(
485 caller->CreateOfferAndSetAsLocal(options_with_bundle)));
486
Zhi Huange830e682018-03-30 10:48:35 -0700487 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700488
489 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
490
Zhi Huange830e682018-03-30 10:48:35 -0700491 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700492}
493
Steve Anton7464fca2018-01-19 11:10:37 -0800494TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700495 FailToSetRemoteOfferWithNoBundleWhenBundlePolicyMaxBundle) {
496 auto caller = CreatePeerConnectionWithAudioVideo();
497 RTCConfiguration config;
498 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
499 auto callee = CreatePeerConnectionWithAudioVideo(config);
500
501 RTCOfferAnswerOptions options_no_bundle;
502 options_no_bundle.use_rtp_mux = false;
503 EXPECT_FALSE(callee->SetRemoteDescription(
504 caller->CreateOfferAndSetAsLocal(options_no_bundle)));
505}
506
507// Test that if the media section which has the bundled transport is rejected,
508// then the peers still connect and the bundled transport switches to the other
509// media section.
510// Note: This is currently failing because of the following bug:
511// https://bugs.chromium.org/p/webrtc/issues/detail?id=6280
Steve Anton7464fca2018-01-19 11:10:37 -0800512TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700513 DISABLED_SuccessfullyNegotiateMaxBundleIfBundleTransportMediaRejected) {
514 RTCConfiguration config;
515 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
516 auto caller = CreatePeerConnectionWithAudioVideo(config);
517 auto callee = CreatePeerConnection();
518 callee->AddVideoTrack("v");
519
520 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
521
522 RTCOfferAnswerOptions options;
523 options.offer_to_receive_audio = 0;
524 ASSERT_TRUE(
525 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
526
Zhi Huange830e682018-03-30 10:48:35 -0700527 EXPECT_FALSE(caller->voice_rtp_transport());
528 EXPECT_TRUE(caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700529}
530
531// When requiring RTCP multiplexing, the PeerConnection never makes RTCP
532// transport channels.
Steve Anton7464fca2018-01-19 11:10:37 -0800533TEST_P(PeerConnectionBundleTest, NeverCreateRtcpTransportWithRtcpMuxRequired) {
Steve Anton6f25b092017-10-23 09:39:20 -0700534 RTCConfiguration config;
535 config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyRequire;
536 auto caller = CreatePeerConnectionWithAudioVideo(config);
537 auto callee = CreatePeerConnectionWithAudioVideo();
538
539 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
540
Zhi Huange830e682018-03-30 10:48:35 -0700541 EXPECT_FALSE(caller->voice_rtcp_transport());
542 EXPECT_FALSE(caller->video_rtcp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700543
544 ASSERT_TRUE(
545 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
546
Zhi Huange830e682018-03-30 10:48:35 -0700547 EXPECT_FALSE(caller->voice_rtcp_transport());
548 EXPECT_FALSE(caller->video_rtcp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700549}
550
Zhi Huange830e682018-03-30 10:48:35 -0700551// When negotiating RTCP multiplexing, the PeerConnection makes RTCP transports
552// when the offer is sent, but will destroy them once the remote answer is set.
Steve Anton7464fca2018-01-19 11:10:37 -0800553TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700554 CreateRtcpTransportOnlyBeforeAnswerWithRtcpMuxNegotiate) {
555 RTCConfiguration config;
556 config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyNegotiate;
557 auto caller = CreatePeerConnectionWithAudioVideo(config);
558 auto callee = CreatePeerConnectionWithAudioVideo();
559
560 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
561
Zhi Huange830e682018-03-30 10:48:35 -0700562 EXPECT_TRUE(caller->voice_rtcp_transport());
563 EXPECT_TRUE(caller->video_rtcp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700564
565 ASSERT_TRUE(
566 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
567
Zhi Huange830e682018-03-30 10:48:35 -0700568 EXPECT_FALSE(caller->voice_rtcp_transport());
569 EXPECT_FALSE(caller->video_rtcp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700570}
571
Steve Anton7464fca2018-01-19 11:10:37 -0800572TEST_P(PeerConnectionBundleTest, FailToSetDescriptionWithBundleAndNoRtcpMux) {
Steve Anton6f25b092017-10-23 09:39:20 -0700573 auto caller = CreatePeerConnectionWithAudioVideo();
574 auto callee = CreatePeerConnectionWithAudioVideo();
575
576 RTCOfferAnswerOptions options;
577 options.use_rtp_mux = true;
578
579 auto offer = caller->CreateOffer(options);
580 SdpContentsForEach(RemoveRtcpMux(), offer->description());
581
582 std::string error;
583 EXPECT_FALSE(caller->SetLocalDescription(CloneSessionDescription(offer.get()),
584 &error));
585 EXPECT_EQ(
586 "Failed to set local offer sdp: rtcp-mux must be enabled when BUNDLE is "
587 "enabled.",
588 error);
589
590 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer), &error));
591 EXPECT_EQ(
592 "Failed to set remote offer sdp: rtcp-mux must be enabled when BUNDLE is "
593 "enabled.",
594 error);
595}
596
597// Test that candidates sent to the "video" transport do not get pushed down to
598// the "audio" transport channel when bundling.
Steve Anton7464fca2018-01-19 11:10:37 -0800599TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700600 IgnoreCandidatesForUnusedTransportWhenBundling) {
601 const SocketAddress kAudioAddress1("1.1.1.1", 1111);
602 const SocketAddress kAudioAddress2("2.2.2.2", 2222);
603 const SocketAddress kVideoAddress("3.3.3.3", 3333);
604 const SocketAddress kCallerAddress("4.4.4.4", 0);
605 const SocketAddress kCalleeAddress("5.5.5.5", 0);
606
607 auto caller = CreatePeerConnectionWithAudioVideo();
608 auto callee = CreatePeerConnectionWithAudioVideo();
609
610 caller->network()->AddInterface(kCallerAddress);
611 callee->network()->AddInterface(kCalleeAddress);
612
613 RTCOfferAnswerOptions options;
614 options.use_rtp_mux = true;
615
616 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
617 ASSERT_TRUE(
Zhi Huange830e682018-03-30 10:48:35 -0700618 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
Steve Anton6f25b092017-10-23 09:39:20 -0700619
620 // The way the *_WAIT checks work is they only wait if the condition fails,
621 // which does not help in the case where state is not changing. This is
622 // problematic in this test since we want to verify that adding a video
623 // candidate does _not_ change state. So we interleave candidates and assume
624 // that messages are executed in the order they were posted.
625
626 cricket::Candidate audio_candidate1 = CreateLocalUdpCandidate(kAudioAddress1);
627 ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate1,
628 cricket::MEDIA_TYPE_AUDIO));
629
630 cricket::Candidate video_candidate = CreateLocalUdpCandidate(kVideoAddress);
631 ASSERT_TRUE(caller->AddIceCandidateToMedia(&video_candidate,
632 cricket::MEDIA_TYPE_VIDEO));
633
634 cricket::Candidate audio_candidate2 = CreateLocalUdpCandidate(kAudioAddress2);
635 ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate2,
636 cricket::MEDIA_TYPE_AUDIO));
637
638 EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress1),
639 kDefaultTimeout);
640 EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress2),
641 kDefaultTimeout);
642 EXPECT_FALSE(caller->HasConnectionWithRemoteAddress(kVideoAddress));
643}
644
645// Test that the transport used by both audio and video is the transport
646// associated with the first MID in the answer BUNDLE group, even if it's in a
647// different order from the offer.
Steve Anton7464fca2018-01-19 11:10:37 -0800648TEST_P(PeerConnectionBundleTest, BundleOnFirstMidInAnswer) {
Steve Anton6f25b092017-10-23 09:39:20 -0700649 auto caller = CreatePeerConnectionWithAudioVideo();
650 auto callee = CreatePeerConnectionWithAudioVideo();
651
652 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
653
Zhi Huange830e682018-03-30 10:48:35 -0700654 auto* old_video_transport = caller->video_rtp_transport();
Steve Anton6f25b092017-10-23 09:39:20 -0700655
656 auto answer = callee->CreateAnswer();
657 auto* old_bundle_group =
658 answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
Steve Anton7464fca2018-01-19 11:10:37 -0800659 std::string first_mid = old_bundle_group->content_names()[0];
660 std::string second_mid = old_bundle_group->content_names()[1];
Steve Anton6f25b092017-10-23 09:39:20 -0700661 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
662
663 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
Steve Anton7464fca2018-01-19 11:10:37 -0800664 new_bundle_group.AddContentName(second_mid);
665 new_bundle_group.AddContentName(first_mid);
Steve Anton6f25b092017-10-23 09:39:20 -0700666 answer->description()->AddGroup(new_bundle_group);
667
668 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
669
Zhi Huange830e682018-03-30 10:48:35 -0700670 EXPECT_EQ(old_video_transport, caller->video_rtp_transport());
671 EXPECT_EQ(caller->voice_rtp_transport(), caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700672}
673
Zhi Huang365381f2018-04-13 16:44:34 -0700674// This tests that applying description with conflicted RTP demuxing criteria
675// will fail.
676TEST_P(PeerConnectionBundleTest,
677 ApplyDescriptionWithConflictedDemuxCriteriaFail) {
678 auto caller = CreatePeerConnectionWithAudioVideo();
679 auto callee = CreatePeerConnectionWithAudioVideo();
680
681 RTCOfferAnswerOptions options;
682 options.use_rtp_mux = false;
683 auto offer = caller->CreateOffer(options);
684 // Modified the SDP to make two m= sections have the same SSRC.
685 ASSERT_GE(offer->description()->contents().size(), 2U);
686 offer->description()
687 ->contents()[0]
688 .description->mutable_streams()[0]
689 .ssrcs[0] = 1111222;
690 offer->description()
691 ->contents()[1]
692 .description->mutable_streams()[0]
693 .ssrcs[0] = 1111222;
694 EXPECT_TRUE(
695 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
696 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
697 EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal(options));
698
699 // Enable BUNDLE in subsequent offer/answer exchange and two m= sections are
700 // expectd to use one RtpTransport underneath.
701 options.use_rtp_mux = true;
702 EXPECT_TRUE(
703 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal(options)));
704 auto answer = callee->CreateAnswer(options);
705 // When BUNDLE is enabled, applying the description is expected to fail
706 // because the demuxing criteria is conflicted.
707 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
708}
709
Zhi Huangd2248f82018-04-10 14:41:03 -0700710// This tests that changing the pre-negotiated BUNDLE tag is not supported.
711TEST_P(PeerConnectionBundleTest, RejectDescriptionChangingBundleTag) {
712 RTCConfiguration config;
713 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
714 auto caller = CreatePeerConnectionWithAudioVideo(config);
715 auto callee = CreatePeerConnectionWithAudioVideo(config);
716
717 RTCOfferAnswerOptions options;
718 options.use_rtp_mux = true;
719 auto offer = caller->CreateOfferAndSetAsLocal(options);
720
721 // Create a new bundle-group with different bundled_mid.
722 auto* old_bundle_group =
723 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
724 std::string first_mid = old_bundle_group->content_names()[0];
725 std::string second_mid = old_bundle_group->content_names()[1];
726 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
727 new_bundle_group.AddContentName(second_mid);
728
729 auto re_offer = CloneSessionDescription(offer.get());
730 callee->SetRemoteDescription(std::move(offer));
731 auto answer = callee->CreateAnswer(options);
732 // Reject the first MID.
733 answer->description()->contents()[0].rejected = true;
734 // Remove the first MID from the bundle group.
735 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
736 answer->description()->AddGroup(new_bundle_group);
737 // The answer is expected to be rejected.
738 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
739
740 // Do the same thing for re-offer.
741 re_offer->description()->contents()[0].rejected = true;
742 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
743 re_offer->description()->AddGroup(new_bundle_group);
744 // The re-offer is expected to be rejected.
745 EXPECT_FALSE(caller->SetLocalDescription(std::move(re_offer)));
746}
747
748// This tests that removing contents from BUNDLE group and reject the whole
749// BUNDLE group could work. This is a regression test for
750// (https://bugs.chromium.org/p/chromium/issues/detail?id=827917)
751TEST_P(PeerConnectionBundleTest, RemovingContentAndRejectBundleGroup) {
752 RTCConfiguration config;
753#ifndef HAVE_SCTP
754 config.enable_rtp_data_channel = true;
755#endif
756 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
757 auto caller = CreatePeerConnectionWithAudioVideo(config);
758 caller->CreateDataChannel("dc");
759
760 auto offer = caller->CreateOfferAndSetAsLocal();
761 auto re_offer = CloneSessionDescription(offer.get());
762
763 // Removing the second MID from the BUNDLE group.
764 auto* old_bundle_group =
765 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
766 std::string first_mid = old_bundle_group->content_names()[0];
767 std::string third_mid = old_bundle_group->content_names()[2];
768 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
769 new_bundle_group.AddContentName(first_mid);
770 new_bundle_group.AddContentName(third_mid);
771
772 // Reject the entire new bundle group.
773 re_offer->description()->contents()[0].rejected = true;
774 re_offer->description()->contents()[2].rejected = true;
775 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
776 re_offer->description()->AddGroup(new_bundle_group);
777
778 EXPECT_TRUE(caller->SetLocalDescription(std::move(re_offer)));
779}
780
781// This tests that the BUNDLE group in answer should be a subset of the offered
782// group.
783TEST_P(PeerConnectionBundleTest, AddContentToBundleGroupInAnswerNotSupported) {
784 auto caller = CreatePeerConnectionWithAudioVideo();
785 auto callee = CreatePeerConnectionWithAudioVideo();
786
787 auto offer = caller->CreateOffer();
788 std::string first_mid = offer->description()->contents()[0].name;
789 std::string second_mid = offer->description()->contents()[1].name;
790
791 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
792 bundle_group.AddContentName(first_mid);
793 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
794 offer->description()->AddGroup(bundle_group);
795 EXPECT_TRUE(
796 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
797 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
798
799 auto answer = callee->CreateAnswer();
800 bundle_group.AddContentName(second_mid);
801 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
802 answer->description()->AddGroup(bundle_group);
803
804 // The answer is expected to be rejected because second mid is not in the
805 // offered BUNDLE group.
806 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
807}
808
809// This tests that the BUNDLE group with non-existing MID should be rejectd.
810TEST_P(PeerConnectionBundleTest, RejectBundleGroupWithNonExistingMid) {
811 auto caller = CreatePeerConnectionWithAudioVideo();
812 auto callee = CreatePeerConnectionWithAudioVideo();
813
814 auto offer = caller->CreateOffer();
815 auto invalid_bundle_group =
816 *offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
817 invalid_bundle_group.AddContentName("non-existing-MID");
818 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
819 offer->description()->AddGroup(invalid_bundle_group);
820
821 EXPECT_FALSE(
822 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
823 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
824}
825
826// This tests that an answer shouldn't be able to remove an m= section from an
827// established group without rejecting it.
828TEST_P(PeerConnectionBundleTest, RemoveContentFromBundleGroup) {
829 auto caller = CreatePeerConnectionWithAudioVideo();
830 auto callee = CreatePeerConnectionWithAudioVideo();
831
832 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
833 EXPECT_TRUE(
834 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
835
836 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
837 auto answer = callee->CreateAnswer();
838 std::string second_mid = answer->description()->contents()[1].name;
839
840 auto invalid_bundle_group =
841 *answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
842 invalid_bundle_group.RemoveContentName(second_mid);
843 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
844 answer->description()->AddGroup(invalid_bundle_group);
845
846 EXPECT_FALSE(
847 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
848}
849
Steve Anton7464fca2018-01-19 11:10:37 -0800850INSTANTIATE_TEST_CASE_P(PeerConnectionBundleTest,
851 PeerConnectionBundleTest,
852 Values(SdpSemantics::kPlanB,
853 SdpSemantics::kUnifiedPlan));
854
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700855// According to RFC5888, if an endpoint understands the semantics of an
856// "a=group", it MUST return an answer with that group. So, an empty BUNDLE
857// group is valid when the answerer rejects all m= sections (by stopping all
858// transceivers), meaning there's nothing to bundle.
859//
860// Only writing this test for Unified Plan mode, since there's no way to reject
861// m= sections in answers for Plan B without SDP munging.
862TEST_F(PeerConnectionBundleTestUnifiedPlan,
863 EmptyBundleGroupCreatedInAnswerWhenAppropriate) {
864 auto caller = CreatePeerConnectionWithAudioVideo();
865 auto callee = CreatePeerConnection();
866
867 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
868
869 // Stop all transceivers, causing all m= sections to be rejected.
870 for (const auto& transceiver : callee->pc()->GetTransceivers()) {
871 transceiver->Stop();
872 }
873 EXPECT_TRUE(
874 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
875
876 // Verify that the answer actually contained an empty bundle group.
877 const SessionDescriptionInterface* desc = callee->pc()->local_description();
878 ASSERT_NE(nullptr, desc);
879 const cricket::ContentGroup* bundle_group =
880 desc->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
881 ASSERT_NE(nullptr, bundle_group);
882 EXPECT_TRUE(bundle_group->content_names().empty());
883}
884
Steve Anton6f25b092017-10-23 09:39:20 -0700885} // namespace webrtc