daemon_process_unittest.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/bind.h" 6#include "base/bind_helpers.h" 7#include "base/location.h" 8#include "base/memory/ref_counted.h" 9#include "base/process.h" 10#include "ipc/ipc_message.h" 11#include "ipc/ipc_message_macros.h" 12#include "ipc/ipc_platform_file.h" 13#include "remoting/base/auto_thread_task_runner.h" 14#include "remoting/host/chromoting_messages.h" 15#include "remoting/host/daemon_process.h" 16#include "remoting/host/desktop_session.h" 17#include "testing/gmock_mutant.h" 18#include "testing/gmock/include/gmock/gmock.h" 19#include "testing/gtest/include/gtest/gtest.h" 20 21using testing::_; 22using testing::AnyNumber; 23using testing::InSequence; 24 25namespace remoting { 26 27namespace { 28 29enum Messages { 30 kMessageCrash = ChromotingDaemonMsg_Crash::ID, 31 kMessageConfiguration = ChromotingDaemonNetworkMsg_Configuration::ID, 32 kMessageConnectTerminal = ChromotingNetworkHostMsg_ConnectTerminal::ID, 33 kMessageDisconnectTerminal = ChromotingNetworkHostMsg_DisconnectTerminal::ID, 34 kMessageTerminalDisconnected = 35 ChromotingDaemonNetworkMsg_TerminalDisconnected::ID 36}; 37 38// Provides a public constructor allowing the test to create instances of 39// DesktopSession directly. 40class FakeDesktopSession : public DesktopSession { 41 public: 42 FakeDesktopSession(DaemonProcess* daemon_process, int id); 43 virtual ~FakeDesktopSession(); 44 45 virtual void SetScreenResolution( 46 const ScreenResolution& resolution) OVERRIDE {} 47 48 private: 49 DISALLOW_COPY_AND_ASSIGN(FakeDesktopSession); 50}; 51 52class MockDaemonProcess : public DaemonProcess { 53 public: 54 MockDaemonProcess( 55 scoped_refptr<AutoThreadTaskRunner> caller_task_runner, 56 scoped_refptr<AutoThreadTaskRunner> io_task_runner, 57 const base::Closure& stopped_callback); 58 virtual ~MockDaemonProcess(); 59 60 virtual scoped_ptr<DesktopSession> DoCreateDesktopSession( 61 int terminal_id, 62 const ScreenResolution& resolution, 63 bool virtual_terminal) OVERRIDE; 64 65 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; 66 virtual void SendToNetwork(IPC::Message* message) OVERRIDE; 67 68 MOCK_METHOD1(Received, void(const IPC::Message&)); 69 MOCK_METHOD1(Sent, void(const IPC::Message&)); 70 71 MOCK_METHOD3(OnDesktopSessionAgentAttached, 72 bool(int, base::ProcessHandle, IPC::PlatformFileForTransit)); 73 74 MOCK_METHOD1(DoCreateDesktopSessionPtr, DesktopSession*(int)); 75 MOCK_METHOD1(DoCrashNetworkProcess, void(const tracked_objects::Location&)); 76 MOCK_METHOD0(LaunchNetworkProcess, void()); 77 78 private: 79 DISALLOW_COPY_AND_ASSIGN(MockDaemonProcess); 80}; 81 82FakeDesktopSession::FakeDesktopSession(DaemonProcess* daemon_process, int id) 83 : DesktopSession(daemon_process, id) { 84} 85 86FakeDesktopSession::~FakeDesktopSession() { 87} 88 89MockDaemonProcess::MockDaemonProcess( 90 scoped_refptr<AutoThreadTaskRunner> caller_task_runner, 91 scoped_refptr<AutoThreadTaskRunner> io_task_runner, 92 const base::Closure& stopped_callback) 93 : DaemonProcess(caller_task_runner, io_task_runner, stopped_callback) { 94} 95 96MockDaemonProcess::~MockDaemonProcess() { 97} 98 99scoped_ptr<DesktopSession> MockDaemonProcess::DoCreateDesktopSession( 100 int terminal_id, 101 const ScreenResolution& resolution, 102 bool virtual_terminal) { 103 return scoped_ptr<DesktopSession>(DoCreateDesktopSessionPtr(terminal_id)); 104} 105 106bool MockDaemonProcess::OnMessageReceived(const IPC::Message& message) { 107 // Notify the mock method. 108 Received(message); 109 110 // Call the actual handler. 111 return DaemonProcess::OnMessageReceived(message); 112} 113 114void MockDaemonProcess::SendToNetwork(IPC::Message* message) { 115 // Notify the mock method. 116 Sent(*message); 117 delete message; 118} 119 120} // namespace 121 122class DaemonProcessTest : public testing::Test { 123 public: 124 DaemonProcessTest(); 125 virtual ~DaemonProcessTest(); 126 127 virtual void SetUp() OVERRIDE; 128 virtual void TearDown() OVERRIDE; 129 130 // DaemonProcess mocks 131 DesktopSession* DoCreateDesktopSession(int terminal_id); 132 void DoCrashNetworkProcess(const tracked_objects::Location& location); 133 void LaunchNetworkProcess(); 134 135 // Deletes |daemon_process_|. 136 void DeleteDaemonProcess(); 137 138 // Quits |message_loop_|. 139 void QuitMessageLoop(); 140 141 void StartDaemonProcess(); 142 143 const DaemonProcess::DesktopSessionList& desktop_sessions() const { 144 return daemon_process_->desktop_sessions(); 145 } 146 147 protected: 148 base::MessageLoop message_loop_; 149 150 scoped_ptr<MockDaemonProcess> daemon_process_; 151 int terminal_id_; 152}; 153 154DaemonProcessTest::DaemonProcessTest() 155 : message_loop_(base::MessageLoop::TYPE_IO), terminal_id_(0) { 156} 157 158DaemonProcessTest::~DaemonProcessTest() { 159} 160 161void DaemonProcessTest::SetUp() { 162 scoped_refptr<AutoThreadTaskRunner> task_runner = new AutoThreadTaskRunner( 163 message_loop_.message_loop_proxy(), 164 base::Bind(&DaemonProcessTest::QuitMessageLoop, 165 base::Unretained(this))); 166 daemon_process_.reset( 167 new MockDaemonProcess(task_runner, task_runner, 168 base::Bind(&DaemonProcessTest::DeleteDaemonProcess, 169 base::Unretained(this)))); 170 171 // Set up daemon process mocks. 172 EXPECT_CALL(*daemon_process_, DoCreateDesktopSessionPtr(_)) 173 .Times(AnyNumber()) 174 .WillRepeatedly(Invoke(this, &DaemonProcessTest::DoCreateDesktopSession)); 175 EXPECT_CALL(*daemon_process_, DoCrashNetworkProcess(_)) 176 .Times(AnyNumber()) 177 .WillRepeatedly(Invoke(this, &DaemonProcessTest::DoCrashNetworkProcess)); 178 EXPECT_CALL(*daemon_process_, LaunchNetworkProcess()) 179 .Times(AnyNumber()) 180 .WillRepeatedly(Invoke(this, &DaemonProcessTest::LaunchNetworkProcess)); 181} 182 183void DaemonProcessTest::TearDown() { 184 daemon_process_->Stop(); 185 message_loop_.Run(); 186} 187 188DesktopSession* DaemonProcessTest::DoCreateDesktopSession(int terminal_id) { 189 return new FakeDesktopSession(daemon_process_.get(), terminal_id); 190} 191 192void DaemonProcessTest::DoCrashNetworkProcess( 193 const tracked_objects::Location& location) { 194 daemon_process_->SendToNetwork( 195 new ChromotingDaemonMsg_Crash(location.function_name(), 196 location.file_name(), 197 location.line_number())); 198} 199 200void DaemonProcessTest::LaunchNetworkProcess() { 201 terminal_id_ = 0; 202 daemon_process_->OnChannelConnected(0); 203} 204 205void DaemonProcessTest::DeleteDaemonProcess() { 206 daemon_process_.reset(); 207} 208 209void DaemonProcessTest::QuitMessageLoop() { 210 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 211} 212 213void DaemonProcessTest::StartDaemonProcess() { 214 // DaemonProcess::Initialize() sets up the config watcher that this test does 215 // not support. Launch the process directly. 216 daemon_process_->LaunchNetworkProcess(); 217} 218 219MATCHER_P(Message, type, "") { 220 return arg.type() == static_cast<uint32>(type); 221} 222 223TEST_F(DaemonProcessTest, OpenClose) { 224 InSequence s; 225 EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration))); 226 EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal))); 227 EXPECT_CALL(*daemon_process_, Received(Message(kMessageDisconnectTerminal))); 228 EXPECT_CALL(*daemon_process_, Sent(Message(kMessageTerminalDisconnected))); 229 230 StartDaemonProcess(); 231 232 int id = terminal_id_++; 233 ScreenResolution resolution; 234 235 EXPECT_TRUE(daemon_process_->OnMessageReceived( 236 ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false))); 237 EXPECT_EQ(1u, desktop_sessions().size()); 238 EXPECT_EQ(id, desktop_sessions().front()->id()); 239 240 EXPECT_TRUE(daemon_process_->OnMessageReceived( 241 ChromotingNetworkHostMsg_DisconnectTerminal(id))); 242 EXPECT_TRUE(desktop_sessions().empty()); 243} 244 245TEST_F(DaemonProcessTest, CallCloseDesktopSession) { 246 InSequence s; 247 EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration))); 248 EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal))); 249 EXPECT_CALL(*daemon_process_, Sent(Message(kMessageTerminalDisconnected))); 250 251 StartDaemonProcess(); 252 253 int id = terminal_id_++; 254 ScreenResolution resolution; 255 256 EXPECT_TRUE(daemon_process_->OnMessageReceived( 257 ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false))); 258 EXPECT_EQ(1u, desktop_sessions().size()); 259 EXPECT_EQ(id, desktop_sessions().front()->id()); 260 261 daemon_process_->CloseDesktopSession(id); 262 EXPECT_TRUE(desktop_sessions().empty()); 263} 264 265// Sends two CloseDesktopSession messages and expects the second one to be 266// ignored. 267TEST_F(DaemonProcessTest, DoubleDisconnectTerminal) { 268 InSequence s; 269 EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration))); 270 EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal))); 271 EXPECT_CALL(*daemon_process_, Received(Message(kMessageDisconnectTerminal))); 272 EXPECT_CALL(*daemon_process_, Sent(Message(kMessageTerminalDisconnected))); 273 EXPECT_CALL(*daemon_process_, Received(Message(kMessageDisconnectTerminal))); 274 275 StartDaemonProcess(); 276 277 int id = terminal_id_++; 278 ScreenResolution resolution; 279 280 EXPECT_TRUE(daemon_process_->OnMessageReceived( 281 ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false))); 282 EXPECT_EQ(1u, desktop_sessions().size()); 283 EXPECT_EQ(id, desktop_sessions().front()->id()); 284 285 EXPECT_TRUE(daemon_process_->OnMessageReceived( 286 ChromotingNetworkHostMsg_DisconnectTerminal(id))); 287 EXPECT_TRUE(desktop_sessions().empty()); 288 289 EXPECT_TRUE(daemon_process_->OnMessageReceived( 290 ChromotingNetworkHostMsg_DisconnectTerminal(id))); 291 EXPECT_TRUE(desktop_sessions().empty()); 292} 293 294// Tries to close an invalid terminal ID and expects the network process to be 295// restarted. 296TEST_F(DaemonProcessTest, InvalidDisconnectTerminal) { 297 InSequence s; 298 EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration))); 299 EXPECT_CALL(*daemon_process_, Received(Message(kMessageDisconnectTerminal))); 300 EXPECT_CALL(*daemon_process_, Sent(Message(kMessageCrash))) 301 .WillOnce(InvokeWithoutArgs(this, 302 &DaemonProcessTest::LaunchNetworkProcess)); 303 EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration))); 304 305 StartDaemonProcess(); 306 307 int id = terminal_id_++; 308 309 EXPECT_TRUE(daemon_process_->OnMessageReceived( 310 ChromotingNetworkHostMsg_DisconnectTerminal(id))); 311 EXPECT_TRUE(desktop_sessions().empty()); 312 EXPECT_EQ(0, terminal_id_); 313} 314 315// Tries to open an invalid terminal ID and expects the network process to be 316// restarted. 317TEST_F(DaemonProcessTest, InvalidConnectTerminal) { 318 InSequence s; 319 EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration))); 320 EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal))); 321 EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal))); 322 EXPECT_CALL(*daemon_process_, Sent(Message(kMessageCrash))) 323 .WillOnce(InvokeWithoutArgs(this, 324 &DaemonProcessTest::LaunchNetworkProcess)); 325 EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration))); 326 327 StartDaemonProcess(); 328 329 int id = terminal_id_++; 330 ScreenResolution resolution; 331 332 EXPECT_TRUE(daemon_process_->OnMessageReceived( 333 ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false))); 334 EXPECT_EQ(1u, desktop_sessions().size()); 335 EXPECT_EQ(id, desktop_sessions().front()->id()); 336 337 EXPECT_TRUE(daemon_process_->OnMessageReceived( 338 ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false))); 339 EXPECT_TRUE(desktop_sessions().empty()); 340 EXPECT_EQ(0, terminal_id_); 341} 342 343} // namespace remoting 344