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 "remoting/host/desktop_process.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/location.h" 10 #include "base/memory/ref_counted.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/run_loop.h" 13 #include "base/single_thread_task_runner.h" 14 #include "ipc/ipc_channel.h" 15 #include "ipc/ipc_channel_proxy.h" 16 #include "ipc/ipc_listener.h" 17 #include "ipc/ipc_message.h" 18 #include "remoting/base/auto_thread.h" 19 #include "remoting/base/auto_thread_task_runner.h" 20 #include "remoting/host/chromoting_messages.h" 21 #include "remoting/host/desktop_process.h" 22 #include "remoting/host/host_exit_codes.h" 23 #include "remoting/host/host_mock_objects.h" 24 #include "remoting/host/screen_capturer_fake.h" 25 #include "remoting/host/screen_resolution.h" 26 #include "remoting/protocol/protocol_mock_objects.h" 27 #include "testing/gmock/include/gmock/gmock.h" 28 #include "testing/gmock_mutant.h" 29 #include "testing/gtest/include/gtest/gtest.h" 30 31 using testing::_; 32 using testing::AnyNumber; 33 using testing::AtMost; 34 using testing::InSequence; 35 using testing::Return; 36 37 namespace remoting { 38 39 namespace { 40 41 class MockDaemonListener : public IPC::Listener { 42 public: 43 MockDaemonListener() {} 44 virtual ~MockDaemonListener() {} 45 46 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; 47 48 MOCK_METHOD1(OnDesktopAttached, void(IPC::PlatformFileForTransit)); 49 MOCK_METHOD1(OnChannelConnected, void(int32)); 50 MOCK_METHOD0(OnChannelError, void()); 51 52 private: 53 DISALLOW_COPY_AND_ASSIGN(MockDaemonListener); 54 }; 55 56 class MockNetworkListener : public IPC::Listener { 57 public: 58 MockNetworkListener() {} 59 virtual ~MockNetworkListener() {} 60 61 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; 62 63 MOCK_METHOD1(OnChannelConnected, void(int32)); 64 MOCK_METHOD0(OnChannelError, void()); 65 66 MOCK_METHOD0(OnDesktopEnvironmentCreated, void()); 67 68 private: 69 DISALLOW_COPY_AND_ASSIGN(MockNetworkListener); 70 }; 71 72 bool MockDaemonListener::OnMessageReceived(const IPC::Message& message) { 73 bool handled = true; 74 IPC_BEGIN_MESSAGE_MAP(MockDaemonListener, message) 75 IPC_MESSAGE_HANDLER(ChromotingDesktopDaemonMsg_DesktopAttached, 76 OnDesktopAttached) 77 IPC_MESSAGE_UNHANDLED(handled = false) 78 IPC_END_MESSAGE_MAP() 79 80 EXPECT_TRUE(handled); 81 return handled; 82 } 83 84 bool MockNetworkListener::OnMessageReceived(const IPC::Message& message) { 85 bool handled = true; 86 87 // TODO(alexeypa): handle received messages here. 88 89 EXPECT_TRUE(handled); 90 return handled; 91 } 92 93 } // namespace 94 95 class DesktopProcessTest : public testing::Test { 96 public: 97 DesktopProcessTest(); 98 virtual ~DesktopProcessTest(); 99 100 // testing::Test overrides 101 virtual void SetUp() OVERRIDE; 102 virtual void TearDown() OVERRIDE; 103 104 // MockDaemonListener mocks 105 void ConnectNetworkChannel(IPC::PlatformFileForTransit desktop_process); 106 void OnDesktopAttached(IPC::PlatformFileForTransit desktop_process); 107 108 // Creates a DesktopEnvironment with a fake webrtc::ScreenCapturer, to mock 109 // DesktopEnvironmentFactory::Create(). 110 DesktopEnvironment* CreateDesktopEnvironment(); 111 112 // Creates a dummy InputInjector, to mock 113 // DesktopEnvironment::CreateInputInjector(). 114 InputInjector* CreateInputInjector(); 115 116 // Creates a fake webrtc::ScreenCapturer, to mock 117 // DesktopEnvironment::CreateVideoCapturer(). 118 webrtc::ScreenCapturer* CreateVideoCapturer(); 119 120 // Disconnects the daemon-to-desktop channel causing the desktop process to 121 // exit. 122 void DisconnectChannels(); 123 124 // Posts DisconnectChannels() to |message_loop_|. 125 void PostDisconnectChannels(); 126 127 // Runs the desktop process code in a separate thread. 128 void RunDesktopProcess(); 129 130 // Creates the desktop process and sends a crash request to it. 131 void RunDeathTest(); 132 133 // Sends a crash request to the desktop process. 134 void SendCrashRequest(); 135 136 // Requests the desktop process to start the desktop session agent. 137 void SendStartSessionAgent(); 138 139 protected: 140 // The daemon's end of the daemon-to-desktop channel. 141 scoped_ptr<IPC::ChannelProxy> daemon_channel_; 142 143 // Delegate that is passed to |daemon_channel_|. 144 MockDaemonListener daemon_listener_; 145 146 // Runs the daemon's end of the channel. 147 base::MessageLoop message_loop_; 148 149 scoped_refptr<AutoThreadTaskRunner> io_task_runner_; 150 151 // The network's end of the network-to-desktop channel. 152 scoped_ptr<IPC::ChannelProxy> network_channel_; 153 154 // Delegate that is passed to |network_channel_|. 155 MockNetworkListener network_listener_; 156 }; 157 158 DesktopProcessTest::DesktopProcessTest() 159 : message_loop_(base::MessageLoop::TYPE_UI) {} 160 161 DesktopProcessTest::~DesktopProcessTest() { 162 } 163 164 void DesktopProcessTest::SetUp() { 165 } 166 167 void DesktopProcessTest::TearDown() { 168 } 169 170 void DesktopProcessTest::ConnectNetworkChannel( 171 IPC::PlatformFileForTransit desktop_process) { 172 173 #if defined(OS_POSIX) 174 IPC::ChannelHandle channel_handle(std::string(), desktop_process); 175 #elif defined(OS_WIN) 176 IPC::ChannelHandle channel_handle(desktop_process); 177 #endif // defined(OS_WIN) 178 179 network_channel_.reset(new IPC::ChannelProxy(channel_handle, 180 IPC::Channel::MODE_CLIENT, 181 &network_listener_, 182 io_task_runner_.get())); 183 } 184 185 void DesktopProcessTest::OnDesktopAttached( 186 IPC::PlatformFileForTransit desktop_process) { 187 #if defined(OS_POSIX) 188 DCHECK(desktop_process.auto_close); 189 190 base::ClosePlatformFile(desktop_process.fd); 191 #endif // defined(OS_POSIX) 192 } 193 194 DesktopEnvironment* DesktopProcessTest::CreateDesktopEnvironment() { 195 MockDesktopEnvironment* desktop_environment = new MockDesktopEnvironment(); 196 EXPECT_CALL(*desktop_environment, CreateAudioCapturerPtr()) 197 .Times(0); 198 EXPECT_CALL(*desktop_environment, CreateInputInjectorPtr()) 199 .Times(AtMost(1)) 200 .WillOnce(Invoke(this, &DesktopProcessTest::CreateInputInjector)); 201 EXPECT_CALL(*desktop_environment, CreateScreenControlsPtr()) 202 .Times(AtMost(1)); 203 EXPECT_CALL(*desktop_environment, CreateVideoCapturerPtr()) 204 .Times(AtMost(1)) 205 .WillOnce(Invoke(this, &DesktopProcessTest::CreateVideoCapturer)); 206 EXPECT_CALL(*desktop_environment, GetCapabilities()) 207 .Times(AtMost(1)); 208 EXPECT_CALL(*desktop_environment, SetCapabilities(_)) 209 .Times(AtMost(1)); 210 211 // Notify the test that the desktop environment has been created. 212 network_listener_.OnDesktopEnvironmentCreated(); 213 return desktop_environment; 214 } 215 216 InputInjector* DesktopProcessTest::CreateInputInjector() { 217 MockInputInjector* input_injector = new MockInputInjector(); 218 EXPECT_CALL(*input_injector, StartPtr(_)); 219 return input_injector; 220 } 221 222 webrtc::ScreenCapturer* DesktopProcessTest::CreateVideoCapturer() { 223 return new ScreenCapturerFake(); 224 } 225 226 void DesktopProcessTest::DisconnectChannels() { 227 daemon_channel_.reset(); 228 network_channel_.reset(); 229 io_task_runner_ = NULL; 230 } 231 232 void DesktopProcessTest::PostDisconnectChannels() { 233 message_loop_.PostTask(FROM_HERE, base::Bind( 234 &DesktopProcessTest::DisconnectChannels, base::Unretained(this))); 235 } 236 237 void DesktopProcessTest::RunDesktopProcess() { 238 base::RunLoop run_loop; 239 base::Closure quit_ui_task_runner = base::Bind( 240 base::IgnoreResult(&base::SingleThreadTaskRunner::PostTask), 241 message_loop_.message_loop_proxy(), 242 FROM_HERE, run_loop.QuitClosure()); 243 scoped_refptr<AutoThreadTaskRunner> ui_task_runner = new AutoThreadTaskRunner( 244 message_loop_.message_loop_proxy(), quit_ui_task_runner); 245 246 io_task_runner_ = AutoThread::CreateWithType( 247 "IPC thread", ui_task_runner, base::MessageLoop::TYPE_IO); 248 249 std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID(); 250 daemon_channel_.reset(new IPC::ChannelProxy(IPC::ChannelHandle(channel_name), 251 IPC::Channel::MODE_SERVER, 252 &daemon_listener_, 253 io_task_runner_.get())); 254 255 scoped_ptr<MockDesktopEnvironmentFactory> desktop_environment_factory( 256 new MockDesktopEnvironmentFactory()); 257 EXPECT_CALL(*desktop_environment_factory, CreatePtr()) 258 .Times(AnyNumber()) 259 .WillRepeatedly(Invoke(this, 260 &DesktopProcessTest::CreateDesktopEnvironment)); 261 EXPECT_CALL(*desktop_environment_factory, SupportsAudioCapture()) 262 .Times(AnyNumber()) 263 .WillRepeatedly(Return(false)); 264 265 DesktopProcess desktop_process(ui_task_runner, io_task_runner_, channel_name); 266 EXPECT_TRUE(desktop_process.Start( 267 desktop_environment_factory.PassAs<DesktopEnvironmentFactory>())); 268 269 ui_task_runner = NULL; 270 run_loop.Run(); 271 } 272 273 void DesktopProcessTest::RunDeathTest() { 274 InSequence s; 275 EXPECT_CALL(daemon_listener_, OnChannelConnected(_)); 276 EXPECT_CALL(daemon_listener_, OnDesktopAttached(_)) 277 .WillOnce(DoAll( 278 Invoke(this, &DesktopProcessTest::OnDesktopAttached), 279 InvokeWithoutArgs(this, &DesktopProcessTest::SendCrashRequest))); 280 281 RunDesktopProcess(); 282 } 283 284 void DesktopProcessTest::SendCrashRequest() { 285 tracked_objects::Location location = FROM_HERE; 286 daemon_channel_->Send(new ChromotingDaemonMsg_Crash( 287 location.function_name(), location.file_name(), location.line_number())); 288 } 289 290 void DesktopProcessTest::SendStartSessionAgent() { 291 network_channel_->Send(new ChromotingNetworkDesktopMsg_StartSessionAgent( 292 "user@domain/rest-of-jid", ScreenResolution(), false)); 293 } 294 295 // Launches the desktop process and waits when it connects back. 296 TEST_F(DesktopProcessTest, Basic) { 297 InSequence s; 298 EXPECT_CALL(daemon_listener_, OnChannelConnected(_)); 299 EXPECT_CALL(daemon_listener_, OnDesktopAttached(_)) 300 .WillOnce(DoAll( 301 Invoke(this, &DesktopProcessTest::OnDesktopAttached), 302 InvokeWithoutArgs(this, &DesktopProcessTest::DisconnectChannels))); 303 304 RunDesktopProcess(); 305 } 306 307 // Launches the desktop process and waits when it connects back. 308 TEST_F(DesktopProcessTest, ConnectNetworkChannel) { 309 InSequence s; 310 EXPECT_CALL(daemon_listener_, OnChannelConnected(_)); 311 EXPECT_CALL(daemon_listener_, OnDesktopAttached(_)) 312 .WillOnce(Invoke(this, &DesktopProcessTest::ConnectNetworkChannel)); 313 EXPECT_CALL(network_listener_, OnChannelConnected(_)) 314 .WillOnce(InvokeWithoutArgs( 315 this, &DesktopProcessTest::DisconnectChannels)); 316 317 RunDesktopProcess(); 318 } 319 320 // Launches the desktop process, waits when it connects back and starts 321 // the desktop session agent. 322 TEST_F(DesktopProcessTest, StartSessionAgent) { 323 { 324 InSequence s; 325 EXPECT_CALL(daemon_listener_, OnChannelConnected(_)); 326 EXPECT_CALL(daemon_listener_, OnDesktopAttached(_)) 327 .WillOnce(Invoke(this, &DesktopProcessTest::ConnectNetworkChannel)); 328 EXPECT_CALL(network_listener_, OnChannelConnected(_)) 329 .WillOnce(InvokeWithoutArgs( 330 this, &DesktopProcessTest::SendStartSessionAgent)); 331 } 332 333 EXPECT_CALL(network_listener_, OnDesktopEnvironmentCreated()) 334 .WillOnce(InvokeWithoutArgs( 335 this, &DesktopProcessTest::PostDisconnectChannels)); 336 337 RunDesktopProcess(); 338 } 339 340 // Run the desktop process and ask it to crash. 341 TEST_F(DesktopProcessTest, DeathTest) { 342 testing::GTEST_FLAG(death_test_style) = "threadsafe"; 343 344 EXPECT_DEATH(RunDeathTest(), ""); 345 } 346 347 } // namespace remoting 348