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@bot.talk.google.com";
32 const char kHostId[] = "0";
33 const char kTestJid[] = "user@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:
SetUp()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
TearDown()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
TEST_F(HostStatusSenderTest,SendOnlineStatus)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
TEST_F(HostStatusSenderTest,SendOfflineStatus)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.
ValidateHostStatusStanza(XmlElement * stanza,HostStatusSender::HostStatus status)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.
ValidateSignature(XmlElement * signature,HostStatusSender::HostStatus status)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