// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/protocol/jingle_messages.h"
#include "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
#include "third_party/libjingle/source/talk/xmpp/constants.h"
using buzz::QName;
using buzz::XmlAttr;
using buzz::XmlElement;
namespace remoting {
namespace protocol {
namespace {
const char kXmlNsNs[] = "http://www.w3.org/2000/xmlns/";
const char kXmlNs[] = "xmlns";
// Compares two XML blobs and returns true if they are
// equivalent. Otherwise |error| is set to error message that
// specifies the first test.
bool VerifyXml(const XmlElement* exp,
const XmlElement* val,
std::string* error) {
if (exp->Name() != val->Name()) {
*error = "<" + exp->Name().Merged() + ">" + " is expected, but " +
"<" + val->Name().Merged() + ">" + " found";
return false;
}
if (exp->BodyText() != val->BodyText()) {
*error = "<" + exp->Name().LocalPart() + ">" + exp->BodyText() +
"" + exp->Name().LocalPart() + ">" " is expected, but found " +
"<" + exp->Name().LocalPart() + ">" + val->BodyText() +
"" + exp->Name().LocalPart() + ">";
return false;
}
for (const XmlAttr* exp_attr = exp->FirstAttr(); exp_attr != NULL;
exp_attr = exp_attr->NextAttr()) {
if (exp_attr->Name().Namespace() == kXmlNsNs ||
exp_attr->Name() == QName(kXmlNs)) {
continue; // Skip NS attributes.
}
if (val->Attr(exp_attr->Name()) != exp_attr->Value()) {
*error = "In <" + exp->Name().LocalPart() + "> attribute " +
exp_attr->Name().LocalPart() + " is expected to be set to " +
exp_attr->Value();
return false;
}
}
for (const XmlAttr* val_attr = val->FirstAttr(); val_attr;
val_attr = val_attr->NextAttr()) {
if (val_attr->Name().Namespace() == kXmlNsNs ||
val_attr->Name() == QName(kXmlNs)) {
continue; // Skip NS attributes.
}
if (exp->Attr(val_attr->Name()) != val_attr->Value()) {
*error = "In <" + exp->Name().LocalPart() + "> unexpected attribute " +
val_attr->Name().LocalPart();
return false;
}
}
const XmlElement* exp_child = exp->FirstElement();
const XmlElement* val_child = val->FirstElement();
while (exp_child && val_child) {
if (!VerifyXml(exp_child, val_child, error))
return false;
exp_child = exp_child->NextElement();
val_child = val_child->NextElement();
}
if (exp_child) {
*error = "<" + exp_child->Name().Merged() + "> is expected, but not found";
return false;
}
if (val_child) {
*error = "Unexpected <" + val_child->Name().Merged() + "> found";
return false;
}
return true;
}
} // namespace
// Each of the tests below try to parse a message, format it again,
// and then verify that the formatted message is the same as the
// original stanza. The sample messages were generated by libjingle.
TEST(JingleMessageTest, SessionInitiate) {
const char* kTestSessionInitiateMessage =
""
""
""
""
""
""
""
""
""
""
"j7whCMii0Z0AAPwj7whCM/j7whCMii0Z0AAPw="
""
""
""
""
""
"";
scoped_ptr source_message(
XmlElement::ForStr(kTestSessionInitiateMessage));
ASSERT_TRUE(source_message.get());
EXPECT_TRUE(JingleMessage::IsJingleMessage(source_message.get()));
JingleMessage message;
std::string error;
EXPECT_TRUE(message.ParseXml(source_message.get(), &error)) << error;
EXPECT_EQ(message.action, JingleMessage::SESSION_INITIATE);
scoped_ptr formatted_message(message.ToXml());
ASSERT_TRUE(formatted_message.get());
EXPECT_TRUE(VerifyXml(formatted_message.get(), source_message.get(), &error))
<< error;
}
TEST(JingleMessageTest, SessionAccept) {
const char* kTestSessionAcceptMessage =
""
"i"
""
""
""
""
""
""
""
""
"MIICpjCCAY6gW0Cert0TANBgkqhkiG9w0BAQUFA="
""
""
""
""
""
"";
scoped_ptr source_message(
XmlElement::ForStr(kTestSessionAcceptMessage));
ASSERT_TRUE(source_message.get());
EXPECT_TRUE(JingleMessage::IsJingleMessage(source_message.get()));
JingleMessage message;
std::string error;
EXPECT_TRUE(message.ParseXml(source_message.get(), &error)) << error;
EXPECT_EQ(message.action, JingleMessage::SESSION_ACCEPT);
scoped_ptr formatted_message(message.ToXml());
ASSERT_TRUE(formatted_message.get());
EXPECT_TRUE(VerifyXml(source_message.get(), formatted_message.get(), &error))
<< error;
}
TEST(JingleMessageTest, TransportInfo) {
const char* kTestTransportInfoMessage =
""
"";
scoped_ptr source_message(
XmlElement::ForStr(kTestTransportInfoMessage));
ASSERT_TRUE(source_message.get());
EXPECT_TRUE(JingleMessage::IsJingleMessage(source_message.get()));
JingleMessage message;
std::string error;
EXPECT_TRUE(message.ParseXml(source_message.get(), &error)) << error;
EXPECT_EQ(message.action, JingleMessage::TRANSPORT_INFO);
EXPECT_EQ(message.candidates.size(), 2U);
scoped_ptr formatted_message(message.ToXml());
ASSERT_TRUE(formatted_message.get());
EXPECT_TRUE(VerifyXml(source_message.get(), formatted_message.get(), &error))
<< error;
}
TEST(JingleMessageTest, SessionTerminate) {
const char* kTestSessionTerminateMessage =
""
"";
scoped_ptr source_message(
XmlElement::ForStr(kTestSessionTerminateMessage));
ASSERT_TRUE(source_message.get());
EXPECT_TRUE(JingleMessage::IsJingleMessage(source_message.get()));
JingleMessage message;
std::string error;
EXPECT_TRUE(message.ParseXml(source_message.get(), &error)) << error;
EXPECT_EQ(message.action, JingleMessage::SESSION_TERMINATE);
scoped_ptr formatted_message(message.ToXml());
ASSERT_TRUE(formatted_message.get());
EXPECT_TRUE(VerifyXml(source_message.get(), formatted_message.get(), &error))
<< error;
}
TEST(JingleMessageTest, SessionInfo) {
const char* kTestSessionTerminateMessage =
"TestMessage"
"";
scoped_ptr source_message(
XmlElement::ForStr(kTestSessionTerminateMessage));
ASSERT_TRUE(source_message.get());
EXPECT_TRUE(JingleMessage::IsJingleMessage(source_message.get()));
JingleMessage message;
std::string error;
EXPECT_TRUE(message.ParseXml(source_message.get(), &error)) << error;
EXPECT_EQ(message.action, JingleMessage::SESSION_INFO);
ASSERT_TRUE(message.info.get() != NULL);
EXPECT_TRUE(message.info->Name() ==
buzz::QName("urn:xmpp:jingle:1", "test-info"));
scoped_ptr formatted_message(message.ToXml());
ASSERT_TRUE(formatted_message.get());
EXPECT_TRUE(VerifyXml(source_message.get(), formatted_message.get(), &error))
<< error;
}
TEST(JingleMessageReplyTest, ToXml) {
const char* kTestIncomingMessage =
""
"";
scoped_ptr incoming_message(
XmlElement::ForStr(kTestIncomingMessage));
ASSERT_TRUE(incoming_message.get());
struct TestCase {
const JingleMessageReply::ErrorType error;
std::string error_text;
std::string expected_text;
} tests[] = {
{ JingleMessageReply::BAD_REQUEST, "", ""
""
"" },
{ JingleMessageReply::BAD_REQUEST, "ErrorText", ""
""
"ErrorText" },
{ JingleMessageReply::NOT_IMPLEMENTED, "", ""
""
"" },
{ JingleMessageReply::INVALID_SID, "", ""
""
"Invalid SID" },
{ JingleMessageReply::INVALID_SID, "ErrorText", ""
""
"ErrorText" },
{ JingleMessageReply::UNEXPECTED_REQUEST, "", ""
""
"" },
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
JingleMessageReply reply_msg;
if (tests[i].error_text.empty()) {
reply_msg = JingleMessageReply(tests[i].error);
} else {
reply_msg = JingleMessageReply(tests[i].error, tests[i].error_text);
}
scoped_ptr reply(reply_msg.ToXml(incoming_message.get()));
scoped_ptr expected(XmlElement::ForStr(tests[i].expected_text));
ASSERT_TRUE(expected.get());
std::string error;
EXPECT_TRUE(VerifyXml(expected.get(), reply.get(), &error)) << error;
}
}
TEST(JingleMessageTest, ErrorMessage) {
const char* kTestSessionInitiateErrorMessage =
""
""
""
""
""
""
""
""
""
""
"j7whCMii0Z0AAPwj7whCM/j7whCMii0Z0AAPw="
""
""
""
""
""
""
""
""
"";
scoped_ptr source_message(
XmlElement::ForStr(kTestSessionInitiateErrorMessage));
ASSERT_TRUE(source_message.get());
EXPECT_FALSE(JingleMessage::IsJingleMessage(source_message.get()));
JingleMessage message;
std::string error;
EXPECT_FALSE(message.ParseXml(source_message.get(), &error));
EXPECT_FALSE(error.empty());
}
} // namespace protocol
} // namespace remoting