普通文本  |  173行  |  6.02 KB

// Copyright 2015 The Weave 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 "src/notification/xmpp_iq_stanza_handler.h"

#include <map>
#include <memory>

#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <weave/provider/test/fake_task_runner.h>

#include "src/bind_lambda.h"
#include "src/notification/xml_node.h"
#include "src/notification/xmpp_channel.h"
#include "src/notification/xmpp_stream_parser.h"

using testing::_;

namespace weave {
namespace {

class MockXmppChannelInterface : public XmppChannelInterface {
 public:
  MockXmppChannelInterface() = default;

  MOCK_METHOD1(SendMessage, void(const std::string&));

 private:
  DISALLOW_COPY_AND_ASSIGN(MockXmppChannelInterface);
};

// Simple class that allows to parse XML from string to XmlNode.
class XmlParser : public XmppStreamParser::Delegate {
 public:
  std::unique_ptr<XmlNode> Parse(const std::string& xml) {
    parser_.ParseData(xml);
    return std::move(node_);
  }

 private:
  // Overrides from XmppStreamParser::Delegate.
  void OnStreamStart(const std::string& node_name,
                     std::map<std::string, std::string> attributes) override {
    node_.reset(new XmlNode{node_name, std::move(attributes)});
  }

  void OnStreamEnd(const std::string& node_name) override {}

  void OnStanza(std::unique_ptr<XmlNode> stanza) override {
    node_->AddChild(std::move(stanza));
  }

  std::unique_ptr<XmlNode> node_;
  XmppStreamParser parser_{this};
};

class MockResponseReceiver {
 public:
  MOCK_METHOD2(OnResponse, void(int id, const std::string&));

  IqStanzaHandler::ResponseCallback callback(int id) {
    return base::Bind(&MockResponseReceiver::OnResponseCallback,
                      base::Unretained(this), id);
  }

 private:
  void OnResponseCallback(int id, std::unique_ptr<XmlNode> response) {
    OnResponse(id, response->children().front()->name());
  }
};

}  // anonymous namespace

class IqStanzaHandlerTest : public testing::Test {
 public:
  testing::StrictMock<MockXmppChannelInterface> mock_xmpp_channel_;
  provider::test::FakeTaskRunner task_runner_;
  IqStanzaHandler iq_stanza_handler_{&mock_xmpp_channel_, &task_runner_};
  MockResponseReceiver receiver_;
};

TEST_F(IqStanzaHandlerTest, SendRequest) {
  std::string expected_msg = "<iq id='1' type='set'><body/></iq>";
  EXPECT_CALL(mock_xmpp_channel_, SendMessage(expected_msg)).Times(1);
  iq_stanza_handler_.SendRequest("set", "", "", "<body/>", {}, {});

  expected_msg = "<iq id='2' type='get'><body/></iq>";
  EXPECT_CALL(mock_xmpp_channel_, SendMessage(expected_msg)).Times(1);
  iq_stanza_handler_.SendRequest("get", "", "", "<body/>", {}, {});

  expected_msg = "<iq id='3' type='query' from='foo@bar'><body/></iq>";
  EXPECT_CALL(mock_xmpp_channel_, SendMessage(expected_msg)).Times(1);
  iq_stanza_handler_.SendRequest("query", "foo@bar", "", "<body/>", {}, {});

  expected_msg = "<iq id='4' type='query' to='foo@bar'><body/></iq>";
  EXPECT_CALL(mock_xmpp_channel_, SendMessage(expected_msg)).Times(1);
  iq_stanza_handler_.SendRequest("query", "", "foo@bar", "<body/>", {}, {});

  expected_msg = "<iq id='5' type='query' from='foo@bar' to='baz'><body/></iq>";
  EXPECT_CALL(mock_xmpp_channel_, SendMessage(expected_msg)).Times(1);
  iq_stanza_handler_.SendRequest("query", "foo@bar", "baz", "<body/>", {}, {});
  // This test ignores all the posted callbacks.
}

TEST_F(IqStanzaHandlerTest, UnsupportedIqRequest) {
  // Server IQ requests are not supported for now. Expect an error response.
  std::string expected_msg =
      "<iq id='1' type='error'><error type='modify'>"
      "<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
      "</error></iq>";
  EXPECT_CALL(mock_xmpp_channel_, SendMessage(expected_msg)).Times(1);
  auto request = XmlParser{}.Parse("<iq id='1' type='set'><foo/></iq>");
  EXPECT_TRUE(iq_stanza_handler_.HandleIqStanza(std::move(request)));
}

TEST_F(IqStanzaHandlerTest, UnknownResponseId) {
  // No requests with ID=100 have been previously sent.
  auto request = XmlParser{}.Parse("<iq id='100' type='result'><foo/></iq>");
  EXPECT_TRUE(iq_stanza_handler_.HandleIqStanza(std::move(request)));
}

TEST_F(IqStanzaHandlerTest, SequentialResponses) {
  EXPECT_CALL(mock_xmpp_channel_, SendMessage(_)).Times(2);
  iq_stanza_handler_.SendRequest("set", "", "", "<body/>",
                                 receiver_.callback(1), {});
  iq_stanza_handler_.SendRequest("get", "", "", "<body/>",
                                 receiver_.callback(2), {});

  EXPECT_CALL(receiver_, OnResponse(1, "foo"));
  auto request = XmlParser{}.Parse("<iq id='1' type='result'><foo/></iq>");
  EXPECT_TRUE(iq_stanza_handler_.HandleIqStanza(std::move(request)));

  EXPECT_CALL(receiver_, OnResponse(2, "bar"));
  request = XmlParser{}.Parse("<iq id='2' type='result'><bar/></iq>");
  EXPECT_TRUE(iq_stanza_handler_.HandleIqStanza(std::move(request)));

  task_runner_.Run();
}

TEST_F(IqStanzaHandlerTest, OutOfOrderResponses) {
  EXPECT_CALL(mock_xmpp_channel_, SendMessage(_)).Times(2);
  iq_stanza_handler_.SendRequest("set", "", "", "<body/>",
                                 receiver_.callback(1), {});
  iq_stanza_handler_.SendRequest("get", "", "", "<body/>",
                                 receiver_.callback(2), {});

  EXPECT_CALL(receiver_, OnResponse(2, "bar"));
  auto request = XmlParser{}.Parse("<iq id='2' type='result'><bar/></iq>");
  EXPECT_TRUE(iq_stanza_handler_.HandleIqStanza(std::move(request)));

  EXPECT_CALL(receiver_, OnResponse(1, "foo"));
  request = XmlParser{}.Parse("<iq id='1' type='result'><foo/></iq>");
  EXPECT_TRUE(iq_stanza_handler_.HandleIqStanza(std::move(request)));

  task_runner_.Run();
}

TEST_F(IqStanzaHandlerTest, RequestTimeout) {
  bool called = false;
  auto on_timeout = [&called]() { called = true; };

  EXPECT_CALL(mock_xmpp_channel_, SendMessage(_)).Times(1);
  EXPECT_FALSE(called);
  iq_stanza_handler_.SendRequest("set", "", "", "<body/>", {},
                                 base::Bind(on_timeout));
  task_runner_.Run();
  EXPECT_TRUE(called);
}

}  // namespace weave