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