Home | History | Annotate | Download | only in host
      1 // Copyright 2013 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.
      4 
      5 #include "remoting/host/host_status_sender.h"
      6 
      7 #include "base/strings/string_number_conversions.h"
      8 #include "base/time/time.h"
      9 #include "remoting/base/constants.h"
     10 #include "remoting/base/rsa_key_pair.h"
     11 #include "remoting/base/test_rsa_key_pair.h"
     12 #include "remoting/host/host_exit_codes.h"
     13 #include "remoting/jingle_glue/mock_objects.h"
     14 #include "testing/gmock/include/gmock/gmock.h"
     15 #include "testing/gtest/include/gtest/gtest.h"
     16 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
     17 
     18 using buzz::QName;
     19 using buzz::XmlElement;
     20 
     21 using testing::DoAll;
     22 using testing::NotNull;
     23 using testing::Return;
     24 using testing::SaveArg;
     25 
     26 namespace remoting {
     27 
     28 namespace {
     29 
     30 const char kTestBotJid[] = "remotingunittest (at) bot.talk.google.com";
     31 const char kHostId[] = "0";
     32 const char kTestJid[] = "user (at) gmail.com/chromoting123";
     33 const char kStanzaId[] = "123";
     34 
     35 const HostExitCodes kTestExitCode = kInvalidHostConfigurationExitCode;
     36 const char kTestExitCodeString[] = "INVALID_HOST_CONFIGURATION";
     37 
     38 }  // namespace
     39 
     40 class HostStatusSenderTest
     41     : public testing::Test {
     42  protected:
     43   virtual void SetUp() OVERRIDE {
     44     key_pair_ = RsaKeyPair::FromString(kTestRsaKeyPair);
     45     ASSERT_TRUE(key_pair_.get());
     46 
     47     host_status_sender_.reset(new HostStatusSender(
     48         kHostId, &signal_strategy_, key_pair_, kTestBotJid));
     49   }
     50 
     51   virtual void TearDown() OVERRIDE {
     52     host_status_sender_.reset();
     53   }
     54 
     55   void ValidateHostStatusStanza(XmlElement* stanza,
     56                                 HostStatusSender::HostStatus status);
     57 
     58   void ValidateSignature(
     59       XmlElement* signature, HostStatusSender::HostStatus status);
     60 
     61   MockSignalStrategy signal_strategy_;
     62   scoped_refptr<RsaKeyPair> key_pair_;
     63   scoped_ptr<HostStatusSender> host_status_sender_;
     64 };
     65 
     66 TEST_F(HostStatusSenderTest, SendOnlineStatus) {
     67   XmlElement* sent_iq = NULL;
     68   EXPECT_CALL(signal_strategy_, GetState())
     69       .WillOnce(Return(SignalStrategy::DISCONNECTED))
     70       .WillRepeatedly(Return(SignalStrategy::CONNECTED));
     71   EXPECT_CALL(signal_strategy_, GetLocalJid())
     72       .WillRepeatedly(Return(kTestJid));
     73   EXPECT_CALL(signal_strategy_, GetNextId())
     74       .WillOnce(Return(kStanzaId));
     75   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
     76       .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
     77 
     78   // Call SendOnlineStatus twice. The first call should be a
     79   // no-op because |signal_strategy_| is diconnected.
     80   // So we expect SendStanza to be called only once.
     81   host_status_sender_->SendOnlineStatus();
     82 
     83   host_status_sender_->OnSignalStrategyStateChange(
     84       SignalStrategy::CONNECTED);
     85   host_status_sender_->SendOnlineStatus();
     86 
     87   scoped_ptr<XmlElement> stanza(sent_iq);
     88 
     89   ASSERT_TRUE(stanza != NULL);
     90   LOG(INFO) << stanza->Str();
     91 
     92   ValidateHostStatusStanza(stanza.get(), HostStatusSender::ONLINE);
     93 }
     94 
     95 TEST_F(HostStatusSenderTest, SendOfflineStatus) {
     96   XmlElement* sent_iq = NULL;
     97   EXPECT_CALL(signal_strategy_, GetState())
     98       .WillOnce(Return(SignalStrategy::DISCONNECTED))
     99       .WillRepeatedly(Return(SignalStrategy::CONNECTED));
    100   EXPECT_CALL(signal_strategy_, GetLocalJid())
    101       .WillRepeatedly(Return(kTestJid));
    102   EXPECT_CALL(signal_strategy_, GetNextId())
    103       .WillOnce(Return(kStanzaId));
    104   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
    105       .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
    106 
    107   // Call SendOfflineStatus twice. The first call should be a
    108   // no-op because |signal_strategy_| is diconnected.
    109   // So we expect SendStanza to be called only once.
    110   host_status_sender_->SendOfflineStatus(kTestExitCode);
    111 
    112   host_status_sender_->OnSignalStrategyStateChange(
    113       SignalStrategy::CONNECTED);
    114   host_status_sender_->SendOfflineStatus(kTestExitCode);
    115 
    116   scoped_ptr<XmlElement> stanza(sent_iq);
    117 
    118   ASSERT_TRUE(stanza != NULL);
    119   LOG(INFO) << stanza->Str();
    120 
    121   ValidateHostStatusStanza(stanza.get(), HostStatusSender::OFFLINE);
    122 }
    123 
    124 // Validate a host status stanza.
    125 void HostStatusSenderTest::ValidateHostStatusStanza(
    126     XmlElement* stanza, HostStatusSender::HostStatus status) {
    127   EXPECT_EQ(stanza->Attr(QName(std::string(), "to")),
    128             std::string(kTestBotJid));
    129   EXPECT_EQ(stanza->Attr(QName(std::string(), "type")), "set");
    130 
    131   XmlElement* host_status_stanza =
    132       stanza->FirstNamed(QName(kChromotingXmlNamespace, "host-status"));
    133   ASSERT_TRUE(host_status_stanza != NULL);
    134 
    135   if (status == HostStatusSender::ONLINE) {
    136     EXPECT_EQ("ONLINE",
    137               host_status_stanza->Attr(
    138                   QName(kChromotingXmlNamespace, "status")));
    139     EXPECT_FALSE(host_status_stanza->HasAttr(
    140         QName(kChromotingXmlNamespace, "exit-code")));
    141   } else {
    142     EXPECT_EQ("OFFLINE",
    143               host_status_stanza->Attr(
    144                   QName(kChromotingXmlNamespace, "status")));
    145     EXPECT_EQ(kTestExitCodeString,
    146               host_status_stanza->Attr(
    147                   QName(kChromotingXmlNamespace, "exit-code")));
    148   }
    149 
    150   EXPECT_EQ(std::string(kHostId),
    151             host_status_stanza->Attr(
    152                 QName(kChromotingXmlNamespace, "hostid")));
    153 
    154   QName signature_tag(kChromotingXmlNamespace, "signature");
    155   XmlElement* signature = host_status_stanza->FirstNamed(signature_tag);
    156   ASSERT_TRUE(signature != NULL);
    157   EXPECT_TRUE(host_status_stanza->NextNamed(signature_tag) == NULL);
    158 
    159   ValidateSignature(signature, status);
    160 }
    161 
    162 // Validate the signature.
    163 void HostStatusSenderTest::ValidateSignature(
    164     XmlElement* signature, HostStatusSender::HostStatus status) {
    165 
    166   EXPECT_TRUE(signature->HasAttr(
    167       QName(kChromotingXmlNamespace, "time")));
    168 
    169   std::string time_str =
    170       signature->Attr(QName(kChromotingXmlNamespace, "time"));
    171 
    172   int64 time;
    173   ASSERT_TRUE(base::StringToInt64(time_str, &time));
    174 
    175   std::string message;
    176   message += kTestJid;
    177   message += " ";
    178   message += time_str;
    179   message += " ";
    180 
    181   if (status == HostStatusSender::OFFLINE) {
    182     message += "OFFLINE";
    183     message += " ";
    184     message += kTestExitCodeString;
    185   } else {
    186     message += "ONLINE";
    187   }
    188 
    189   scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::FromString(kTestRsaKeyPair);
    190   ASSERT_TRUE(key_pair.get());
    191 
    192   std::string expected_signature =
    193       key_pair->SignMessage(message);
    194   EXPECT_EQ(expected_signature, signature->BodyText());
    195 
    196   int64 now = static_cast<int64>(base::Time::Now().ToDoubleT());
    197   LOG(INFO) << "SendHostStatus took " << now - time << " seconds.";
    198 }
    199 
    200 }  // namespace remoting
    201