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 "base/message_loop/message_loop.h"
6#include "base/message_loop/message_loop_proxy.h"
7#include "remoting/host/host_status_monitor_fake.h"
8#include "remoting/host/log_to_server.h"
9#include "remoting/jingle_glue/mock_objects.h"
10#include "testing/gmock/include/gmock/gmock.h"
11#include "testing/gmock_mutant.h"
12#include "testing/gtest/include/gtest/gtest.h"
13#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
14
15using buzz::XmlElement;
16using buzz::QName;
17using testing::_;
18using testing::DeleteArg;
19using testing::InSequence;
20using testing::Return;
21
22namespace remoting {
23
24namespace {
25
26ACTION_P(QuitMainMessageLoop, message_loop) {
27  message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
28}
29
30const char kJabberClientNamespace[] = "jabber:client";
31const char kChromotingNamespace[] = "google:remoting";
32const char kTestBotJid[] = "remotingunittest@bot.talk.google.com";
33const char kClientJid1[] = "client@domain.com/1234";
34const char kClientJid2[] = "client@domain.com/5678";
35const char kHostJid[] = "host@domain.com/1234";
36
37bool IsLogEntryForConnection(XmlElement* node, const char* connection_type) {
38  return (node->Name() == QName(kChromotingNamespace, "entry") &&
39          node->Attr(QName(std::string(), "event-name")) == "session-state" &&
40          node->Attr(QName(std::string(), "session-state")) == "connected" &&
41          node->Attr(QName(std::string(), "role")) == "host" &&
42          node->Attr(QName(std::string(), "mode")) == "me2me" &&
43          node->Attr(QName(std::string(), "connection-type")) ==
44              connection_type);
45}
46
47MATCHER_P(IsClientConnected, connection_type, "") {
48  if (arg->Name() != QName(kJabberClientNamespace, "iq")) {
49    return false;
50  }
51  buzz::XmlElement* log_stanza = arg->FirstChild()->AsElement();
52  if (log_stanza->Name() !=QName(kChromotingNamespace, "log")) {
53    return false;
54  }
55  if (log_stanza->NextChild()) {
56    return false;
57  }
58  buzz::XmlElement* log_entry = log_stanza->FirstChild()->AsElement();
59  if (!IsLogEntryForConnection(log_entry, connection_type)) {
60    return false;
61  }
62  if (log_entry->NextChild()) {
63    return false;
64  }
65  return true;
66}
67
68MATCHER_P2(IsTwoClientsConnected, connection_type1, connection_type2, "") {
69  if (arg->Name() != QName(kJabberClientNamespace, "iq")) {
70    return false;
71  }
72  buzz::XmlElement* log_stanza = arg->FirstChild()->AsElement();
73  if (log_stanza->Name() !=QName(kChromotingNamespace, "log")) {
74    return false;
75  }
76  if (log_stanza->NextChild()) {
77    return false;
78  }
79  buzz::XmlElement* log_entry = log_stanza->FirstChild()->AsElement();
80  if (!IsLogEntryForConnection(log_entry, connection_type1)) {
81    return false;
82  }
83  log_entry = log_entry->NextChild()->AsElement();
84  if (!IsLogEntryForConnection(log_entry, connection_type2)) {
85    return false;
86  }
87  if (log_entry->NextChild()) {
88    return false;
89  }
90  return true;
91}
92
93bool IsLogEntryForDisconnection(XmlElement* node) {
94  return (node->Name() == QName(kChromotingNamespace, "entry") &&
95          node->Attr(QName(std::string(), "event-name")) == "session-state" &&
96          node->Attr(QName(std::string(), "session-state")) == "closed" &&
97          node->Attr(QName(std::string(), "role")) == "host" &&
98          node->Attr(QName(std::string(), "mode")) == "me2me");
99}
100
101MATCHER(IsClientDisconnected, "") {
102  if (arg->Name() != QName(kJabberClientNamespace, "iq")) {
103    return false;
104  }
105  buzz::XmlElement* log_stanza = arg->FirstChild()->AsElement();
106  if (log_stanza->Name() !=QName(kChromotingNamespace, "log")) {
107    return false;
108  }
109  if (log_stanza->NextChild()) {
110    return false;
111  }
112  buzz::XmlElement* log_entry = log_stanza->FirstChild()->AsElement();
113  if (!IsLogEntryForDisconnection(log_entry)) {
114    return false;
115  }
116  if (log_entry->NextChild()) {
117    return false;
118  }
119  return true;
120}
121
122}  // namespace
123
124class LogToServerTest : public testing::Test {
125 public:
126  LogToServerTest() {}
127  virtual void SetUp() OVERRIDE {
128    message_loop_proxy_ = base::MessageLoopProxy::current();
129    EXPECT_CALL(signal_strategy_, AddListener(_));
130    log_to_server_.reset(
131        new LogToServer(host_status_monitor_.AsWeakPtr(),
132                        ServerLogEntry::ME2ME,
133                        &signal_strategy_,
134                        kTestBotJid));
135    EXPECT_CALL(signal_strategy_, RemoveListener(_));
136  }
137
138 protected:
139  base::MessageLoop message_loop_;
140  scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
141  MockSignalStrategy signal_strategy_;
142  scoped_ptr<LogToServer> log_to_server_;
143  HostStatusMonitorFake host_status_monitor_;
144};
145
146TEST_F(LogToServerTest, SendNow) {
147  {
148    InSequence s;
149    EXPECT_CALL(signal_strategy_, GetLocalJid())
150        .WillRepeatedly(Return(kHostJid));
151    EXPECT_CALL(signal_strategy_, AddListener(_));
152    EXPECT_CALL(signal_strategy_, GetNextId());
153    EXPECT_CALL(signal_strategy_, SendStanzaPtr(IsClientConnected("direct")))
154        .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
155    EXPECT_CALL(signal_strategy_, RemoveListener(_))
156        .WillOnce(QuitMainMessageLoop(&message_loop_))
157        .RetiresOnSaturation();
158  }
159  log_to_server_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
160  protocol::TransportRoute route;
161  route.type = protocol::TransportRoute::DIRECT;
162  log_to_server_->OnClientRouteChange(kClientJid1, "video", route);
163  log_to_server_->OnClientAuthenticated(kClientJid1);
164  log_to_server_->OnClientConnected(kClientJid1);
165  log_to_server_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
166  message_loop_.Run();
167}
168
169TEST_F(LogToServerTest, SendLater) {
170  protocol::TransportRoute route;
171  route.type = protocol::TransportRoute::DIRECT;
172  log_to_server_->OnClientRouteChange(kClientJid1, "video", route);
173  log_to_server_->OnClientAuthenticated(kClientJid1);
174  log_to_server_->OnClientConnected(kClientJid1);
175  {
176    InSequence s;
177    EXPECT_CALL(signal_strategy_, GetLocalJid())
178        .WillRepeatedly(Return(kHostJid));
179    EXPECT_CALL(signal_strategy_, AddListener(_));
180    EXPECT_CALL(signal_strategy_, GetNextId());
181    EXPECT_CALL(signal_strategy_, SendStanzaPtr(IsClientConnected("direct")))
182        .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
183    EXPECT_CALL(signal_strategy_, RemoveListener(_))
184        .WillOnce(QuitMainMessageLoop(&message_loop_))
185        .RetiresOnSaturation();
186  }
187  log_to_server_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
188  log_to_server_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
189  message_loop_.Run();
190}
191
192TEST_F(LogToServerTest, SendTwoEntriesLater) {
193  protocol::TransportRoute route1;
194  route1.type = protocol::TransportRoute::DIRECT;
195  log_to_server_->OnClientRouteChange(kClientJid1, "video", route1);
196  log_to_server_->OnClientAuthenticated(kClientJid1);
197  log_to_server_->OnClientConnected(kClientJid1);
198  protocol::TransportRoute route2;
199  route2.type = protocol::TransportRoute::STUN;
200  log_to_server_->OnClientRouteChange(kClientJid2, "video", route2);
201  log_to_server_->OnClientAuthenticated(kClientJid2);
202  log_to_server_->OnClientConnected(kClientJid2);
203  {
204    InSequence s;
205    EXPECT_CALL(signal_strategy_, GetLocalJid())
206        .WillRepeatedly(Return(kHostJid));
207    EXPECT_CALL(signal_strategy_, AddListener(_));
208    EXPECT_CALL(signal_strategy_, GetNextId());
209    EXPECT_CALL(signal_strategy_, SendStanzaPtr(
210            IsTwoClientsConnected("direct", "stun")))
211        .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
212    EXPECT_CALL(signal_strategy_, RemoveListener(_))
213        .WillOnce(QuitMainMessageLoop(&message_loop_))
214        .RetiresOnSaturation();
215  }
216  log_to_server_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
217  log_to_server_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
218  message_loop_.Run();
219}
220
221TEST_F(LogToServerTest, HandleRouteChangeInUnusualOrder) {
222  {
223    InSequence s;
224    EXPECT_CALL(signal_strategy_, GetLocalJid())
225        .WillRepeatedly(Return(kHostJid));
226    EXPECT_CALL(signal_strategy_, AddListener(_));
227    EXPECT_CALL(signal_strategy_, GetNextId());
228    EXPECT_CALL(signal_strategy_, SendStanzaPtr(IsClientConnected("direct")))
229        .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
230    EXPECT_CALL(signal_strategy_, GetNextId());
231    EXPECT_CALL(signal_strategy_, SendStanzaPtr(IsClientDisconnected()))
232        .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
233    EXPECT_CALL(signal_strategy_, GetNextId());
234    EXPECT_CALL(signal_strategy_, SendStanzaPtr(IsClientConnected("stun")))
235        .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
236    EXPECT_CALL(signal_strategy_, RemoveListener(_))
237        .WillOnce(QuitMainMessageLoop(&message_loop_))
238        .RetiresOnSaturation();
239  }
240  log_to_server_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
241  protocol::TransportRoute route1;
242  route1.type = protocol::TransportRoute::DIRECT;
243  log_to_server_->OnClientRouteChange(kClientJid1, "video", route1);
244  log_to_server_->OnClientAuthenticated(kClientJid1);
245  log_to_server_->OnClientConnected(kClientJid1);
246  protocol::TransportRoute route2;
247  route2.type = protocol::TransportRoute::STUN;
248  log_to_server_->OnClientRouteChange(kClientJid2, "video", route2);
249  log_to_server_->OnClientDisconnected(kClientJid1);
250  log_to_server_->OnClientAuthenticated(kClientJid2);
251  log_to_server_->OnClientConnected(kClientJid2);
252  log_to_server_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
253  message_loop_.Run();
254}
255
256}  // namespace remoting
257