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