1 /* 2 * libjingle 3 * Copyright 2004 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 "talk/session/media/mediamessages.h" 29 30 #include <string> 31 #include <vector> 32 33 #include "talk/base/gunit.h" 34 #include "talk/base/scoped_ptr.h" 35 #include "talk/p2p/base/constants.h" 36 #include "talk/session/media/mediasessionclient.h" 37 #include "talk/xmllite/xmlelement.h" 38 39 // Unit tests for mediamessages.cc. 40 41 namespace cricket { 42 43 namespace { 44 45 static const char kViewVideoNoneXml[] = 46 "<view xmlns='google:jingle'" 47 " name='video1'" 48 " type='none'" 49 "/>"; 50 51 class MediaMessagesTest : public testing::Test { 52 public: 53 // CreateMediaSessionDescription uses a static variable cricket::NS_JINGLE_RTP 54 // defined in another file and cannot be used to initialize another static 55 // variable (http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14) 56 MediaMessagesTest() 57 : remote_description_(CreateMediaSessionDescription("audio1", "video1")) { 58 } 59 60 protected: 61 static std::string ViewVideoStaticVgaXml(const std::string& ssrc) { 62 return "<view xmlns='google:jingle'" 63 " name='video1'" 64 " type='static'" 65 " ssrc='" + ssrc + "'" 66 ">" 67 "<params" 68 " width='640'" 69 " height='480'" 70 " framerate='30'" 71 " preference='0'" 72 " />" 73 "</view>"; 74 } 75 76 static cricket::StreamParams CreateStream(const std::string& nick, 77 const std::string& name, 78 uint32 ssrc1, 79 uint32 ssrc2, 80 const std::string& semantics, 81 const std::string& type, 82 const std::string& display) { 83 StreamParams stream; 84 stream.groupid = nick; 85 stream.id = name; 86 stream.ssrcs.push_back(ssrc1); 87 stream.ssrcs.push_back(ssrc2); 88 stream.ssrc_groups.push_back( 89 cricket::SsrcGroup(semantics, stream.ssrcs)); 90 stream.type = type; 91 stream.display = display; 92 return stream; 93 } 94 95 static std::string StreamsXml(const std::string& stream1, 96 const std::string& stream2) { 97 return "<streams xmlns='google:jingle'>" 98 + stream1 99 + stream2 + 100 "</streams>"; 101 } 102 103 104 static std::string StreamXml(const std::string& nick, 105 const std::string& name, 106 const std::string& ssrc1, 107 const std::string& ssrc2, 108 const std::string& semantics, 109 const std::string& type, 110 const std::string& display) { 111 return "<stream" 112 " nick='" + nick + "'" 113 " name='" + name + "'" 114 " type='" + type + "'" 115 " display='" + display + "'" 116 ">" 117 "<ssrc>" + ssrc1 + "</ssrc>" 118 "<ssrc>" + ssrc2 + "</ssrc>" 119 "<ssrc-group" 120 " semantics='" + semantics + "'" 121 ">" 122 "<ssrc>" + ssrc1 + "</ssrc>" 123 "<ssrc>" + ssrc2 + "</ssrc>" 124 "</ssrc-group>" 125 "</stream>"; 126 } 127 128 static std::string HeaderExtensionsXml(const std::string& hdrext1, 129 const std::string& hdrext2) { 130 return "<rtp:description xmlns:rtp=\"urn:xmpp:jingle:apps:rtp:1\">" 131 + hdrext1 132 + hdrext2 + 133 "</rtp:description>"; 134 } 135 136 static std::string HeaderExtensionXml(const std::string& uri, 137 const std::string& id) { 138 return "<rtp:rtp-hdrext" 139 " uri='" + uri + "'" 140 " id='" + id + "'" 141 "/>"; 142 } 143 144 static cricket::SessionDescription* CreateMediaSessionDescription( 145 const std::string& audio_content_name, 146 const std::string& video_content_name) { 147 cricket::SessionDescription* desc = new cricket::SessionDescription(); 148 desc->AddContent(audio_content_name, cricket::NS_JINGLE_RTP, 149 new cricket::AudioContentDescription()); 150 desc->AddContent(video_content_name, cricket::NS_JINGLE_RTP, 151 new cricket::VideoContentDescription()); 152 return desc; 153 } 154 155 size_t ClearXmlElements(cricket::XmlElements* elements) { 156 size_t size = elements->size(); 157 for (size_t i = 0; i < size; i++) { 158 delete elements->at(i); 159 } 160 elements->clear(); 161 return size; 162 } 163 164 talk_base::scoped_ptr<cricket::SessionDescription> remote_description_; 165 }; 166 167 } // anonymous namespace 168 169 // Test serializing/deserializing an empty <view> message. 170 TEST_F(MediaMessagesTest, ViewNoneToFromXml) { 171 buzz::XmlElement* expected_view_elem = 172 buzz::XmlElement::ForStr(kViewVideoNoneXml); 173 talk_base::scoped_ptr<buzz::XmlElement> action_elem( 174 new buzz::XmlElement(QN_JINGLE)); 175 176 EXPECT_FALSE(cricket::IsJingleViewRequest(action_elem.get())); 177 action_elem->AddElement(expected_view_elem); 178 EXPECT_TRUE(cricket::IsJingleViewRequest(action_elem.get())); 179 180 cricket::ViewRequest view_request; 181 cricket::XmlElements actual_view_elems; 182 cricket::WriteError error; 183 184 ASSERT_TRUE(cricket::WriteJingleViewRequest( 185 "video1", view_request, &actual_view_elems, &error)); 186 187 ASSERT_EQ(1U, actual_view_elems.size()); 188 EXPECT_EQ(expected_view_elem->Str(), actual_view_elems[0]->Str()); 189 ClearXmlElements(&actual_view_elems); 190 191 cricket::ParseError parse_error; 192 EXPECT_TRUE(cricket::IsJingleViewRequest(action_elem.get())); 193 ASSERT_TRUE(cricket::ParseJingleViewRequest( 194 action_elem.get(), &view_request, &parse_error)); 195 EXPECT_EQ(0U, view_request.static_video_views.size()); 196 } 197 198 // Test serializing/deserializing an a simple vga <view> message. 199 TEST_F(MediaMessagesTest, ViewVgaToFromXml) { 200 talk_base::scoped_ptr<buzz::XmlElement> action_elem( 201 new buzz::XmlElement(QN_JINGLE)); 202 buzz::XmlElement* expected_view_elem1 = 203 buzz::XmlElement::ForStr(ViewVideoStaticVgaXml("1234")); 204 buzz::XmlElement* expected_view_elem2 = 205 buzz::XmlElement::ForStr(ViewVideoStaticVgaXml("2468")); 206 action_elem->AddElement(expected_view_elem1); 207 action_elem->AddElement(expected_view_elem2); 208 209 cricket::ViewRequest view_request; 210 cricket::XmlElements actual_view_elems; 211 cricket::WriteError error; 212 213 view_request.static_video_views.push_back(cricket::StaticVideoView( 214 cricket::StreamSelector(1234), 640, 480, 30)); 215 view_request.static_video_views.push_back(cricket::StaticVideoView( 216 cricket::StreamSelector(2468), 640, 480, 30)); 217 218 ASSERT_TRUE(cricket::WriteJingleViewRequest( 219 "video1", view_request, &actual_view_elems, &error)); 220 221 ASSERT_EQ(2U, actual_view_elems.size()); 222 EXPECT_EQ(expected_view_elem1->Str(), actual_view_elems[0]->Str()); 223 EXPECT_EQ(expected_view_elem2->Str(), actual_view_elems[1]->Str()); 224 ClearXmlElements(&actual_view_elems); 225 226 view_request.static_video_views.clear(); 227 cricket::ParseError parse_error; 228 EXPECT_TRUE(cricket::IsJingleViewRequest(action_elem.get())); 229 ASSERT_TRUE(cricket::ParseJingleViewRequest( 230 action_elem.get(), &view_request, &parse_error)); 231 EXPECT_EQ(2U, view_request.static_video_views.size()); 232 EXPECT_EQ(1234U, view_request.static_video_views[0].selector.ssrc); 233 EXPECT_EQ(640, view_request.static_video_views[0].width); 234 EXPECT_EQ(480, view_request.static_video_views[0].height); 235 EXPECT_EQ(30, view_request.static_video_views[0].framerate); 236 EXPECT_EQ(2468U, view_request.static_video_views[1].selector.ssrc); 237 } 238 239 // Test deserializing bad view XML. 240 TEST_F(MediaMessagesTest, ParseBadViewXml) { 241 talk_base::scoped_ptr<buzz::XmlElement> action_elem( 242 new buzz::XmlElement(QN_JINGLE)); 243 buzz::XmlElement* view_elem = 244 buzz::XmlElement::ForStr(ViewVideoStaticVgaXml("not-an-ssrc")); 245 action_elem->AddElement(view_elem); 246 247 cricket::ViewRequest view_request; 248 cricket::ParseError parse_error; 249 ASSERT_FALSE(cricket::ParseJingleViewRequest( 250 action_elem.get(), &view_request, &parse_error)); 251 } 252 253 254 // Test serializing/deserializing typical streams xml. 255 TEST_F(MediaMessagesTest, StreamsToFromXml) { 256 talk_base::scoped_ptr<buzz::XmlElement> expected_streams_elem( 257 buzz::XmlElement::ForStr( 258 StreamsXml( 259 StreamXml("nick1", "stream1", "101", "102", 260 "semantics1", "type1", "display1"), 261 StreamXml("nick2", "stream2", "201", "202", 262 "semantics2", "type2", "display2")))); 263 264 std::vector<cricket::StreamParams> expected_streams; 265 expected_streams.push_back(CreateStream("nick1", "stream1", 101U, 102U, 266 "semantics1", "type1", "display1")); 267 expected_streams.push_back(CreateStream("nick2", "stream2", 201U, 202U, 268 "semantics2", "type2", "display2")); 269 270 talk_base::scoped_ptr<buzz::XmlElement> actual_desc_elem( 271 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT)); 272 cricket::WriteJingleStreams(expected_streams, actual_desc_elem.get()); 273 274 const buzz::XmlElement* actual_streams_elem = 275 actual_desc_elem->FirstNamed(QN_JINGLE_DRAFT_STREAMS); 276 ASSERT_TRUE(actual_streams_elem != NULL); 277 EXPECT_EQ(expected_streams_elem->Str(), actual_streams_elem->Str()); 278 279 talk_base::scoped_ptr<buzz::XmlElement> expected_desc_elem( 280 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT)); 281 expected_desc_elem->AddElement(new buzz::XmlElement( 282 *expected_streams_elem)); 283 std::vector<cricket::StreamParams> actual_streams; 284 cricket::ParseError parse_error; 285 286 EXPECT_TRUE(cricket::HasJingleStreams(expected_desc_elem.get())); 287 ASSERT_TRUE(cricket::ParseJingleStreams( 288 expected_desc_elem.get(), &actual_streams, &parse_error)); 289 EXPECT_EQ(2U, actual_streams.size()); 290 EXPECT_EQ(expected_streams[0], actual_streams[0]); 291 EXPECT_EQ(expected_streams[1], actual_streams[1]); 292 } 293 294 // Test deserializing bad streams xml. 295 TEST_F(MediaMessagesTest, StreamsFromBadXml) { 296 talk_base::scoped_ptr<buzz::XmlElement> streams_elem( 297 buzz::XmlElement::ForStr( 298 StreamsXml( 299 StreamXml("nick1", "name1", "101", "not-an-ssrc", 300 "semantics1", "type1", "display1"), 301 StreamXml("nick2", "name2", "202", "not-an-ssrc", 302 "semantics2", "type2", "display2")))); 303 talk_base::scoped_ptr<buzz::XmlElement> desc_elem( 304 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT)); 305 desc_elem->AddElement(new buzz::XmlElement(*streams_elem)); 306 307 std::vector<cricket::StreamParams> actual_streams; 308 cricket::ParseError parse_error; 309 ASSERT_FALSE(cricket::ParseJingleStreams( 310 desc_elem.get(), &actual_streams, &parse_error)); 311 } 312 313 // Test serializing/deserializing typical RTP Header Extension xml. 314 TEST_F(MediaMessagesTest, HeaderExtensionsToFromXml) { 315 talk_base::scoped_ptr<buzz::XmlElement> expected_desc_elem( 316 buzz::XmlElement::ForStr( 317 HeaderExtensionsXml( 318 HeaderExtensionXml("abc", "123"), 319 HeaderExtensionXml("def", "456")))); 320 321 std::vector<cricket::RtpHeaderExtension> expected_hdrexts; 322 expected_hdrexts.push_back(RtpHeaderExtension("abc", 123)); 323 expected_hdrexts.push_back(RtpHeaderExtension("def", 456)); 324 325 talk_base::scoped_ptr<buzz::XmlElement> actual_desc_elem( 326 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT)); 327 cricket::WriteJingleRtpHeaderExtensions(expected_hdrexts, actual_desc_elem.get()); 328 329 ASSERT_TRUE(actual_desc_elem != NULL); 330 EXPECT_EQ(expected_desc_elem->Str(), actual_desc_elem->Str()); 331 332 std::vector<cricket::RtpHeaderExtension> actual_hdrexts; 333 cricket::ParseError parse_error; 334 ASSERT_TRUE(cricket::ParseJingleRtpHeaderExtensions( 335 expected_desc_elem.get(), &actual_hdrexts, &parse_error)); 336 EXPECT_EQ(2U, actual_hdrexts.size()); 337 EXPECT_EQ(expected_hdrexts[0], actual_hdrexts[0]); 338 EXPECT_EQ(expected_hdrexts[1], actual_hdrexts[1]); 339 } 340 341 // Test deserializing bad RTP header extension xml. 342 TEST_F(MediaMessagesTest, HeaderExtensionsFromBadXml) { 343 std::vector<cricket::RtpHeaderExtension> actual_hdrexts; 344 cricket::ParseError parse_error; 345 346 talk_base::scoped_ptr<buzz::XmlElement> desc_elem( 347 buzz::XmlElement::ForStr( 348 HeaderExtensionsXml( 349 HeaderExtensionXml("abc", "123"), 350 HeaderExtensionXml("def", "not-an-id")))); 351 ASSERT_FALSE(cricket::ParseJingleRtpHeaderExtensions( 352 desc_elem.get(), &actual_hdrexts, &parse_error)); 353 354 desc_elem.reset( 355 buzz::XmlElement::ForStr( 356 HeaderExtensionsXml( 357 HeaderExtensionXml("abc", "123"), 358 HeaderExtensionXml("def", "-1")))); 359 ASSERT_FALSE(cricket::ParseJingleRtpHeaderExtensions( 360 desc_elem.get(), &actual_hdrexts, &parse_error)); 361 } 362 363 } // namespace cricket 364