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