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/fake_desktop_capturer.h" 23 #include "remoting/host/host_exit_codes.h" 24 #include "remoting/host/host_mock_objects.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::DesktopCapturer, 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::DesktopCapturer, to mock 117 // DesktopEnvironment::CreateVideoCapturer(). 118 webrtc::DesktopCapturer* 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::MessageLoopForUI 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 160 DesktopProcessTest::~DesktopProcessTest() { 161 } 162 163 void DesktopProcessTest::SetUp() { 164 } 165 166 void DesktopProcessTest::TearDown() { 167 } 168 169 void DesktopProcessTest::ConnectNetworkChannel( 170 IPC::PlatformFileForTransit desktop_process) { 171 172 #if defined(OS_POSIX) 173 IPC::ChannelHandle channel_handle(std::string(), desktop_process); 174 #elif defined(OS_WIN) 175 IPC::ChannelHandle channel_handle(desktop_process); 176 #endif // defined(OS_WIN) 177 178 network_channel_ = IPC::ChannelProxy::Create(channel_handle, 179 IPC::Channel::MODE_CLIENT, 180 &network_listener_, 181 io_task_runner_.get()); 182 } 183 184 void DesktopProcessTest::OnDesktopAttached( 185 IPC::PlatformFileForTransit desktop_process) { 186 #if defined(OS_POSIX) 187 DCHECK(desktop_process.auto_close); 188 189 base::File closer(IPC::PlatformFileForTransitToFile(desktop_process)); 190 #endif // defined(OS_POSIX) 191 } 192 193 DesktopEnvironment* DesktopProcessTest::CreateDesktopEnvironment() { 194 MockDesktopEnvironment* desktop_environment = new MockDesktopEnvironment(); 195 EXPECT_CALL(*desktop_environment, CreateAudioCapturerPtr()) 196 .Times(0); 197 EXPECT_CALL(*desktop_environment, CreateInputInjectorPtr()) 198 .Times(AtMost(1)) 199 .WillOnce(Invoke(this, &DesktopProcessTest::CreateInputInjector)); 200 EXPECT_CALL(*desktop_environment, CreateScreenControlsPtr()) 201 .Times(AtMost(1)); 202 EXPECT_CALL(*desktop_environment, CreateVideoCapturerPtr()) 203 .Times(AtMost(1)) 204 .WillOnce(Invoke(this, &DesktopProcessTest::CreateVideoCapturer)); 205 EXPECT_CALL(*desktop_environment, GetCapabilities()) 206 .Times(AtMost(1)); 207 EXPECT_CALL(*desktop_environment, SetCapabilities(_)) 208 .Times(AtMost(1)); 209 210 // Notify the test that the desktop environment has been created. 211 network_listener_.OnDesktopEnvironmentCreated(); 212 return desktop_environment; 213 } 214 215 InputInjector* DesktopProcessTest::CreateInputInjector() { 216 MockInputInjector* input_injector = new MockInputInjector(); 217 EXPECT_CALL(*input_injector, StartPtr(_)); 218 return input_injector; 219 } 220 221 webrtc::DesktopCapturer* DesktopProcessTest::CreateVideoCapturer() { 222 return new FakeDesktopCapturer(); 223 } 224 225 void DesktopProcessTest::DisconnectChannels() { 226 daemon_channel_.reset(); 227 network_channel_.reset(); 228 io_task_runner_ = NULL; 229 } 230 231 void DesktopProcessTest::PostDisconnectChannels() { 232 message_loop_.PostTask(FROM_HERE, base::Bind( 233 &DesktopProcessTest::DisconnectChannels, base::Unretained(this))); 234 } 235 236 void DesktopProcessTest::RunDesktopProcess() { 237 base::RunLoop run_loop; 238 base::Closure quit_ui_task_runner = base::Bind( 239 base::IgnoreResult(&base::SingleThreadTaskRunner::PostTask), 240 message_loop_.message_loop_proxy(), 241 FROM_HERE, run_loop.QuitClosure()); 242 scoped_refptr<AutoThreadTaskRunner> ui_task_runner = new AutoThreadTaskRunner( 243 message_loop_.message_loop_proxy(), quit_ui_task_runner); 244 245 io_task_runner_ = AutoThread::CreateWithType( 246 "IPC thread", ui_task_runner, base::MessageLoop::TYPE_IO); 247 248 std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID(); 249 daemon_channel_ = IPC::ChannelProxy::Create(IPC::ChannelHandle(channel_name), 250 IPC::Channel::MODE_SERVER, 251 &daemon_listener_, 252 io_task_runner_.get()); 253 254 scoped_ptr<MockDesktopEnvironmentFactory> desktop_environment_factory( 255 new MockDesktopEnvironmentFactory()); 256 EXPECT_CALL(*desktop_environment_factory, CreatePtr()) 257 .Times(AnyNumber()) 258 .WillRepeatedly(Invoke(this, 259 &DesktopProcessTest::CreateDesktopEnvironment)); 260 EXPECT_CALL(*desktop_environment_factory, SupportsAudioCapture()) 261 .Times(AnyNumber()) 262 .WillRepeatedly(Return(false)); 263 264 DesktopProcess desktop_process(ui_task_runner, io_task_runner_, channel_name); 265 EXPECT_TRUE(desktop_process.Start( 266 desktop_environment_factory.PassAs<DesktopEnvironmentFactory>())); 267 268 ui_task_runner = NULL; 269 run_loop.Run(); 270 } 271 272 void DesktopProcessTest::RunDeathTest() { 273 InSequence s; 274 EXPECT_CALL(daemon_listener_, OnChannelConnected(_)); 275 EXPECT_CALL(daemon_listener_, OnDesktopAttached(_)) 276 .WillOnce(DoAll( 277 Invoke(this, &DesktopProcessTest::OnDesktopAttached), 278 InvokeWithoutArgs(this, &DesktopProcessTest::SendCrashRequest))); 279 280 RunDesktopProcess(); 281 } 282 283 void DesktopProcessTest::SendCrashRequest() { 284 tracked_objects::Location location = FROM_HERE; 285 daemon_channel_->Send(new ChromotingDaemonMsg_Crash( 286 location.function_name(), location.file_name(), location.line_number())); 287 } 288 289 void DesktopProcessTest::SendStartSessionAgent() { 290 network_channel_->Send(new ChromotingNetworkDesktopMsg_StartSessionAgent( 291 "user@domain/rest-of-jid", ScreenResolution(), false)); 292 } 293 294 // Launches the desktop process and waits when it connects back. 295 TEST_F(DesktopProcessTest, Basic) { 296 InSequence s; 297 EXPECT_CALL(daemon_listener_, OnChannelConnected(_)); 298 EXPECT_CALL(daemon_listener_, OnDesktopAttached(_)) 299 .WillOnce(DoAll( 300 Invoke(this, &DesktopProcessTest::OnDesktopAttached), 301 InvokeWithoutArgs(this, &DesktopProcessTest::DisconnectChannels))); 302 303 RunDesktopProcess(); 304 } 305 306 // Launches the desktop process and waits when it connects back. 307 TEST_F(DesktopProcessTest, ConnectNetworkChannel) { 308 InSequence s; 309 EXPECT_CALL(daemon_listener_, OnChannelConnected(_)); 310 EXPECT_CALL(daemon_listener_, OnDesktopAttached(_)) 311 .WillOnce(Invoke(this, &DesktopProcessTest::ConnectNetworkChannel)); 312 EXPECT_CALL(network_listener_, OnChannelConnected(_)) 313 .WillOnce(InvokeWithoutArgs( 314 this, &DesktopProcessTest::DisconnectChannels)); 315 316 RunDesktopProcess(); 317 } 318 319 // Launches the desktop process, waits when it connects back and starts 320 // the desktop session agent. 321 TEST_F(DesktopProcessTest, StartSessionAgent) { 322 { 323 InSequence s; 324 EXPECT_CALL(daemon_listener_, OnChannelConnected(_)); 325 EXPECT_CALL(daemon_listener_, OnDesktopAttached(_)) 326 .WillOnce(Invoke(this, &DesktopProcessTest::ConnectNetworkChannel)); 327 EXPECT_CALL(network_listener_, OnChannelConnected(_)) 328 .WillOnce(InvokeWithoutArgs( 329 this, &DesktopProcessTest::SendStartSessionAgent)); 330 } 331 332 EXPECT_CALL(network_listener_, OnDesktopEnvironmentCreated()) 333 .WillOnce(InvokeWithoutArgs( 334 this, &DesktopProcessTest::PostDisconnectChannels)); 335 336 RunDesktopProcess(); 337 } 338 339 // Run the desktop process and ask it to crash. 340 TEST_F(DesktopProcessTest, DeathTest) { 341 testing::GTEST_FLAG(death_test_style) = "threadsafe"; 342 343 EXPECT_DEATH(RunDeathTest(), ""); 344 } 345 346 } // namespace remoting 347