Home | History | Annotate | Download | only in host
      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.
      4 
      5 #include "remoting/host/heartbeat_sender.h"
      6 
      7 #include <set>
      8 
      9 #include "base/memory/ref_counted.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/message_loop/message_loop_proxy.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "remoting/base/constants.h"
     14 #include "remoting/base/rsa_key_pair.h"
     15 #include "remoting/base/test_rsa_key_pair.h"
     16 #include "remoting/jingle_glue/iq_sender.h"
     17 #include "remoting/jingle_glue/mock_objects.h"
     18 #include "testing/gmock/include/gmock/gmock.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
     21 #include "third_party/libjingle/source/talk/xmpp/constants.h"
     22 
     23 using buzz::QName;
     24 using buzz::XmlElement;
     25 
     26 using testing::_;
     27 using testing::DeleteArg;
     28 using testing::DoAll;
     29 using testing::Invoke;
     30 using testing::NotNull;
     31 using testing::Return;
     32 using testing::SaveArg;
     33 
     34 namespace remoting {
     35 
     36 namespace {
     37 
     38 const char kTestBotJid[] = "remotingunittest (at) bot.talk.google.com";
     39 const char kHostId[] = "0";
     40 const char kTestJid[] = "user (at) gmail.com/chromoting123";
     41 const char kStanzaId[] = "123";
     42 
     43 class MockListener : public HeartbeatSender::Listener {
     44  public:
     45   // Overridden from HeartbeatSender::Listener
     46   virtual void OnUnknownHostIdError() OVERRIDE {
     47     NOTREACHED();
     48   }
     49 
     50   // Overridden from HeartbeatSender::Listener
     51   MOCK_METHOD0(OnHeartbeatSuccessful, void());
     52 };
     53 
     54 }  // namespace
     55 
     56 ACTION_P(AddListener, list) {
     57   list->insert(arg0);
     58 }
     59 ACTION_P(RemoveListener, list) {
     60   EXPECT_TRUE(list->find(arg0) != list->end());
     61   list->erase(arg0);
     62 }
     63 
     64 class HeartbeatSenderTest
     65     : public testing::Test {
     66  protected:
     67   virtual void SetUp() OVERRIDE {
     68     key_pair_ = RsaKeyPair::FromString(kTestRsaKeyPair);
     69     ASSERT_TRUE(key_pair_.get());
     70 
     71     EXPECT_CALL(signal_strategy_, GetState())
     72         .WillOnce(Return(SignalStrategy::DISCONNECTED));
     73     EXPECT_CALL(signal_strategy_, AddListener(NotNull()))
     74         .WillRepeatedly(AddListener(&signal_strategy_listeners_));
     75     EXPECT_CALL(signal_strategy_, RemoveListener(NotNull()))
     76         .WillRepeatedly(RemoveListener(&signal_strategy_listeners_));
     77     EXPECT_CALL(signal_strategy_, GetLocalJid())
     78         .WillRepeatedly(Return(kTestJid));
     79 
     80     heartbeat_sender_.reset(new HeartbeatSender(
     81         &mock_listener_, kHostId, &signal_strategy_, key_pair_, kTestBotJid));
     82   }
     83 
     84   virtual void TearDown() OVERRIDE {
     85     heartbeat_sender_.reset();
     86     EXPECT_TRUE(signal_strategy_listeners_.empty());
     87   }
     88 
     89   void ValidateHeartbeatStanza(XmlElement* stanza,
     90                                const char* expectedSequenceId);
     91 
     92   base::MessageLoop message_loop_;
     93   MockSignalStrategy signal_strategy_;
     94   MockListener mock_listener_;
     95   std::set<SignalStrategy::Listener*> signal_strategy_listeners_;
     96   scoped_refptr<RsaKeyPair> key_pair_;
     97   scoped_ptr<HeartbeatSender> heartbeat_sender_;
     98 };
     99 
    100 // Call Start() followed by Stop(), and make sure a valid heartbeat is sent.
    101 TEST_F(HeartbeatSenderTest, DoSendStanza) {
    102   XmlElement* sent_iq = NULL;
    103   EXPECT_CALL(signal_strategy_, GetLocalJid())
    104       .WillRepeatedly(Return(kTestJid));
    105   EXPECT_CALL(signal_strategy_, GetNextId())
    106       .WillOnce(Return(kStanzaId));
    107   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
    108       .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
    109 
    110   heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
    111   message_loop_.RunUntilIdle();
    112 
    113   scoped_ptr<XmlElement> stanza(sent_iq);
    114   ASSERT_TRUE(stanza != NULL);
    115   ValidateHeartbeatStanza(stanza.get(), "0");
    116 
    117   heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
    118   message_loop_.RunUntilIdle();
    119 }
    120 
    121 // Call Start() followed by Stop(), twice, and make sure two valid heartbeats
    122 // are sent, with the correct sequence IDs.
    123 TEST_F(HeartbeatSenderTest, DoSendStanzaTwice) {
    124   XmlElement* sent_iq = NULL;
    125   EXPECT_CALL(signal_strategy_, GetLocalJid())
    126       .WillRepeatedly(Return(kTestJid));
    127   EXPECT_CALL(signal_strategy_, GetNextId())
    128       .WillOnce(Return(kStanzaId));
    129   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
    130       .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
    131 
    132   heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
    133   message_loop_.RunUntilIdle();
    134 
    135   scoped_ptr<XmlElement> stanza(sent_iq);
    136   ASSERT_TRUE(stanza != NULL);
    137   ValidateHeartbeatStanza(stanza.get(), "0");
    138 
    139   heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
    140   message_loop_.RunUntilIdle();
    141 
    142   EXPECT_CALL(signal_strategy_, GetLocalJid())
    143       .WillRepeatedly(Return(kTestJid));
    144   EXPECT_CALL(signal_strategy_, GetNextId())
    145       .WillOnce(Return(kStanzaId + 1));
    146   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
    147       .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
    148 
    149   heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
    150   message_loop_.RunUntilIdle();
    151 
    152   scoped_ptr<XmlElement> stanza2(sent_iq);
    153   ValidateHeartbeatStanza(stanza2.get(), "1");
    154 
    155   heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
    156   message_loop_.RunUntilIdle();
    157 }
    158 
    159 // Call Start() followed by Stop(), make sure a valid Iq stanza is sent,
    160 // reply with an expected sequence ID, and make sure two valid heartbeats
    161 // are sent, with the correct sequence IDs.
    162 TEST_F(HeartbeatSenderTest, DoSendStanzaWithExpectedSequenceId) {
    163   XmlElement* sent_iq = NULL;
    164   EXPECT_CALL(signal_strategy_, GetLocalJid())
    165       .WillRepeatedly(Return(kTestJid));
    166   EXPECT_CALL(signal_strategy_, GetNextId())
    167       .WillOnce(Return(kStanzaId));
    168   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
    169       .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
    170 
    171   heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
    172   message_loop_.RunUntilIdle();
    173 
    174   scoped_ptr<XmlElement> stanza(sent_iq);
    175   ASSERT_TRUE(stanza != NULL);
    176   ValidateHeartbeatStanza(stanza.get(), "0");
    177 
    178   XmlElement* sent_iq2 = NULL;
    179   EXPECT_CALL(signal_strategy_, GetLocalJid())
    180       .WillRepeatedly(Return(kTestJid));
    181   EXPECT_CALL(signal_strategy_, GetNextId())
    182       .WillOnce(Return(kStanzaId + 1));
    183   EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
    184       .WillOnce(DoAll(SaveArg<0>(&sent_iq2), Return(true)));
    185   EXPECT_CALL(mock_listener_, OnHeartbeatSuccessful());
    186 
    187   scoped_ptr<XmlElement> response(new XmlElement(buzz::QN_IQ));
    188   response->AddAttr(QName(std::string(), "type"), "result");
    189   XmlElement* result =
    190       new XmlElement(QName(kChromotingXmlNamespace, "heartbeat-result"));
    191   response->AddElement(result);
    192   XmlElement* expected_sequence_id = new XmlElement(
    193       QName(kChromotingXmlNamespace, "expected-sequence-id"));
    194   result->AddElement(expected_sequence_id);
    195   const int kExpectedSequenceId = 456;
    196   expected_sequence_id->AddText(base::IntToString(kExpectedSequenceId));
    197   heartbeat_sender_->ProcessResponse(NULL, response.get());
    198   message_loop_.RunUntilIdle();
    199 
    200   scoped_ptr<XmlElement> stanza2(sent_iq2);
    201   ASSERT_TRUE(stanza2 != NULL);
    202   ValidateHeartbeatStanza(stanza2.get(),
    203                           base::IntToString(kExpectedSequenceId).c_str());
    204 
    205   heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
    206   message_loop_.RunUntilIdle();
    207 }
    208 
    209 // Verify that ProcessResponse parses set-interval result.
    210 TEST_F(HeartbeatSenderTest, ProcessResponseSetInterval) {
    211   EXPECT_CALL(mock_listener_, OnHeartbeatSuccessful());
    212 
    213   scoped_ptr<XmlElement> response(new XmlElement(buzz::QN_IQ));
    214   response->AddAttr(QName(std::string(), "type"), "result");
    215 
    216   XmlElement* result = new XmlElement(
    217       QName(kChromotingXmlNamespace, "heartbeat-result"));
    218   response->AddElement(result);
    219 
    220   XmlElement* set_interval = new XmlElement(
    221       QName(kChromotingXmlNamespace, "set-interval"));
    222   result->AddElement(set_interval);
    223 
    224   const int kTestInterval = 123;
    225   set_interval->AddText(base::IntToString(kTestInterval));
    226 
    227   heartbeat_sender_->ProcessResponse(NULL, response.get());
    228 
    229   EXPECT_EQ(kTestInterval * 1000, heartbeat_sender_->interval_ms_);
    230 }
    231 
    232 // Validate a heartbeat stanza.
    233 void HeartbeatSenderTest::ValidateHeartbeatStanza(
    234     XmlElement* stanza, const char* expectedSequenceId) {
    235   EXPECT_EQ(stanza->Attr(buzz::QName(std::string(), "to")),
    236             std::string(kTestBotJid));
    237   EXPECT_EQ(stanza->Attr(buzz::QName(std::string(), "type")), "set");
    238   XmlElement* heartbeat_stanza =
    239       stanza->FirstNamed(QName(kChromotingXmlNamespace, "heartbeat"));
    240   ASSERT_TRUE(heartbeat_stanza != NULL);
    241   EXPECT_EQ(expectedSequenceId, heartbeat_stanza->Attr(
    242       buzz::QName(kChromotingXmlNamespace, "sequence-id")));
    243   EXPECT_EQ(std::string(kHostId),
    244             heartbeat_stanza->Attr(QName(kChromotingXmlNamespace, "hostid")));
    245 
    246   QName signature_tag(kChromotingXmlNamespace, "signature");
    247   XmlElement* signature = heartbeat_stanza->FirstNamed(signature_tag);
    248   ASSERT_TRUE(signature != NULL);
    249   EXPECT_TRUE(heartbeat_stanza->NextNamed(signature_tag) == NULL);
    250 
    251   scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::FromString(kTestRsaKeyPair);
    252   ASSERT_TRUE(key_pair.get());
    253   std::string expected_signature =
    254       key_pair->SignMessage(std::string(kTestJid) + ' ' + expectedSequenceId);
    255   EXPECT_EQ(expected_signature, signature->BodyText());
    256 }
    257 
    258 }  // namespace remoting
    259