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