1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "remoting/protocol/jingle_messages.h"
7 #include "base/logging.h"
8 #include "testing/gmock/include/gmock/gmock.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
11 #include "third_party/webrtc/libjingle/xmpp/constants.h"
15 using buzz::XmlElement
;
22 const char kXmlNsNs
[] = "http://www.w3.org/2000/xmlns/";
23 const char kXmlNs
[] = "xmlns";
25 // Compares two XML blobs and returns true if they are
26 // equivalent. Otherwise |error| is set to error message that
27 // specifies the first test.
28 bool VerifyXml(const XmlElement
* exp
,
29 const XmlElement
* val
,
31 if (exp
->Name() != val
->Name()) {
32 *error
= "<" + exp
->Name().Merged() + ">" + " is expected, but " +
33 "<" + val
->Name().Merged() + ">" + " found";
36 if (exp
->BodyText() != val
->BodyText()) {
37 *error
= "<" + exp
->Name().LocalPart() + ">" + exp
->BodyText() +
38 "</" + exp
->Name().LocalPart() + ">" " is expected, but found " +
39 "<" + exp
->Name().LocalPart() + ">" + val
->BodyText() +
40 "</" + exp
->Name().LocalPart() + ">";
44 for (const XmlAttr
* exp_attr
= exp
->FirstAttr(); exp_attr
!= nullptr;
45 exp_attr
= exp_attr
->NextAttr()) {
46 if (exp_attr
->Name().Namespace() == kXmlNsNs
||
47 exp_attr
->Name() == QName(kXmlNs
)) {
48 continue; // Skip NS attributes.
50 if (val
->Attr(exp_attr
->Name()) != exp_attr
->Value()) {
51 *error
= "In <" + exp
->Name().LocalPart() + "> attribute " +
52 exp_attr
->Name().LocalPart() + " is expected to be set to " +
58 for (const XmlAttr
* val_attr
= val
->FirstAttr(); val_attr
;
59 val_attr
= val_attr
->NextAttr()) {
60 if (val_attr
->Name().Namespace() == kXmlNsNs
||
61 val_attr
->Name() == QName(kXmlNs
)) {
62 continue; // Skip NS attributes.
64 if (exp
->Attr(val_attr
->Name()) != val_attr
->Value()) {
65 *error
= "In <" + exp
->Name().LocalPart() + "> unexpected attribute " +
66 val_attr
->Name().LocalPart();
71 const XmlElement
* exp_child
= exp
->FirstElement();
72 const XmlElement
* val_child
= val
->FirstElement();
73 while (exp_child
&& val_child
) {
74 if (!VerifyXml(exp_child
, val_child
, error
))
76 exp_child
= exp_child
->NextElement();
77 val_child
= val_child
->NextElement();
80 *error
= "<" + exp_child
->Name().Merged() + "> is expected, but not found";
85 *error
= "Unexpected <" + val_child
->Name().Merged() + "> found";
94 // Each of the tests below try to parse a message, format it again,
95 // and then verify that the formatted message is the same as the
96 // original stanza. The sample messages were generated by libjingle.
98 TEST(JingleMessageTest
, SessionInitiate
) {
99 const char* kTestSessionInitiateMessage
=
100 "<iq to='user@gmail.com/chromoting016DBB07' type='set' "
101 "from='user@gmail.com/chromiumsy5C6A652D' "
102 "xmlns='jabber:client'>"
103 "<jingle xmlns='urn:xmpp:jingle:1' "
104 "action='session-initiate' sid='2227053353' "
105 "initiator='user@gmail.com/chromiumsy5C6A652D'>"
106 "<content name='chromoting' creator='initiator'>"
107 "<description xmlns='google:remoting'>"
109 "<control transport='stream' version='2'/>"
110 "<event transport='stream' version='2'/>"
111 "<video transport='stream' version='2' codec='vp8'/>"
112 "<audio transport='stream' version='2' codec='verbatim'/>"
113 "<initial-resolution width='640' height='480'/>"
114 "<authentication><auth-token>"
115 "j7whCMii0Z0AAPwj7whCM/j7whCMii0Z0AAPw="
116 "</auth-token></authentication>"
121 scoped_ptr
<XmlElement
> source_message(
122 XmlElement::ForStr(kTestSessionInitiateMessage
));
123 ASSERT_TRUE(source_message
.get());
125 EXPECT_TRUE(JingleMessage::IsJingleMessage(source_message
.get()));
127 JingleMessage message
;
129 EXPECT_TRUE(message
.ParseXml(source_message
.get(), &error
)) << error
;
131 EXPECT_EQ(message
.action
, JingleMessage::SESSION_INITIATE
);
133 scoped_ptr
<XmlElement
> formatted_message(message
.ToXml());
134 ASSERT_TRUE(formatted_message
.get());
135 EXPECT_TRUE(VerifyXml(formatted_message
.get(), source_message
.get(), &error
))
139 TEST(JingleMessageTest
, SessionAccept
) {
140 const char* kTestSessionAcceptMessage
=
141 "<cli:iq from='user@gmail.com/chromoting016DBB07' "
142 "to='user@gmail.com/chromiumsy5C6A652D' type='set' "
143 "xmlns:cli='jabber:client'>"
144 "<jingle action='session-accept' sid='2227053353' "
145 "xmlns='urn:xmpp:jingle:1'>i"
146 "<content creator='initiator' name='chromoting'>"
147 "<description xmlns='google:remoting'>"
149 "<control transport='stream' version='2'/>"
150 "<event transport='stream' version='2'/>"
151 "<video codec='vp8' transport='stream' version='2'/>"
152 "<audio transport='stream' version='2' codec='verbatim'/>"
153 "<initial-resolution height='480' width='640'/>"
154 "<authentication><certificate>"
155 "MIICpjCCAY6gW0Cert0TANBgkqhkiG9w0BAQUFA="
156 "</certificate></authentication>"
162 scoped_ptr
<XmlElement
> source_message(
163 XmlElement::ForStr(kTestSessionAcceptMessage
));
164 ASSERT_TRUE(source_message
.get());
166 EXPECT_TRUE(JingleMessage::IsJingleMessage(source_message
.get()));
168 JingleMessage message
;
170 EXPECT_TRUE(message
.ParseXml(source_message
.get(), &error
)) << error
;
172 EXPECT_EQ(message
.action
, JingleMessage::SESSION_ACCEPT
);
174 scoped_ptr
<XmlElement
> formatted_message(message
.ToXml());
175 ASSERT_TRUE(formatted_message
.get());
176 EXPECT_TRUE(VerifyXml(source_message
.get(), formatted_message
.get(), &error
))
180 TEST(JingleMessageTest
, SessionAcceptNoIce
) {
181 const char* kTestSessionAcceptMessage
=
182 "<cli:iq from='user@gmail.com/chromoting016DBB07' "
183 "to='user@gmail.com/chromiumsy5C6A652D' type='set' "
184 "xmlns:cli='jabber:client'>"
185 "<jingle action='session-accept' sid='2227053353' "
186 "xmlns='urn:xmpp:jingle:1'>i"
187 "<content creator='initiator' name='chromoting'>"
188 "<description xmlns='google:remoting'>"
189 "<control transport='stream' version='2'/>"
190 "<event transport='stream' version='2'/>"
191 "<video codec='vp8' transport='stream' version='2'/>"
192 "<audio transport='stream' version='2' codec='verbatim'/>"
193 "<initial-resolution height='480' width='640'/>"
194 "<authentication><certificate>"
195 "MIICpjCCAY6gW0Cert0TANBgkqhkiG9w0BAQUFA="
196 "</certificate></authentication>"
202 scoped_ptr
<XmlElement
> source_message(
203 XmlElement::ForStr(kTestSessionAcceptMessage
));
204 ASSERT_TRUE(source_message
.get());
206 EXPECT_TRUE(JingleMessage::IsJingleMessage(source_message
.get()));
208 JingleMessage message
;
210 EXPECT_TRUE(message
.ParseXml(source_message
.get(), &error
)) << error
;
212 EXPECT_EQ(message
.action
, JingleMessage::SESSION_ACCEPT
);
214 scoped_ptr
<XmlElement
> formatted_message(message
.ToXml());
215 ASSERT_TRUE(formatted_message
.get());
216 EXPECT_TRUE(VerifyXml(source_message
.get(), formatted_message
.get(), &error
))
220 TEST(JingleMessageTest
, IceTransportInfo
) {
221 const char* kTestIceTransportInfoMessage
=
222 "<cli:iq to='user@gmail.com/chromoting016DBB07' type='set' "
223 "xmlns:cli='jabber:client'>"
224 "<jingle xmlns='urn:xmpp:jingle:1' action='transport-info' "
226 "<content name='chromoting' creator='initiator'>"
227 "<transport xmlns='google:remoting:ice'>"
228 "<credentials channel='event' ufrag='tPUyEAmQrEw3y7hi' "
229 "password='2iRdhLfawKZC5ydJ'/>"
230 "<credentials channel='video' ufrag='EPK3CXo5sTLJSez0' "
231 "password='eM0VUfUkZ+1Pyi0M'/>"
232 "<candidate name='event' foundation='725747215' "
233 "address='172.23.164.186' port='59089' type='local' "
234 "protocol='udp' priority='2122194688' generation='0'/>"
235 "<candidate name='video' foundation='3623806809' "
236 "address='172.23.164.186' port='57040' type='local' "
237 "protocol='udp' priority='2122194688' generation='0'/>"
243 scoped_ptr
<XmlElement
> source_message(
244 XmlElement::ForStr(kTestIceTransportInfoMessage
));
245 ASSERT_TRUE(source_message
.get());
247 EXPECT_TRUE(JingleMessage::IsJingleMessage(source_message
.get()));
249 JingleMessage message
;
251 EXPECT_TRUE(message
.ParseXml(source_message
.get(), &error
)) << error
;
253 EXPECT_EQ(message
.action
, JingleMessage::TRANSPORT_INFO
);
254 EXPECT_EQ(message
.candidates
.size(), 2U);
256 scoped_ptr
<XmlElement
> formatted_message(message
.ToXml());
257 ASSERT_TRUE(formatted_message
.get());
258 EXPECT_TRUE(VerifyXml(source_message
.get(), formatted_message
.get(), &error
))
262 TEST(JingleMessageTest
, SessionTerminate
) {
263 const char* kTestSessionTerminateMessage
=
264 "<cli:iq from='user@gmail.com/chromoting016DBB07' "
265 "to='user@gmail.com/chromiumsy5C6A652D' type='set' "
266 "xmlns:cli='jabber:client'><jingle action='session-terminate' "
267 "sid='2227053353' xmlns='urn:xmpp:jingle:1'><reason><success/>"
268 "</reason></jingle></cli:iq>";
270 scoped_ptr
<XmlElement
> source_message(
271 XmlElement::ForStr(kTestSessionTerminateMessage
));
272 ASSERT_TRUE(source_message
.get());
274 EXPECT_TRUE(JingleMessage::IsJingleMessage(source_message
.get()));
276 JingleMessage message
;
278 EXPECT_TRUE(message
.ParseXml(source_message
.get(), &error
)) << error
;
280 EXPECT_EQ(message
.action
, JingleMessage::SESSION_TERMINATE
);
282 scoped_ptr
<XmlElement
> formatted_message(message
.ToXml());
283 ASSERT_TRUE(formatted_message
.get());
284 EXPECT_TRUE(VerifyXml(source_message
.get(), formatted_message
.get(), &error
))
288 TEST(JingleMessageTest
, SessionInfo
) {
289 const char* kTestSessionTerminateMessage
=
290 "<cli:iq from='user@gmail.com/chromoting016DBB07' "
291 "to='user@gmail.com/chromiumsy5C6A652D' type='set' "
292 "xmlns:cli='jabber:client'><jingle action='session-info' "
293 "sid='2227053353' xmlns='urn:xmpp:jingle:1'><test-info>TestMessage"
294 "</test-info></jingle></cli:iq>";
296 scoped_ptr
<XmlElement
> source_message(
297 XmlElement::ForStr(kTestSessionTerminateMessage
));
298 ASSERT_TRUE(source_message
.get());
300 EXPECT_TRUE(JingleMessage::IsJingleMessage(source_message
.get()));
302 JingleMessage message
;
304 EXPECT_TRUE(message
.ParseXml(source_message
.get(), &error
)) << error
;
306 EXPECT_EQ(message
.action
, JingleMessage::SESSION_INFO
);
307 ASSERT_TRUE(message
.info
.get() != nullptr);
308 EXPECT_TRUE(message
.info
->Name() ==
309 buzz::QName("urn:xmpp:jingle:1", "test-info"));
311 scoped_ptr
<XmlElement
> formatted_message(message
.ToXml());
312 ASSERT_TRUE(formatted_message
.get());
313 EXPECT_TRUE(VerifyXml(source_message
.get(), formatted_message
.get(), &error
))
317 TEST(JingleMessageReplyTest
, ToXml
) {
318 const char* kTestIncomingMessage
=
319 "<cli:iq from='user@gmail.com/chromoting016DBB07' id='4' "
320 "to='user@gmail.com/chromiumsy5C6A652D' type='set' "
321 "xmlns:cli='jabber:client'><jingle action='session-terminate' "
322 "sid='2227053353' xmlns='urn:xmpp:jingle:1'><reason><success/>"
323 "</reason></jingle></cli:iq>";
324 scoped_ptr
<XmlElement
> incoming_message(
325 XmlElement::ForStr(kTestIncomingMessage
));
326 ASSERT_TRUE(incoming_message
.get());
329 const JingleMessageReply::ErrorType error
;
330 std::string error_text
;
331 std::string expected_text
;
333 { JingleMessageReply::BAD_REQUEST
, "", "<iq xmlns='jabber:client' "
334 "to='user@gmail.com/chromoting016DBB07' id='4' type='error'><jingle "
335 "action='session-terminate' sid='2227053353' xmlns='urn:xmpp:jingle:1'>"
336 "<reason><success/></reason></jingle><error type='modify'><bad-request/>"
338 { JingleMessageReply::BAD_REQUEST
, "ErrorText", "<iq xmlns='jabber:client' "
339 "to='user@gmail.com/chromoting016DBB07' id='4' type='error'><jingle "
340 "action='session-terminate' sid='2227053353' xmlns='urn:xmpp:jingle:1'>"
341 "<reason><success/></reason></jingle><error type='modify'><bad-request/>"
342 "<text xml:lang='en'>ErrorText</text></error></iq>" },
343 { JingleMessageReply::NOT_IMPLEMENTED
, "", "<iq xmlns='jabber:client' "
344 "to='user@gmail.com/chromoting016DBB07' id='4' type='error'><jingle "
345 "action='session-terminate' sid='2227053353' xmlns='urn:xmpp:jingle:1'>"
346 "<reason><success/></reason></jingle><error type='cancel'>"
347 "<feature-bad-request/></error></iq>" },
348 { JingleMessageReply::INVALID_SID
, "", "<iq xmlns='jabber:client' "
349 "to='user@gmail.com/chromoting016DBB07' id='4' type='error'><jingle "
350 "action='session-terminate' sid='2227053353' xmlns='urn:xmpp:jingle:1'>"
351 "<reason><success/></reason></jingle><error type='modify'>"
352 "<item-not-found/><text xml:lang='en'>Invalid SID</text></error></iq>" },
353 { JingleMessageReply::INVALID_SID
, "ErrorText", "<iq xmlns='jabber:client' "
354 "to='user@gmail.com/chromoting016DBB07' id='4' type='error'><jingle "
355 "action='session-terminate' sid='2227053353' xmlns='urn:xmpp:jingle:1'>"
356 "<reason><success/></reason></jingle><error type='modify'>"
357 "<item-not-found/><text xml:lang='en'>ErrorText</text></error></iq>" },
358 { JingleMessageReply::UNEXPECTED_REQUEST
, "", "<iq xmlns='jabber:client' "
359 "to='user@gmail.com/chromoting016DBB07' id='4' type='error'><jingle "
360 "action='session-terminate' sid='2227053353' xmlns='urn:xmpp:jingle:1'>"
361 "<reason><success/></reason></jingle><error type='modify'>"
362 "<unexpected-request/></error></iq>" },
365 for (size_t i
= 0; i
< arraysize(tests
); ++i
) {
366 JingleMessageReply reply_msg
;
367 if (tests
[i
].error_text
.empty()) {
368 reply_msg
= JingleMessageReply(tests
[i
].error
);
370 reply_msg
= JingleMessageReply(tests
[i
].error
, tests
[i
].error_text
);
372 scoped_ptr
<XmlElement
> reply(reply_msg
.ToXml(incoming_message
.get()));
374 scoped_ptr
<XmlElement
> expected(XmlElement::ForStr(tests
[i
].expected_text
));
375 ASSERT_TRUE(expected
.get());
378 EXPECT_TRUE(VerifyXml(expected
.get(), reply
.get(), &error
)) << error
;
382 TEST(JingleMessageTest
, ErrorMessage
) {
383 const char* kTestSessionInitiateErrorMessage
=
384 "<iq to='user@gmail.com/chromoting016DBB07' type='error' "
385 "from='user@gmail.com/chromiumsy5C6A652D' "
386 "xmlns='jabber:client'>"
387 "<jingle xmlns='urn:xmpp:jingle:1' "
388 "action='session-initiate' sid='2227053353' "
389 "initiator='user@gmail.com/chromiumsy5C6A652D'>"
390 "<content name='chromoting' creator='initiator'>"
391 "<description xmlns='google:remoting'>"
392 "<control transport='stream' version='2'/>"
393 "<event transport='stream' version='2'/>"
394 "<video transport='stream' version='2' codec='vp8'/>"
395 "<audio transport='stream' version='2' codec='verbatim'/>"
396 "<initial-resolution width='800' height='600'/>"
397 "<authentication><auth-token>"
398 "j7whCMii0Z0AAPwj7whCM/j7whCMii0Z0AAPw="
399 "</auth-token></authentication>"
403 "<error code='501' type='cancel'>"
404 "<feature-not-implemented "
405 "xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
408 scoped_ptr
<XmlElement
> source_message(
409 XmlElement::ForStr(kTestSessionInitiateErrorMessage
));
410 ASSERT_TRUE(source_message
.get());
412 EXPECT_FALSE(JingleMessage::IsJingleMessage(source_message
.get()));
414 JingleMessage message
;
416 EXPECT_FALSE(message
.ParseXml(source_message
.get(), &error
));
417 EXPECT_FALSE(error
.empty());
420 } // namespace protocol
421 } // namespace remoting