1 /*
2  *  Copyright 2012 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 
11 #include "api/jsep_session_description.h"
12 
13 #include <stddef.h>
14 #include <stdint.h>
15 
16 #include <memory>
17 #include <string>
18 #include <utility>
19 #include <vector>
20 
21 #include "api/candidate.h"
22 #include "api/jsep.h"
23 #include "api/jsep_ice_candidate.h"
24 #include "media/base/codec.h"
25 #include "p2p/base/p2p_constants.h"
26 #include "p2p/base/port.h"
27 #include "p2p/base/transport_description.h"
28 #include "p2p/base/transport_info.h"
29 #include "pc/session_description.h"
30 #include "pc/webrtc_sdp.h"
31 #include "rtc_base/helpers.h"
32 #include "rtc_base/socket_address.h"
33 #include "rtc_base/string_encode.h"
34 #include "test/gtest.h"
35 
36 using cricket::MediaProtocolType;
37 using ::testing::Values;
38 using webrtc::IceCandidateCollection;
39 using webrtc::IceCandidateInterface;
40 using webrtc::JsepIceCandidate;
41 using webrtc::JsepSessionDescription;
42 using webrtc::SdpType;
43 using webrtc::SessionDescriptionInterface;
44 
45 static const char kCandidateUfrag[] = "ufrag";
46 static const char kCandidatePwd[] = "pwd";
47 static const char kCandidateUfragVoice[] = "ufrag_voice";
48 static const char kCandidatePwdVoice[] = "pwd_voice";
49 static const char kCandidateUfragVideo[] = "ufrag_video";
50 static const char kCandidatePwdVideo[] = "pwd_video";
51 static const char kCandidateFoundation[] = "a0+B/1";
52 static const uint32_t kCandidatePriority = 2130706432U;  // pref = 1.0
53 static const uint32_t kCandidateGeneration = 2;
54 
55 // This creates a session description with both audio and video media contents.
56 // In SDP this is described by two m lines, one audio and one video.
57 static std::unique_ptr<cricket::SessionDescription>
CreateCricketSessionDescription()58 CreateCricketSessionDescription() {
59   auto desc = std::make_unique<cricket::SessionDescription>();
60 
61   // AudioContentDescription
62   auto audio = std::make_unique<cricket::AudioContentDescription>();
63   // VideoContentDescription
64   auto video = std::make_unique<cricket::VideoContentDescription>();
65 
66   audio->AddCodec(cricket::AudioCodec(103, "ISAC", 16000, 0, 0));
67   desc->AddContent(cricket::CN_AUDIO, MediaProtocolType::kRtp,
68                    std::move(audio));
69 
70   video->AddCodec(cricket::VideoCodec(120, "VP8"));
71   desc->AddContent(cricket::CN_VIDEO, MediaProtocolType::kRtp,
72                    std::move(video));
73 
74   desc->AddTransportInfo(cricket::TransportInfo(
75       cricket::CN_AUDIO,
76       cricket::TransportDescription(
77           std::vector<std::string>(), kCandidateUfragVoice, kCandidatePwdVoice,
78           cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE, NULL)));
79   desc->AddTransportInfo(cricket::TransportInfo(
80       cricket::CN_VIDEO,
81       cricket::TransportDescription(
82           std::vector<std::string>(), kCandidateUfragVideo, kCandidatePwdVideo,
83           cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE, NULL)));
84   return desc;
85 }
86 
87 class JsepSessionDescriptionTest : public ::testing::Test {
88  protected:
SetUp()89   virtual void SetUp() {
90     int port = 1234;
91     rtc::SocketAddress address("127.0.0.1", port++);
92     cricket::Candidate candidate(cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
93                                  address, 1, "", "", "local", 0, "1");
94     candidate_ = candidate;
95     const std::string session_id = rtc::ToString(rtc::CreateRandomId64());
96     const std::string session_version = rtc::ToString(rtc::CreateRandomId());
97     jsep_desc_ = std::make_unique<JsepSessionDescription>(SdpType::kOffer);
98     ASSERT_TRUE(jsep_desc_->Initialize(CreateCricketSessionDescription(),
99                                        session_id, session_version));
100   }
101 
Serialize(const SessionDescriptionInterface * desc)102   std::string Serialize(const SessionDescriptionInterface* desc) {
103     std::string sdp;
104     EXPECT_TRUE(desc->ToString(&sdp));
105     EXPECT_FALSE(sdp.empty());
106     return sdp;
107   }
108 
DeSerialize(const std::string & sdp)109   std::unique_ptr<SessionDescriptionInterface> DeSerialize(
110       const std::string& sdp) {
111     auto jsep_desc = std::make_unique<JsepSessionDescription>(SdpType::kOffer);
112     EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jsep_desc.get(), nullptr));
113     return std::move(jsep_desc);
114   }
115 
116   cricket::Candidate candidate_;
117   std::unique_ptr<JsepSessionDescription> jsep_desc_;
118 };
119 
TEST_F(JsepSessionDescriptionTest,CloneDefault)120 TEST_F(JsepSessionDescriptionTest, CloneDefault) {
121   auto new_desc = jsep_desc_->Clone();
122   EXPECT_EQ(jsep_desc_->type(), new_desc->type());
123   std::string old_desc_string;
124   std::string new_desc_string;
125   EXPECT_TRUE(jsep_desc_->ToString(&old_desc_string));
126   EXPECT_TRUE(new_desc->ToString(&new_desc_string));
127   EXPECT_EQ(old_desc_string, new_desc_string);
128   EXPECT_EQ(jsep_desc_->session_id(), new_desc->session_id());
129   EXPECT_EQ(jsep_desc_->session_version(), new_desc->session_version());
130 }
131 
TEST_F(JsepSessionDescriptionTest,CloneWithCandidates)132 TEST_F(JsepSessionDescriptionTest, CloneWithCandidates) {
133   cricket::Candidate candidate_v4(
134       cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
135       rtc::SocketAddress("192.168.1.5", 1234), kCandidatePriority, "", "",
136       cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
137   cricket::Candidate candidate_v6(
138       cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
139       rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
140       cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
141 
142   JsepIceCandidate jice_v4("audio", 0, candidate_v4);
143   JsepIceCandidate jice_v6("audio", 0, candidate_v6);
144   JsepIceCandidate jice_v4_video("video", 0, candidate_v4);
145   JsepIceCandidate jice_v6_video("video", 0, candidate_v6);
146   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v4));
147   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v6));
148   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v4_video));
149   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v6_video));
150   auto new_desc = jsep_desc_->Clone();
151   EXPECT_EQ(jsep_desc_->type(), new_desc->type());
152   std::string old_desc_string;
153   std::string new_desc_string;
154   EXPECT_TRUE(jsep_desc_->ToString(&old_desc_string));
155   EXPECT_TRUE(new_desc->ToString(&new_desc_string));
156   EXPECT_EQ(old_desc_string, new_desc_string);
157 }
158 
159 // Test that number_of_mediasections() returns the number of media contents in
160 // a session description.
TEST_F(JsepSessionDescriptionTest,CheckSessionDescription)161 TEST_F(JsepSessionDescriptionTest, CheckSessionDescription) {
162   EXPECT_EQ(2u, jsep_desc_->number_of_mediasections());
163 }
164 
165 // Test that we can add a candidate to a session description without MID.
TEST_F(JsepSessionDescriptionTest,AddCandidateWithoutMid)166 TEST_F(JsepSessionDescriptionTest, AddCandidateWithoutMid) {
167   JsepIceCandidate jsep_candidate("", 0, candidate_);
168   EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
169   const IceCandidateCollection* ice_candidates = jsep_desc_->candidates(0);
170   ASSERT_TRUE(ice_candidates != NULL);
171   EXPECT_EQ(1u, ice_candidates->count());
172   const IceCandidateInterface* ice_candidate = ice_candidates->at(0);
173   ASSERT_TRUE(ice_candidate != NULL);
174   candidate_.set_username(kCandidateUfragVoice);
175   candidate_.set_password(kCandidatePwdVoice);
176   EXPECT_TRUE(ice_candidate->candidate().IsEquivalent(candidate_));
177   EXPECT_EQ(0, ice_candidate->sdp_mline_index());
178   EXPECT_EQ(0u, jsep_desc_->candidates(1)->count());
179 }
180 
181 // Test that we can add and remove candidates to a session description with
182 // MID. Removing candidates requires MID (transport_name).
TEST_F(JsepSessionDescriptionTest,AddAndRemoveCandidatesWithMid)183 TEST_F(JsepSessionDescriptionTest, AddAndRemoveCandidatesWithMid) {
184   // mid and m-line index don't match, in this case mid is preferred.
185   std::string mid = "video";
186   JsepIceCandidate jsep_candidate(mid, 0, candidate_);
187   EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
188   EXPECT_EQ(0u, jsep_desc_->candidates(0)->count());
189   const IceCandidateCollection* ice_candidates = jsep_desc_->candidates(1);
190   ASSERT_TRUE(ice_candidates != NULL);
191   EXPECT_EQ(1u, ice_candidates->count());
192   const IceCandidateInterface* ice_candidate = ice_candidates->at(0);
193   ASSERT_TRUE(ice_candidate != NULL);
194   candidate_.set_username(kCandidateUfragVideo);
195   candidate_.set_password(kCandidatePwdVideo);
196   EXPECT_TRUE(ice_candidate->candidate().IsEquivalent(candidate_));
197   // The mline index should have been updated according to mid.
198   EXPECT_EQ(1, ice_candidate->sdp_mline_index());
199 
200   std::vector<cricket::Candidate> candidates(1, candidate_);
201   candidates[0].set_transport_name(mid);
202   EXPECT_EQ(1u, jsep_desc_->RemoveCandidates(candidates));
203   EXPECT_EQ(0u, jsep_desc_->candidates(0)->count());
204   EXPECT_EQ(0u, jsep_desc_->candidates(1)->count());
205 }
206 
TEST_F(JsepSessionDescriptionTest,AddCandidateAlreadyHasUfrag)207 TEST_F(JsepSessionDescriptionTest, AddCandidateAlreadyHasUfrag) {
208   candidate_.set_username(kCandidateUfrag);
209   candidate_.set_password(kCandidatePwd);
210   JsepIceCandidate jsep_candidate("audio", 0, candidate_);
211   EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
212   const IceCandidateCollection* ice_candidates = jsep_desc_->candidates(0);
213   ASSERT_TRUE(ice_candidates != NULL);
214   EXPECT_EQ(1u, ice_candidates->count());
215   const IceCandidateInterface* ice_candidate = ice_candidates->at(0);
216   ASSERT_TRUE(ice_candidate != NULL);
217   candidate_.set_username(kCandidateUfrag);
218   candidate_.set_password(kCandidatePwd);
219   EXPECT_TRUE(ice_candidate->candidate().IsEquivalent(candidate_));
220 
221   EXPECT_EQ(0u, jsep_desc_->candidates(1)->count());
222 }
223 
224 // Test that we can not add a candidate if there is no corresponding media
225 // content in the session description.
TEST_F(JsepSessionDescriptionTest,AddBadCandidate)226 TEST_F(JsepSessionDescriptionTest, AddBadCandidate) {
227   JsepIceCandidate bad_candidate1("", 55, candidate_);
228   EXPECT_FALSE(jsep_desc_->AddCandidate(&bad_candidate1));
229 
230   JsepIceCandidate bad_candidate2("some weird mid", 0, candidate_);
231   EXPECT_FALSE(jsep_desc_->AddCandidate(&bad_candidate2));
232 }
233 
234 // Tests that repeatedly adding the same candidate, with or without credentials,
235 // does not increase the number of candidates in the description.
TEST_F(JsepSessionDescriptionTest,AddCandidateDuplicates)236 TEST_F(JsepSessionDescriptionTest, AddCandidateDuplicates) {
237   JsepIceCandidate jsep_candidate("", 0, candidate_);
238   EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
239   EXPECT_EQ(1u, jsep_desc_->candidates(0)->count());
240 
241   // Add the same candidate again.  It should be ignored.
242   EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
243   EXPECT_EQ(1u, jsep_desc_->candidates(0)->count());
244 
245   // Create a new candidate, identical except that the ufrag and pwd are now
246   // populated.
247   candidate_.set_username(kCandidateUfragVoice);
248   candidate_.set_password(kCandidatePwdVoice);
249   JsepIceCandidate jsep_candidate_with_credentials("", 0, candidate_);
250 
251   // This should also be identified as redundant and ignored.
252   EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate_with_credentials));
253   EXPECT_EQ(1u, jsep_desc_->candidates(0)->count());
254 }
255 
256 // Test that the connection address is set to a hostname address after adding a
257 // hostname candidate.
TEST_F(JsepSessionDescriptionTest,AddHostnameCandidate)258 TEST_F(JsepSessionDescriptionTest, AddHostnameCandidate) {
259   cricket::Candidate c;
260   c.set_component(cricket::ICE_CANDIDATE_COMPONENT_RTP);
261   c.set_protocol(cricket::UDP_PROTOCOL_NAME);
262   c.set_address(rtc::SocketAddress("example.local", 1234));
263   c.set_type(cricket::LOCAL_PORT_TYPE);
264   const size_t audio_index = 0;
265   JsepIceCandidate hostname_candidate("audio", audio_index, c);
266   EXPECT_TRUE(jsep_desc_->AddCandidate(&hostname_candidate));
267 
268   ASSERT_NE(nullptr, jsep_desc_->description());
269   ASSERT_EQ(2u, jsep_desc_->description()->contents().size());
270   const auto& content = jsep_desc_->description()->contents()[audio_index];
271   EXPECT_EQ("0.0.0.0:9",
272             content.media_description()->connection_address().ToString());
273 }
274 
275 // Test that we can serialize a JsepSessionDescription and deserialize it again.
TEST_F(JsepSessionDescriptionTest,SerializeDeserialize)276 TEST_F(JsepSessionDescriptionTest, SerializeDeserialize) {
277   std::string sdp = Serialize(jsep_desc_.get());
278 
279   auto parsed_jsep_desc = DeSerialize(sdp);
280   EXPECT_EQ(2u, parsed_jsep_desc->number_of_mediasections());
281 
282   std::string parsed_sdp = Serialize(parsed_jsep_desc.get());
283   EXPECT_EQ(sdp, parsed_sdp);
284 }
285 
286 // Test that we can serialize a JsepSessionDescription when a hostname candidate
287 // is the default destination and deserialize it again. The connection address
288 // in the deserialized description should be the dummy address 0.0.0.0:9.
TEST_F(JsepSessionDescriptionTest,SerializeDeserializeWithHostnameCandidate)289 TEST_F(JsepSessionDescriptionTest, SerializeDeserializeWithHostnameCandidate) {
290   cricket::Candidate c;
291   c.set_component(cricket::ICE_CANDIDATE_COMPONENT_RTP);
292   c.set_protocol(cricket::UDP_PROTOCOL_NAME);
293   c.set_address(rtc::SocketAddress("example.local", 1234));
294   c.set_type(cricket::LOCAL_PORT_TYPE);
295   const size_t audio_index = 0;
296   const size_t video_index = 1;
297   JsepIceCandidate hostname_candidate_audio("audio", audio_index, c);
298   JsepIceCandidate hostname_candidate_video("video", video_index, c);
299   EXPECT_TRUE(jsep_desc_->AddCandidate(&hostname_candidate_audio));
300   EXPECT_TRUE(jsep_desc_->AddCandidate(&hostname_candidate_video));
301 
302   std::string sdp = Serialize(jsep_desc_.get());
303 
304   auto parsed_jsep_desc = DeSerialize(sdp);
305   EXPECT_EQ(2u, parsed_jsep_desc->number_of_mediasections());
306 
307   ASSERT_NE(nullptr, parsed_jsep_desc->description());
308   ASSERT_EQ(2u, parsed_jsep_desc->description()->contents().size());
309   const auto& audio_content =
310       parsed_jsep_desc->description()->contents()[audio_index];
311   const auto& video_content =
312       parsed_jsep_desc->description()->contents()[video_index];
313   EXPECT_EQ("0.0.0.0:9",
314             audio_content.media_description()->connection_address().ToString());
315   EXPECT_EQ("0.0.0.0:9",
316             video_content.media_description()->connection_address().ToString());
317 }
318 
319 // Tests that we can serialize and deserialize a JsepSesssionDescription
320 // with candidates.
TEST_F(JsepSessionDescriptionTest,SerializeDeserializeWithCandidates)321 TEST_F(JsepSessionDescriptionTest, SerializeDeserializeWithCandidates) {
322   std::string sdp = Serialize(jsep_desc_.get());
323 
324   // Add a candidate and check that the serialized result is different.
325   JsepIceCandidate jsep_candidate("audio", 0, candidate_);
326   EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate));
327   std::string sdp_with_candidate = Serialize(jsep_desc_.get());
328   EXPECT_NE(sdp, sdp_with_candidate);
329 
330   auto parsed_jsep_desc = DeSerialize(sdp_with_candidate);
331   std::string parsed_sdp_with_candidate = Serialize(parsed_jsep_desc.get());
332 
333   EXPECT_EQ(sdp_with_candidate, parsed_sdp_with_candidate);
334 }
335 
336 // TODO(zhihuang): Modify these tests. These are used to verify that after
337 // adding the candidates, the connection_address field is set correctly. Modify
338 // those so that the "connection address" is tested directly.
339 // Tests serialization of SDP with only IPv6 candidates and verifies that IPv6
340 // is used as default address in c line according to preference.
TEST_F(JsepSessionDescriptionTest,SerializeSessionDescriptionWithIPv6Only)341 TEST_F(JsepSessionDescriptionTest, SerializeSessionDescriptionWithIPv6Only) {
342   // Stun has a high preference than local host.
343   cricket::Candidate candidate1(
344       cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
345       rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
346       cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
347   cricket::Candidate candidate2(
348       cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
349       rtc::SocketAddress("::2", 1235), kCandidatePriority, "", "",
350       cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
351 
352   JsepIceCandidate jice1("audio", 0, candidate1);
353   JsepIceCandidate jice2("audio", 0, candidate2);
354   JsepIceCandidate jice3("video", 0, candidate1);
355   JsepIceCandidate jice4("video", 0, candidate2);
356   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
357   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
358   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
359   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice4));
360   std::string message = Serialize(jsep_desc_.get());
361 
362   // Should have a c line like this one.
363   EXPECT_NE(message.find("c=IN IP6 ::1"), std::string::npos);
364   // Shouldn't have a IP4 c line.
365   EXPECT_EQ(message.find("c=IN IP4"), std::string::npos);
366 }
367 
368 // Tests serialization of SDP with both IPv4 and IPv6 candidates and
369 // verifies that IPv4 is used as default address in c line even if the
370 // preference of IPv4 is lower.
TEST_F(JsepSessionDescriptionTest,SerializeSessionDescriptionWithBothIPFamilies)371 TEST_F(JsepSessionDescriptionTest,
372        SerializeSessionDescriptionWithBothIPFamilies) {
373   cricket::Candidate candidate_v4(
374       cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
375       rtc::SocketAddress("192.168.1.5", 1234), kCandidatePriority, "", "",
376       cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
377   cricket::Candidate candidate_v6(
378       cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
379       rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
380       cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
381 
382   JsepIceCandidate jice_v4("audio", 0, candidate_v4);
383   JsepIceCandidate jice_v6("audio", 0, candidate_v6);
384   JsepIceCandidate jice_v4_video("video", 0, candidate_v4);
385   JsepIceCandidate jice_v6_video("video", 0, candidate_v6);
386   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v4));
387   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v6));
388   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v4_video));
389   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v6_video));
390   std::string message = Serialize(jsep_desc_.get());
391 
392   // Should have a c line like this one.
393   EXPECT_NE(message.find("c=IN IP4 192.168.1.5"), std::string::npos);
394   // Shouldn't have a IP6 c line.
395   EXPECT_EQ(message.find("c=IN IP6"), std::string::npos);
396 }
397 
398 // Tests serialization of SDP with both UDP and TCP candidates and
399 // verifies that UDP is used as default address in c line even if the
400 // preference of UDP is lower.
TEST_F(JsepSessionDescriptionTest,SerializeSessionDescriptionWithBothProtocols)401 TEST_F(JsepSessionDescriptionTest,
402        SerializeSessionDescriptionWithBothProtocols) {
403   // Stun has a high preference than local host.
404   cricket::Candidate candidate1(
405       cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
406       rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
407       cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
408   cricket::Candidate candidate2(
409       cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
410       rtc::SocketAddress("fe80::1234:5678:abcd:ef12", 1235), kCandidatePriority,
411       "", "", cricket::LOCAL_PORT_TYPE, kCandidateGeneration,
412       kCandidateFoundation);
413 
414   JsepIceCandidate jice1("audio", 0, candidate1);
415   JsepIceCandidate jice2("audio", 0, candidate2);
416   JsepIceCandidate jice3("video", 0, candidate1);
417   JsepIceCandidate jice4("video", 0, candidate2);
418   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
419   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
420   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
421   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice4));
422   std::string message = Serialize(jsep_desc_.get());
423 
424   // Should have a c line like this one.
425   EXPECT_NE(message.find("c=IN IP6 fe80::1234:5678:abcd:ef12"),
426             std::string::npos);
427   // Shouldn't have a IP4 c line.
428   EXPECT_EQ(message.find("c=IN IP4"), std::string::npos);
429 }
430 
431 // Tests serialization of SDP with only TCP candidates and verifies that
432 // null IPv4 is used as default address in c line.
TEST_F(JsepSessionDescriptionTest,SerializeSessionDescriptionWithTCPOnly)433 TEST_F(JsepSessionDescriptionTest, SerializeSessionDescriptionWithTCPOnly) {
434   // Stun has a high preference than local host.
435   cricket::Candidate candidate1(
436       cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
437       rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
438       cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
439   cricket::Candidate candidate2(
440       cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
441       rtc::SocketAddress("::2", 1235), kCandidatePriority, "", "",
442       cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
443 
444   JsepIceCandidate jice1("audio", 0, candidate1);
445   JsepIceCandidate jice2("audio", 0, candidate2);
446   JsepIceCandidate jice3("video", 0, candidate1);
447   JsepIceCandidate jice4("video", 0, candidate2);
448   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
449   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
450   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
451   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice4));
452 
453   std::string message = Serialize(jsep_desc_.get());
454   EXPECT_EQ(message.find("c=IN IP6 ::3"), std::string::npos);
455   // Should have a c line like this one when no any default exists.
456   EXPECT_NE(message.find("c=IN IP4 0.0.0.0"), std::string::npos);
457 }
458 
459 // Tests that the connection address will be correctly set when the Candidate is
460 // removed.
TEST_F(JsepSessionDescriptionTest,RemoveCandidateAndSetConnectionAddress)461 TEST_F(JsepSessionDescriptionTest, RemoveCandidateAndSetConnectionAddress) {
462   cricket::Candidate candidate1(
463       cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
464       rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
465       cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
466   candidate1.set_transport_name("audio");
467 
468   cricket::Candidate candidate2(
469       cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
470       rtc::SocketAddress("::2", 1235), kCandidatePriority, "", "",
471       cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
472   candidate2.set_transport_name("audio");
473 
474   cricket::Candidate candidate3(
475       cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
476       rtc::SocketAddress("192.168.1.1", 1236), kCandidatePriority, "", "",
477       cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
478   candidate3.set_transport_name("audio");
479 
480   JsepIceCandidate jice1("audio", 0, candidate1);
481   JsepIceCandidate jice2("audio", 0, candidate2);
482   JsepIceCandidate jice3("audio", 0, candidate3);
483 
484   size_t audio_index = 0;
485   auto media_desc =
486       jsep_desc_->description()->contents()[audio_index].media_description();
487 
488   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
489   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
490   ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
491 
492   std::vector<cricket::Candidate> candidates;
493   EXPECT_EQ("192.168.1.1:1236", media_desc->connection_address().ToString());
494 
495   candidates.push_back(candidate3);
496   ASSERT_TRUE(jsep_desc_->RemoveCandidates(candidates));
497   EXPECT_EQ("[::1]:1234", media_desc->connection_address().ToString());
498 
499   candidates.clear();
500   candidates.push_back(candidate2);
501   ASSERT_TRUE(jsep_desc_->RemoveCandidates(candidates));
502   EXPECT_EQ("[::1]:1234", media_desc->connection_address().ToString());
503 
504   candidates.clear();
505   candidates.push_back(candidate1);
506   ASSERT_TRUE(jsep_desc_->RemoveCandidates(candidates));
507   EXPECT_EQ("0.0.0.0:9", media_desc->connection_address().ToString());
508 }
509 
510 class EnumerateAllSdpTypesTest : public ::testing::Test,
511                                  public ::testing::WithParamInterface<SdpType> {
512 };
513 
TEST_P(EnumerateAllSdpTypesTest,TestIdentity)514 TEST_P(EnumerateAllSdpTypesTest, TestIdentity) {
515   SdpType type = GetParam();
516 
517   const char* str = webrtc::SdpTypeToString(type);
518   EXPECT_EQ(type, webrtc::SdpTypeFromString(str));
519 }
520 
521 INSTANTIATE_TEST_SUITE_P(JsepSessionDescriptionTest,
522                          EnumerateAllSdpTypesTest,
523                          Values(SdpType::kOffer,
524                                 SdpType::kPrAnswer,
525                                 SdpType::kAnswer));
526