普通文本  |  197行  |  6.21 KB

// Copyright 2013 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/host/host_status_sender.h"

#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "remoting/base/constants.h"
#include "remoting/base/logging.h"
#include "remoting/base/rsa_key_pair.h"
#include "remoting/base/test_rsa_key_pair.h"
#include "remoting/host/host_exit_codes.h"
#include "remoting/signaling/mock_signal_strategy.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/webrtc/libjingle/xmllite/xmlelement.h"

using buzz::QName;
using buzz::XmlElement;

using testing::DoAll;
using testing::NotNull;
using testing::Return;
using testing::SaveArg;

namespace remoting {

namespace {

const char kTestBotJid[] = "remotingunittest@bot.talk.google.com";
const char kHostId[] = "0";
const char kTestJid[] = "user@gmail.com/chromoting123";
const char kStanzaId[] = "123";

const HostExitCodes kTestExitCode = kInvalidHostConfigurationExitCode;
const char kTestExitCodeString[] = "INVALID_HOST_CONFIGURATION";

}  // namespace

class HostStatusSenderTest
    : public testing::Test {
 protected:
  virtual void SetUp() OVERRIDE {
    key_pair_ = RsaKeyPair::FromString(kTestRsaKeyPair);
    ASSERT_TRUE(key_pair_.get());

    host_status_sender_.reset(new HostStatusSender(
        kHostId, &signal_strategy_, key_pair_, kTestBotJid));
  }

  virtual void TearDown() OVERRIDE {
    host_status_sender_.reset();
  }

  void ValidateHostStatusStanza(XmlElement* stanza,
                                HostStatusSender::HostStatus status);

  void ValidateSignature(
      XmlElement* signature, HostStatusSender::HostStatus status);

  MockSignalStrategy signal_strategy_;
  scoped_refptr<RsaKeyPair> key_pair_;
  scoped_ptr<HostStatusSender> host_status_sender_;
};

TEST_F(HostStatusSenderTest, SendOnlineStatus) {
  XmlElement* sent_iq = NULL;
  EXPECT_CALL(signal_strategy_, GetState())
      .WillOnce(Return(SignalStrategy::DISCONNECTED))
      .WillRepeatedly(Return(SignalStrategy::CONNECTED));
  EXPECT_CALL(signal_strategy_, GetLocalJid())
      .WillRepeatedly(Return(kTestJid));
  EXPECT_CALL(signal_strategy_, GetNextId())
      .WillOnce(Return(kStanzaId));
  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
      .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));

  // Call SendOnlineStatus twice. The first call should be a
  // no-op because |signal_strategy_| is diconnected.
  // So we expect SendStanza to be called only once.
  host_status_sender_->SendOnlineStatus();

  host_status_sender_->OnSignalStrategyStateChange(
      SignalStrategy::CONNECTED);
  host_status_sender_->SendOnlineStatus();

  scoped_ptr<XmlElement> stanza(sent_iq);

  ASSERT_TRUE(stanza != NULL);

  ValidateHostStatusStanza(stanza.get(), HostStatusSender::ONLINE);
}

TEST_F(HostStatusSenderTest, SendOfflineStatus) {
  XmlElement* sent_iq = NULL;
  EXPECT_CALL(signal_strategy_, GetState())
      .WillOnce(Return(SignalStrategy::DISCONNECTED))
      .WillRepeatedly(Return(SignalStrategy::CONNECTED));
  EXPECT_CALL(signal_strategy_, GetLocalJid())
      .WillRepeatedly(Return(kTestJid));
  EXPECT_CALL(signal_strategy_, GetNextId())
      .WillOnce(Return(kStanzaId));
  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
      .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));

  // Call SendOfflineStatus twice. The first call should be a
  // no-op because |signal_strategy_| is diconnected.
  // So we expect SendStanza to be called only once.
  host_status_sender_->SendOfflineStatus(kTestExitCode);

  host_status_sender_->OnSignalStrategyStateChange(
      SignalStrategy::CONNECTED);
  host_status_sender_->SendOfflineStatus(kTestExitCode);

  scoped_ptr<XmlElement> stanza(sent_iq);

  ASSERT_TRUE(stanza != NULL);

  ValidateHostStatusStanza(stanza.get(), HostStatusSender::OFFLINE);
}

// Validate a host status stanza.
void HostStatusSenderTest::ValidateHostStatusStanza(
    XmlElement* stanza, HostStatusSender::HostStatus status) {
  EXPECT_EQ(stanza->Attr(QName(std::string(), "to")),
            std::string(kTestBotJid));
  EXPECT_EQ(stanza->Attr(QName(std::string(), "type")), "set");

  XmlElement* host_status_stanza =
      stanza->FirstNamed(QName(kChromotingXmlNamespace, "host-status"));
  ASSERT_TRUE(host_status_stanza != NULL);

  if (status == HostStatusSender::ONLINE) {
    EXPECT_EQ("ONLINE",
              host_status_stanza->Attr(
                  QName(kChromotingXmlNamespace, "status")));
    EXPECT_FALSE(host_status_stanza->HasAttr(
        QName(kChromotingXmlNamespace, "exit-code")));
  } else {
    EXPECT_EQ("OFFLINE",
              host_status_stanza->Attr(
                  QName(kChromotingXmlNamespace, "status")));
    EXPECT_EQ(kTestExitCodeString,
              host_status_stanza->Attr(
                  QName(kChromotingXmlNamespace, "exit-code")));
  }

  EXPECT_EQ(std::string(kHostId),
            host_status_stanza->Attr(
                QName(kChromotingXmlNamespace, "hostid")));

  QName signature_tag(kChromotingXmlNamespace, "signature");
  XmlElement* signature = host_status_stanza->FirstNamed(signature_tag);
  ASSERT_TRUE(signature != NULL);
  EXPECT_TRUE(host_status_stanza->NextNamed(signature_tag) == NULL);

  ValidateSignature(signature, status);
}

// Validate the signature.
void HostStatusSenderTest::ValidateSignature(
    XmlElement* signature, HostStatusSender::HostStatus status) {

  EXPECT_TRUE(signature->HasAttr(
      QName(kChromotingXmlNamespace, "time")));

  std::string time_str =
      signature->Attr(QName(kChromotingXmlNamespace, "time"));

  int64 time;
  ASSERT_TRUE(base::StringToInt64(time_str, &time));

  std::string message;
  message += kTestJid;
  message += " ";
  message += time_str;
  message += " ";

  if (status == HostStatusSender::OFFLINE) {
    message += "OFFLINE";
    message += " ";
    message += kTestExitCodeString;
  } else {
    message += "ONLINE";
  }

  scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::FromString(kTestRsaKeyPair);
  ASSERT_TRUE(key_pair.get());

  std::string expected_signature =
      key_pair->SignMessage(message);
  EXPECT_EQ(expected_signature, signature->BodyText());
}

}  // namespace remoting