OLD | NEW |
| (Empty) |
1 /* | |
2 * libjingle | |
3 * Copyright 2012 Google Inc. | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions are met: | |
7 * | |
8 * 1. Redistributions of source code must retain the above copyright notice, | |
9 * this list of conditions and the following disclaimer. | |
10 * 2. Redistributions in binary form must reproduce the above copyright notice, | |
11 * this list of conditions and the following disclaimer in the documentation | |
12 * and/or other materials provided with the distribution. | |
13 * 3. The name of the author may not be used to endorse or promote products | |
14 * derived from this software without specific prior written permission. | |
15 * | |
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
26 */ | |
27 | |
28 #include <string> | |
29 | |
30 #include "talk/app/webrtc/jsepicecandidate.h" | |
31 #include "talk/app/webrtc/jsepsessiondescription.h" | |
32 #include "talk/session/media/mediasession.h" | |
33 #include "webrtc/base/gunit.h" | |
34 #include "webrtc/base/helpers.h" | |
35 #include "webrtc/base/scoped_ptr.h" | |
36 #include "webrtc/base/ssladapter.h" | |
37 #include "webrtc/base/stringencode.h" | |
38 #include "webrtc/p2p/base/candidate.h" | |
39 #include "webrtc/p2p/base/constants.h" | |
40 #include "webrtc/p2p/base/sessiondescription.h" | |
41 | |
42 using webrtc::IceCandidateCollection; | |
43 using webrtc::IceCandidateInterface; | |
44 using webrtc::JsepIceCandidate; | |
45 using webrtc::JsepSessionDescription; | |
46 using webrtc::SessionDescriptionInterface; | |
47 using rtc::scoped_ptr; | |
48 | |
49 static const char kCandidateUfrag[] = "ufrag"; | |
50 static const char kCandidatePwd[] = "pwd"; | |
51 static const char kCandidateUfragVoice[] = "ufrag_voice"; | |
52 static const char kCandidatePwdVoice[] = "pwd_voice"; | |
53 static const char kCandidateUfragVideo[] = "ufrag_video"; | |
54 static const char kCandidatePwdVideo[] = "pwd_video"; | |
55 | |
56 // This creates a session description with both audio and video media contents. | |
57 // In SDP this is described by two m lines, one audio and one video. | |
58 static cricket::SessionDescription* CreateCricketSessionDescription() { | |
59 cricket::SessionDescription* desc(new cricket::SessionDescription()); | |
60 // AudioContentDescription | |
61 scoped_ptr<cricket::AudioContentDescription> audio( | |
62 new cricket::AudioContentDescription()); | |
63 | |
64 // VideoContentDescription | |
65 scoped_ptr<cricket::VideoContentDescription> video( | |
66 new cricket::VideoContentDescription()); | |
67 | |
68 audio->AddCodec(cricket::AudioCodec(103, "ISAC", 16000, 0, 0, 0)); | |
69 desc->AddContent(cricket::CN_AUDIO, cricket::NS_JINGLE_RTP, | |
70 audio.release()); | |
71 | |
72 video->AddCodec(cricket::VideoCodec(120, "VP8", 640, 480, 30, 0)); | |
73 desc->AddContent(cricket::CN_VIDEO, cricket::NS_JINGLE_RTP, | |
74 video.release()); | |
75 | |
76 EXPECT_TRUE(desc->AddTransportInfo(cricket::TransportInfo( | |
77 cricket::CN_AUDIO, | |
78 cricket::TransportDescription( | |
79 std::vector<std::string>(), kCandidateUfragVoice, kCandidatePwdVoice, | |
80 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE, NULL)))); | |
81 EXPECT_TRUE(desc->AddTransportInfo(cricket::TransportInfo( | |
82 cricket::CN_VIDEO, | |
83 cricket::TransportDescription( | |
84 std::vector<std::string>(), kCandidateUfragVideo, kCandidatePwdVideo, | |
85 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE, NULL)))); | |
86 return desc; | |
87 } | |
88 | |
89 class JsepSessionDescriptionTest : public testing::Test { | |
90 protected: | |
91 virtual void SetUp() { | |
92 int port = 1234; | |
93 rtc::SocketAddress address("127.0.0.1", port++); | |
94 cricket::Candidate candidate(cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp", | |
95 address, 1, "", "", "local", 0, "1"); | |
96 candidate_ = candidate; | |
97 const std::string session_id = | |
98 rtc::ToString(rtc::CreateRandomId64()); | |
99 const std::string session_version = | |
100 rtc::ToString(rtc::CreateRandomId()); | |
101 jsep_desc_.reset(new JsepSessionDescription("dummy")); | |
102 ASSERT_TRUE(jsep_desc_->Initialize(CreateCricketSessionDescription(), | |
103 session_id, session_version)); | |
104 } | |
105 | |
106 std::string Serialize(const SessionDescriptionInterface* desc) { | |
107 std::string sdp; | |
108 EXPECT_TRUE(desc->ToString(&sdp)); | |
109 EXPECT_FALSE(sdp.empty()); | |
110 return sdp; | |
111 } | |
112 | |
113 SessionDescriptionInterface* DeSerialize(const std::string& sdp) { | |
114 JsepSessionDescription* desc(new JsepSessionDescription("dummy")); | |
115 EXPECT_TRUE(desc->Initialize(sdp, NULL)); | |
116 return desc; | |
117 } | |
118 | |
119 cricket::Candidate candidate_; | |
120 rtc::scoped_ptr<JsepSessionDescription> jsep_desc_; | |
121 }; | |
122 | |
123 // Test that number_of_mediasections() returns the number of media contents in | |
124 // a session description. | |
125 TEST_F(JsepSessionDescriptionTest, CheckSessionDescription) { | |
126 EXPECT_EQ(2u, jsep_desc_->number_of_mediasections()); | |
127 } | |
128 | |
129 // Test that we can add a candidate to a session description. | |
130 TEST_F(JsepSessionDescriptionTest, AddCandidateWithoutMid) { | |
131 JsepIceCandidate jsep_candidate("", 0, candidate_); | |
132 EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate)); | |
133 const IceCandidateCollection* ice_candidates = jsep_desc_->candidates(0); | |
134 ASSERT_TRUE(ice_candidates != NULL); | |
135 EXPECT_EQ(1u, ice_candidates->count()); | |
136 const IceCandidateInterface* ice_candidate = ice_candidates->at(0); | |
137 ASSERT_TRUE(ice_candidate != NULL); | |
138 candidate_.set_username(kCandidateUfragVoice); | |
139 candidate_.set_password(kCandidatePwdVoice); | |
140 EXPECT_TRUE(ice_candidate->candidate().IsEquivalent(candidate_)); | |
141 EXPECT_EQ(0, ice_candidate->sdp_mline_index()); | |
142 EXPECT_EQ(0u, jsep_desc_->candidates(1)->count()); | |
143 } | |
144 | |
145 TEST_F(JsepSessionDescriptionTest, AddCandidateWithMid) { | |
146 // mid and m-line index don't match, in this case mid is preferred. | |
147 JsepIceCandidate jsep_candidate("video", 0, candidate_); | |
148 EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate)); | |
149 EXPECT_EQ(0u, jsep_desc_->candidates(0)->count()); | |
150 const IceCandidateCollection* ice_candidates = jsep_desc_->candidates(1); | |
151 ASSERT_TRUE(ice_candidates != NULL); | |
152 EXPECT_EQ(1u, ice_candidates->count()); | |
153 const IceCandidateInterface* ice_candidate = ice_candidates->at(0); | |
154 ASSERT_TRUE(ice_candidate != NULL); | |
155 candidate_.set_username(kCandidateUfragVideo); | |
156 candidate_.set_password(kCandidatePwdVideo); | |
157 EXPECT_TRUE(ice_candidate->candidate().IsEquivalent(candidate_)); | |
158 // The mline index should have been updated according to mid. | |
159 EXPECT_EQ(1, ice_candidate->sdp_mline_index()); | |
160 } | |
161 | |
162 TEST_F(JsepSessionDescriptionTest, AddCandidateAlreadyHasUfrag) { | |
163 candidate_.set_username(kCandidateUfrag); | |
164 candidate_.set_password(kCandidatePwd); | |
165 JsepIceCandidate jsep_candidate("audio", 0, candidate_); | |
166 EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate)); | |
167 const IceCandidateCollection* ice_candidates = jsep_desc_->candidates(0); | |
168 ASSERT_TRUE(ice_candidates != NULL); | |
169 EXPECT_EQ(1u, ice_candidates->count()); | |
170 const IceCandidateInterface* ice_candidate = ice_candidates->at(0); | |
171 ASSERT_TRUE(ice_candidate != NULL); | |
172 candidate_.set_username(kCandidateUfrag); | |
173 candidate_.set_password(kCandidatePwd); | |
174 EXPECT_TRUE(ice_candidate->candidate().IsEquivalent(candidate_)); | |
175 | |
176 EXPECT_EQ(0u, jsep_desc_->candidates(1)->count()); | |
177 } | |
178 | |
179 // Test that we can not add a candidate if there is no corresponding media | |
180 // content in the session description. | |
181 TEST_F(JsepSessionDescriptionTest, AddBadCandidate) { | |
182 JsepIceCandidate bad_candidate1("", 55, candidate_); | |
183 EXPECT_FALSE(jsep_desc_->AddCandidate(&bad_candidate1)); | |
184 | |
185 JsepIceCandidate bad_candidate2("some weird mid", 0, candidate_); | |
186 EXPECT_FALSE(jsep_desc_->AddCandidate(&bad_candidate2)); | |
187 } | |
188 | |
189 // Tests that repeatedly adding the same candidate, with or without credentials, | |
190 // does not increase the number of candidates in the description. | |
191 TEST_F(JsepSessionDescriptionTest, AddCandidateDuplicates) { | |
192 JsepIceCandidate jsep_candidate("", 0, candidate_); | |
193 EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate)); | |
194 EXPECT_EQ(1u, jsep_desc_->candidates(0)->count()); | |
195 | |
196 // Add the same candidate again. It should be ignored. | |
197 EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate)); | |
198 EXPECT_EQ(1u, jsep_desc_->candidates(0)->count()); | |
199 | |
200 // Create a new candidate, identical except that the ufrag and pwd are now | |
201 // populated. | |
202 candidate_.set_username(kCandidateUfragVoice); | |
203 candidate_.set_password(kCandidatePwdVoice); | |
204 JsepIceCandidate jsep_candidate_with_credentials("", 0, candidate_); | |
205 | |
206 // This should also be identified as redundant and ignored. | |
207 EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate_with_credentials)); | |
208 EXPECT_EQ(1u, jsep_desc_->candidates(0)->count()); | |
209 } | |
210 | |
211 // Test that we can serialize a JsepSessionDescription and deserialize it again. | |
212 TEST_F(JsepSessionDescriptionTest, SerializeDeserialize) { | |
213 std::string sdp = Serialize(jsep_desc_.get()); | |
214 | |
215 scoped_ptr<SessionDescriptionInterface> parsed_jsep_desc(DeSerialize(sdp)); | |
216 EXPECT_EQ(2u, parsed_jsep_desc->number_of_mediasections()); | |
217 | |
218 std::string parsed_sdp = Serialize(parsed_jsep_desc.get()); | |
219 EXPECT_EQ(sdp, parsed_sdp); | |
220 } | |
221 | |
222 // Tests that we can serialize and deserialize a JsepSesssionDescription | |
223 // with candidates. | |
224 TEST_F(JsepSessionDescriptionTest, SerializeDeserializeWithCandidates) { | |
225 std::string sdp = Serialize(jsep_desc_.get()); | |
226 | |
227 // Add a candidate and check that the serialized result is different. | |
228 JsepIceCandidate jsep_candidate("audio", 0, candidate_); | |
229 EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate)); | |
230 std::string sdp_with_candidate = Serialize(jsep_desc_.get()); | |
231 EXPECT_NE(sdp, sdp_with_candidate); | |
232 | |
233 scoped_ptr<SessionDescriptionInterface> parsed_jsep_desc( | |
234 DeSerialize(sdp_with_candidate)); | |
235 std::string parsed_sdp_with_candidate = Serialize(parsed_jsep_desc.get()); | |
236 | |
237 EXPECT_EQ(sdp_with_candidate, parsed_sdp_with_candidate); | |
238 } | |
OLD | NEW |