henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 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 | |
henrike@webrtc.org | 1e09a71 | 2013-07-26 19:17:59 +0000 | [diff] [blame] | 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 | |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 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()); |
henrike@webrtc.org | 1e09a71 | 2013-07-26 19:17:59 +0000 | [diff] [blame] | 189 | ClearXmlElements(&actual_view_elems); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 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()); |
henrike@webrtc.org | 1e09a71 | 2013-07-26 19:17:59 +0000 | [diff] [blame] | 224 | ClearXmlElements(&actual_view_elems); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 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 |