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
19using buzz::QName;
20using buzz::XmlElement;
21
22using testing::DoAll;
23using testing::NotNull;
24using testing::Return;
25using testing::SaveArg;
26
27namespace remoting {
28
29namespace {
30
31const char kTestBotJid[] = "remotingunittest@bot.talk.google.com";
32const char kHostId[] = "0";
33const char kTestJid[] = "user@gmail.com/chromoting123";
34const char kStanzaId[] = "123";
35
36const HostExitCodes kTestExitCode = kInvalidHostConfigurationExitCode;
37const char kTestExitCodeString[] = "INVALID_HOST_CONFIGURATION";
38
39}  // namespace
40
41class 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
67TEST_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
95TEST_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.
124void 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.
162void 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