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