blob: c406ad8249ccd7a6357105ff8d5a386c5e9a9b55 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellanderb24317b2016-02-10 07:54:43 -08002 * Copyright 2012 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellanderb24317b2016-02-10 07:54:43 -08004 * 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.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
kwibergd1fe2812016-04-27 06:47:29 -070011#include <memory>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012#include <string>
13
Henrik Kjellander15583c12016-02-10 10:53:12 +010014#include "webrtc/api/jsepicecandidate.h"
15#include "webrtc/api/jsepsessiondescription.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000016#include "webrtc/base/gunit.h"
17#include "webrtc/base/helpers.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000018#include "webrtc/base/ssladapter.h"
19#include "webrtc/base/stringencode.h"
kjellandera96e2d72016-02-04 23:52:28 -080020#include "webrtc/p2p/base/candidate.h"
kjellanderf4752772016-03-02 05:42:30 -080021#include "webrtc/p2p/base/p2pconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080022#include "webrtc/p2p/base/sessiondescription.h"
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010023#include "webrtc/pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000024
25using webrtc::IceCandidateCollection;
26using webrtc::IceCandidateInterface;
27using webrtc::JsepIceCandidate;
28using webrtc::JsepSessionDescription;
29using webrtc::SessionDescriptionInterface;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000030
31static const char kCandidateUfrag[] = "ufrag";
32static const char kCandidatePwd[] = "pwd";
33static const char kCandidateUfragVoice[] = "ufrag_voice";
34static const char kCandidatePwdVoice[] = "pwd_voice";
35static const char kCandidateUfragVideo[] = "ufrag_video";
36static const char kCandidatePwdVideo[] = "pwd_video";
zhihuang38989e52017-03-21 11:04:53 -070037static const char kCandidateFoundation[] = "a0+B/1";
38static const uint32_t kCandidatePriority = 2130706432U; // pref = 1.0
39static const uint32_t kCandidateGeneration = 2;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000040
41// This creates a session description with both audio and video media contents.
42// In SDP this is described by two m lines, one audio and one video.
43static cricket::SessionDescription* CreateCricketSessionDescription() {
44 cricket::SessionDescription* desc(new cricket::SessionDescription());
45 // AudioContentDescription
kwibergd1fe2812016-04-27 06:47:29 -070046 std::unique_ptr<cricket::AudioContentDescription> audio(
henrike@webrtc.org28e20752013-07-10 00:45:36 +000047 new cricket::AudioContentDescription());
48
49 // VideoContentDescription
kwibergd1fe2812016-04-27 06:47:29 -070050 std::unique_ptr<cricket::VideoContentDescription> video(
henrike@webrtc.org28e20752013-07-10 00:45:36 +000051 new cricket::VideoContentDescription());
52
deadbeef67cf2c12016-04-13 10:07:16 -070053 audio->AddCodec(cricket::AudioCodec(103, "ISAC", 16000, 0, 0));
henrike@webrtc.org28e20752013-07-10 00:45:36 +000054 desc->AddContent(cricket::CN_AUDIO, cricket::NS_JINGLE_RTP,
55 audio.release());
56
perkj26752742016-10-24 01:21:16 -070057 video->AddCodec(cricket::VideoCodec(120, "VP8"));
henrike@webrtc.org28e20752013-07-10 00:45:36 +000058 desc->AddContent(cricket::CN_VIDEO, cricket::NS_JINGLE_RTP,
59 video.release());
60
deadbeef46eed762016-01-28 13:24:37 -080061 EXPECT_TRUE(desc->AddTransportInfo(cricket::TransportInfo(
62 cricket::CN_AUDIO,
63 cricket::TransportDescription(
64 std::vector<std::string>(), kCandidateUfragVoice, kCandidatePwdVoice,
65 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE, NULL))));
66 EXPECT_TRUE(desc->AddTransportInfo(cricket::TransportInfo(
67 cricket::CN_VIDEO,
68 cricket::TransportDescription(
69 std::vector<std::string>(), kCandidateUfragVideo, kCandidatePwdVideo,
70 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE, NULL))));
henrike@webrtc.org28e20752013-07-10 00:45:36 +000071 return desc;
72}
73
74class JsepSessionDescriptionTest : public testing::Test {
75 protected:
henrike@webrtc.org28e20752013-07-10 00:45:36 +000076 virtual void SetUp() {
77 int port = 1234;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000078 rtc::SocketAddress address("127.0.0.1", port++);
guoweis@webrtc.org61c12472015-01-15 06:53:07 +000079 cricket::Candidate candidate(cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
80 address, 1, "", "", "local", 0, "1");
henrike@webrtc.org28e20752013-07-10 00:45:36 +000081 candidate_ = candidate;
82 const std::string session_id =
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000083 rtc::ToString(rtc::CreateRandomId64());
henrike@webrtc.org28e20752013-07-10 00:45:36 +000084 const std::string session_version =
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000085 rtc::ToString(rtc::CreateRandomId());
henrike@webrtc.org28e20752013-07-10 00:45:36 +000086 jsep_desc_.reset(new JsepSessionDescription("dummy"));
87 ASSERT_TRUE(jsep_desc_->Initialize(CreateCricketSessionDescription(),
88 session_id, session_version));
89 }
90
91 std::string Serialize(const SessionDescriptionInterface* desc) {
92 std::string sdp;
93 EXPECT_TRUE(desc->ToString(&sdp));
94 EXPECT_FALSE(sdp.empty());
95 return sdp;
96 }
97
98 SessionDescriptionInterface* DeSerialize(const std::string& sdp) {
99 JsepSessionDescription* desc(new JsepSessionDescription("dummy"));
100 EXPECT_TRUE(desc->Initialize(sdp, NULL));
101 return desc;
102 }
103
104 cricket::Candidate candidate_;
kwibergd1fe2812016-04-27 06:47:29 -0700105 std::unique_ptr<JsepSessionDescription> jsep_desc_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000106};
107
108// Test that number_of_mediasections() returns the number of media contents in
109// a session description.
110TEST_F(JsepSessionDescriptionTest, CheckSessionDescription) {
111 EXPECT_EQ(2u, jsep_desc_->number_of_mediasections());
112}
113
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700114// Test that we can add a candidate to a session description without MID.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000115TEST_F(JsepSessionDescriptionTest, AddCandidateWithoutMid) {
116 JsepIceCandidate jsep_candidate("", 0, candidate_);
117 EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
118 const IceCandidateCollection* ice_candidates = jsep_desc_->candidates(0);
119 ASSERT_TRUE(ice_candidates != NULL);
120 EXPECT_EQ(1u, ice_candidates->count());
121 const IceCandidateInterface* ice_candidate = ice_candidates->at(0);
122 ASSERT_TRUE(ice_candidate != NULL);
123 candidate_.set_username(kCandidateUfragVoice);
124 candidate_.set_password(kCandidatePwdVoice);
125 EXPECT_TRUE(ice_candidate->candidate().IsEquivalent(candidate_));
126 EXPECT_EQ(0, ice_candidate->sdp_mline_index());
127 EXPECT_EQ(0u, jsep_desc_->candidates(1)->count());
128}
129
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700130// Test that we can add and remove candidates to a session description with
131// MID. Removing candidates requires MID (transport_name).
132TEST_F(JsepSessionDescriptionTest, AddAndRemoveCandidatesWithMid) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000133 // mid and m-line index don't match, in this case mid is preferred.
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700134 std::string mid = "video";
135 JsepIceCandidate jsep_candidate(mid, 0, candidate_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000136 EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
137 EXPECT_EQ(0u, jsep_desc_->candidates(0)->count());
138 const IceCandidateCollection* ice_candidates = jsep_desc_->candidates(1);
139 ASSERT_TRUE(ice_candidates != NULL);
140 EXPECT_EQ(1u, ice_candidates->count());
141 const IceCandidateInterface* ice_candidate = ice_candidates->at(0);
142 ASSERT_TRUE(ice_candidate != NULL);
143 candidate_.set_username(kCandidateUfragVideo);
144 candidate_.set_password(kCandidatePwdVideo);
145 EXPECT_TRUE(ice_candidate->candidate().IsEquivalent(candidate_));
146 // The mline index should have been updated according to mid.
147 EXPECT_EQ(1, ice_candidate->sdp_mline_index());
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700148
149 std::vector<cricket::Candidate> candidates(1, candidate_);
150 candidates[0].set_transport_name(mid);
151 EXPECT_EQ(1u, jsep_desc_->RemoveCandidates(candidates));
152 EXPECT_EQ(0u, jsep_desc_->candidates(0)->count());
153 EXPECT_EQ(0u, jsep_desc_->candidates(1)->count());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000154}
155
156TEST_F(JsepSessionDescriptionTest, AddCandidateAlreadyHasUfrag) {
157 candidate_.set_username(kCandidateUfrag);
158 candidate_.set_password(kCandidatePwd);
159 JsepIceCandidate jsep_candidate("audio", 0, candidate_);
160 EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
161 const IceCandidateCollection* ice_candidates = jsep_desc_->candidates(0);
162 ASSERT_TRUE(ice_candidates != NULL);
163 EXPECT_EQ(1u, ice_candidates->count());
164 const IceCandidateInterface* ice_candidate = ice_candidates->at(0);
165 ASSERT_TRUE(ice_candidate != NULL);
166 candidate_.set_username(kCandidateUfrag);
167 candidate_.set_password(kCandidatePwd);
168 EXPECT_TRUE(ice_candidate->candidate().IsEquivalent(candidate_));
169
170 EXPECT_EQ(0u, jsep_desc_->candidates(1)->count());
171}
172
173// Test that we can not add a candidate if there is no corresponding media
174// content in the session description.
175TEST_F(JsepSessionDescriptionTest, AddBadCandidate) {
176 JsepIceCandidate bad_candidate1("", 55, candidate_);
177 EXPECT_FALSE(jsep_desc_->AddCandidate(&bad_candidate1));
178
179 JsepIceCandidate bad_candidate2("some weird mid", 0, candidate_);
180 EXPECT_FALSE(jsep_desc_->AddCandidate(&bad_candidate2));
181}
182
mallinath@webrtc.org67ee6b92014-02-03 16:57:16 +0000183// Tests that repeatedly adding the same candidate, with or without credentials,
184// does not increase the number of candidates in the description.
185TEST_F(JsepSessionDescriptionTest, AddCandidateDuplicates) {
186 JsepIceCandidate jsep_candidate("", 0, candidate_);
187 EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
188 EXPECT_EQ(1u, jsep_desc_->candidates(0)->count());
189
190 // Add the same candidate again. It should be ignored.
191 EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
192 EXPECT_EQ(1u, jsep_desc_->candidates(0)->count());
193
194 // Create a new candidate, identical except that the ufrag and pwd are now
195 // populated.
196 candidate_.set_username(kCandidateUfragVoice);
197 candidate_.set_password(kCandidatePwdVoice);
198 JsepIceCandidate jsep_candidate_with_credentials("", 0, candidate_);
199
200 // This should also be identified as redundant and ignored.
201 EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate_with_credentials));
202 EXPECT_EQ(1u, jsep_desc_->candidates(0)->count());
203}
204
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000205// Test that we can serialize a JsepSessionDescription and deserialize it again.
206TEST_F(JsepSessionDescriptionTest, SerializeDeserialize) {
207 std::string sdp = Serialize(jsep_desc_.get());
208
kwibergd1fe2812016-04-27 06:47:29 -0700209 std::unique_ptr<SessionDescriptionInterface> parsed_jsep_desc(
210 DeSerialize(sdp));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000211 EXPECT_EQ(2u, parsed_jsep_desc->number_of_mediasections());
212
213 std::string parsed_sdp = Serialize(parsed_jsep_desc.get());
214 EXPECT_EQ(sdp, parsed_sdp);
215}
216
217// Tests that we can serialize and deserialize a JsepSesssionDescription
218// with candidates.
219TEST_F(JsepSessionDescriptionTest, SerializeDeserializeWithCandidates) {
220 std::string sdp = Serialize(jsep_desc_.get());
221
222 // Add a candidate and check that the serialized result is different.
223 JsepIceCandidate jsep_candidate("audio", 0, candidate_);
224 EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
225 std::string sdp_with_candidate = Serialize(jsep_desc_.get());
226 EXPECT_NE(sdp, sdp_with_candidate);
227
kwibergd1fe2812016-04-27 06:47:29 -0700228 std::unique_ptr<SessionDescriptionInterface> parsed_jsep_desc(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000229 DeSerialize(sdp_with_candidate));
230 std::string parsed_sdp_with_candidate = Serialize(parsed_jsep_desc.get());
231
232 EXPECT_EQ(sdp_with_candidate, parsed_sdp_with_candidate);
233}
zhihuang38989e52017-03-21 11:04:53 -0700234
235// TODO(zhihuang): Modify these tests. These are used to verify that after
236// adding the candidates, the connection_address field is set correctly. Modify
237// those so that the "connection address" is tested directly.
238// Tests serialization of SDP with only IPv6 candidates and verifies that IPv6
239// is used as default address in c line according to preference.
240TEST_F(JsepSessionDescriptionTest, SerializeSessionDescriptionWithIPv6Only) {
241 // Stun has a high preference than local host.
242 cricket::Candidate candidate1(
243 cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
244 rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
245 cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
246 cricket::Candidate candidate2(
247 cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
248 rtc::SocketAddress("::2", 1235), kCandidatePriority, "", "",
249 cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
250
251 JsepIceCandidate jice1("audio", 0, candidate1);
252 JsepIceCandidate jice2("audio", 0, candidate2);
253 JsepIceCandidate jice3("video", 0, candidate1);
254 JsepIceCandidate jice4("video", 0, candidate2);
255 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
256 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
257 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
258 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice4));
259 std::string message = Serialize(jsep_desc_.get());
260
261 // Should have a c line like this one.
262 EXPECT_NE(message.find("c=IN IP6 ::1"), std::string::npos);
263 // Shouldn't have a IP4 c line.
264 EXPECT_EQ(message.find("c=IN IP4"), std::string::npos);
265}
266
267// Tests serialization of SDP with both IPv4 and IPv6 candidates and
268// verifies that IPv4 is used as default address in c line even if the
269// preference of IPv4 is lower.
270TEST_F(JsepSessionDescriptionTest,
271 SerializeSessionDescriptionWithBothIPFamilies) {
272 cricket::Candidate candidate_v4(
273 cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
274 rtc::SocketAddress("192.168.1.5", 1234), kCandidatePriority, "", "",
275 cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
276 cricket::Candidate candidate_v6(
277 cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
278 rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
279 cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
280
281 JsepIceCandidate jice_v4("audio", 0, candidate_v4);
282 JsepIceCandidate jice_v6("audio", 0, candidate_v6);
283 JsepIceCandidate jice_v4_video("video", 0, candidate_v4);
284 JsepIceCandidate jice_v6_video("video", 0, candidate_v6);
285 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v4));
286 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v6));
287 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v4_video));
288 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v6_video));
289 std::string message = Serialize(jsep_desc_.get());
290
291 // Should have a c line like this one.
292 EXPECT_NE(message.find("c=IN IP4 192.168.1.5"), std::string::npos);
293 // Shouldn't have a IP6 c line.
294 EXPECT_EQ(message.find("c=IN IP6"), std::string::npos);
295}
296
297// Tests serialization of SDP with both UDP and TCP candidates and
298// verifies that UDP is used as default address in c line even if the
299// preference of UDP is lower.
300TEST_F(JsepSessionDescriptionTest,
301 SerializeSessionDescriptionWithBothProtocols) {
302 // Stun has a high preference than local host.
303 cricket::Candidate candidate1(
304 cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
305 rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
306 cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
307 cricket::Candidate candidate2(
308 cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
309 rtc::SocketAddress("fe80::1234:5678:abcd:ef12", 1235), kCandidatePriority,
310 "", "", cricket::LOCAL_PORT_TYPE, kCandidateGeneration,
311 kCandidateFoundation);
312
313 JsepIceCandidate jice1("audio", 0, candidate1);
314 JsepIceCandidate jice2("audio", 0, candidate2);
315 JsepIceCandidate jice3("video", 0, candidate1);
316 JsepIceCandidate jice4("video", 0, candidate2);
317 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
318 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
319 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
320 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice4));
321 std::string message = Serialize(jsep_desc_.get());
322
323 // Should have a c line like this one.
324 EXPECT_NE(message.find("c=IN IP6 fe80::1234:5678:abcd:ef12"),
325 std::string::npos);
326 // Shouldn't have a IP4 c line.
327 EXPECT_EQ(message.find("c=IN IP4"), std::string::npos);
328}
329
330// Tests serialization of SDP with only TCP candidates and verifies that
331// null IPv4 is used as default address in c line.
332TEST_F(JsepSessionDescriptionTest, SerializeSessionDescriptionWithTCPOnly) {
333 // Stun has a high preference than local host.
334 cricket::Candidate candidate1(
335 cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
336 rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
337 cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
338 cricket::Candidate candidate2(
339 cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
340 rtc::SocketAddress("::2", 1235), kCandidatePriority, "", "",
341 cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
342
343 JsepIceCandidate jice1("audio", 0, candidate1);
344 JsepIceCandidate jice2("audio", 0, candidate2);
345 JsepIceCandidate jice3("video", 0, candidate1);
346 JsepIceCandidate jice4("video", 0, candidate2);
347 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
348 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
349 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
350 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice4));
351
352 std::string message = Serialize(jsep_desc_.get());
353 EXPECT_EQ(message.find("c=IN IP6 ::3"), std::string::npos);
354 // Should have a c line like this one when no any default exists.
355 EXPECT_NE(message.find("c=IN IP4 0.0.0.0"), std::string::npos);
356}
357
358// Tests that the connection address will be correctly set when the Candidate is
359// removed.
360TEST_F(JsepSessionDescriptionTest, RemoveCandidateAndSetConnectionAddress) {
361 cricket::Candidate candidate1(
362 cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
363 rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
364 cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
365 candidate1.set_transport_name("audio");
366
367 cricket::Candidate candidate2(
368 cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
369 rtc::SocketAddress("::2", 1235), kCandidatePriority, "", "",
370 cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
371 candidate2.set_transport_name("audio");
372
373 cricket::Candidate candidate3(
374 cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
375 rtc::SocketAddress("192.168.1.1", 1236), kCandidatePriority, "", "",
376 cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
377 candidate3.set_transport_name("audio");
378
379 JsepIceCandidate jice1("audio", 0, candidate1);
380 JsepIceCandidate jice2("audio", 0, candidate2);
381 JsepIceCandidate jice3("audio", 0, candidate3);
382
383 size_t audio_index = 0;
384 auto media_desc = static_cast<cricket::MediaContentDescription*>(
385 jsep_desc_->description()->contents()[audio_index].description);
386
387 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
388 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
389 ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
390
391 std::vector<cricket::Candidate> candidates;
392 EXPECT_EQ("192.168.1.1:1236", media_desc->connection_address().ToString());
393
394 candidates.push_back(candidate3);
395 ASSERT_TRUE(jsep_desc_->RemoveCandidates(candidates));
396 EXPECT_EQ("[::1]:1234", media_desc->connection_address().ToString());
397
398 candidates.clear();
399 candidates.push_back(candidate2);
400 ASSERT_TRUE(jsep_desc_->RemoveCandidates(candidates));
401 EXPECT_EQ("[::1]:1234", media_desc->connection_address().ToString());
402
403 candidates.clear();
404 candidates.push_back(candidate1);
405 ASSERT_TRUE(jsep_desc_->RemoveCandidates(candidates));
406 EXPECT_EQ("0.0.0.0:9", media_desc->connection_address().ToString());
407}