1 // Copyright (c) 2013 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.
6 #include "base/bind_helpers.h"
7 #include "base/callback.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/process/process.h"
12 #include "base/process/process_handle.h"
13 #include "base/run_loop.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 "ipc/ipc_platform_file.h"
19 #include "remoting/base/auto_thread.h"
20 #include "remoting/base/auto_thread_task_runner.h"
21 #include "remoting/base/constants.h"
22 #include "remoting/host/chromoting_messages.h"
23 #include "remoting/host/desktop_process.h"
24 #include "remoting/host/desktop_session.h"
25 #include "remoting/host/desktop_session_connector.h"
26 #include "remoting/host/desktop_session_proxy.h"
27 #include "remoting/host/host_mock_objects.h"
28 #include "remoting/host/ipc_desktop_environment.h"
29 #include "remoting/host/screen_capturer_fake.h"
30 #include "remoting/protocol/protocol_mock_objects.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
34 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
35 #include "third_party/webrtc/modules/desktop_capture/screen_capturer_mock_objects.h"
38 using testing::AnyNumber
;
39 using testing::AtLeast
;
40 using testing::AtMost
;
41 using testing::DeleteArg
;
43 using testing::Return
;
44 using testing::ReturnRef
;
50 // Receives messages sent from the network process to the daemon.
51 class FakeDaemonSender
: public IPC::Sender
{
54 virtual ~FakeDaemonSender() {}
56 // IPC::Sender implementation.
57 virtual bool Send(IPC::Message
* message
) OVERRIDE
;
59 MOCK_METHOD3(ConnectTerminal
, void(int, const ScreenResolution
&, bool));
60 MOCK_METHOD1(DisconnectTerminal
, void(int));
61 MOCK_METHOD2(SetScreenResolution
, void(int, const ScreenResolution
&));
64 void OnMessageReceived(const IPC::Message
& message
);
66 DISALLOW_COPY_AND_ASSIGN(FakeDaemonSender
);
69 // Receives messages sent from the desktop process to the daemon.
70 class MockDaemonListener
: public IPC::Listener
{
72 MockDaemonListener() {}
73 virtual ~MockDaemonListener() {}
75 virtual bool OnMessageReceived(const IPC::Message
& message
) OVERRIDE
;
77 MOCK_METHOD1(OnDesktopAttached
, void(IPC::PlatformFileForTransit
));
78 MOCK_METHOD1(OnChannelConnected
, void(int32
));
79 MOCK_METHOD0(OnChannelError
, void());
82 DISALLOW_COPY_AND_ASSIGN(MockDaemonListener
);
85 bool FakeDaemonSender::Send(IPC::Message
* message
) {
86 OnMessageReceived(*message
);
91 void FakeDaemonSender::OnMessageReceived(const IPC::Message
& message
) {
93 IPC_BEGIN_MESSAGE_MAP(FakeDaemonSender
, message
)
94 IPC_MESSAGE_HANDLER(ChromotingNetworkHostMsg_ConnectTerminal
,
96 IPC_MESSAGE_HANDLER(ChromotingNetworkHostMsg_DisconnectTerminal
,
98 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_SetScreenResolution
,
100 IPC_MESSAGE_UNHANDLED(handled
= false)
101 IPC_END_MESSAGE_MAP()
103 EXPECT_TRUE(handled
);
106 bool MockDaemonListener::OnMessageReceived(const IPC::Message
& message
) {
108 IPC_BEGIN_MESSAGE_MAP(MockDaemonListener
, message
)
109 IPC_MESSAGE_HANDLER(ChromotingDesktopDaemonMsg_DesktopAttached
,
111 IPC_MESSAGE_UNHANDLED(handled
= false)
112 IPC_END_MESSAGE_MAP()
114 EXPECT_TRUE(handled
);
120 class IpcDesktopEnvironmentTest
: public testing::Test
{
122 IpcDesktopEnvironmentTest();
123 virtual ~IpcDesktopEnvironmentTest();
125 virtual void SetUp() OVERRIDE
;
127 void ConnectTerminal(int terminal_id
,
128 const ScreenResolution
& resolution
,
129 bool virtual_terminal
);
130 void DisconnectTerminal(int terminal_id
);
132 // Creates a DesktopEnvironment with a fake webrtc::ScreenCapturer, to mock
133 // DesktopEnvironmentFactory::Create().
134 DesktopEnvironment
* CreateDesktopEnvironment();
136 // Creates a dummy InputInjector, to mock
137 // DesktopEnvironment::CreateInputInjector().
138 InputInjector
* CreateInputInjector();
140 // Creates a fake webrtc::ScreenCapturer, to mock
141 // DesktopEnvironment::CreateVideoCapturer().
142 webrtc::ScreenCapturer
* CreateVideoCapturer();
144 void DeleteDesktopEnvironment();
146 // Forwards |event| to |clipboard_stub_|.
147 void ReflectClipboardEvent(const protocol::ClipboardEvent
& event
);
150 // Creates and starts an instance of desktop process object.
151 void CreateDesktopProcess();
153 // Destroys the desktop process object created by CreateDesktopProcess().
154 void DestoyDesktopProcess();
156 void OnDisconnectCallback();
158 // Invoked when ChromotingDesktopDaemonMsg_DesktopAttached message is
160 void OnDesktopAttached(IPC::PlatformFileForTransit desktop_pipe
);
162 // The main message loop.
163 base::MessageLoopForUI message_loop_
;
165 // Runs until |desktop_session_proxy_| is connected to the desktop.
166 scoped_ptr
<base::RunLoop
> setup_run_loop_
;
168 // Runs until there are references to |task_runner_|.
169 base::RunLoop main_run_loop_
;
171 scoped_refptr
<AutoThreadTaskRunner
> task_runner_
;
172 scoped_refptr
<AutoThreadTaskRunner
> io_task_runner_
;
174 std::string client_jid_
;
176 // Clipboard stub that receives clipboard events from the desktop process.
177 protocol::ClipboardStub
* clipboard_stub_
;
179 // The daemons's end of the daemon-to-desktop channel.
180 scoped_ptr
<IPC::ChannelProxy
> desktop_channel_
;
182 // Name of the daemon-to-desktop channel.
183 std::string desktop_channel_name_
;
185 // Delegate that is passed to |desktop_channel_|.
186 MockDaemonListener desktop_listener_
;
188 FakeDaemonSender daemon_channel_
;
190 scoped_ptr
<IpcDesktopEnvironmentFactory
> desktop_environment_factory_
;
191 scoped_ptr
<DesktopEnvironment
> desktop_environment_
;
193 // The IPC input injector.
194 scoped_ptr
<InputInjector
> input_injector_
;
196 // The IPC screen controls.
197 scoped_ptr
<ScreenControls
> screen_controls_
;
199 // The IPC screen capturer.
200 scoped_ptr
<webrtc::ScreenCapturer
> video_capturer_
;
202 // Represents the desktop process running in a user session.
203 scoped_ptr
<DesktopProcess
> desktop_process_
;
205 // Input injector owned by |desktop_process_|.
206 MockInputInjector
* remote_input_injector_
;
208 // The last |terminal_id| passed to ConnectTermina();
211 webrtc::MockScreenCapturerCallback screen_capturer_callback_
;
213 MockClientSessionControl client_session_control_
;
214 base::WeakPtrFactory
<ClientSessionControl
> client_session_control_factory_
;
217 IpcDesktopEnvironmentTest::IpcDesktopEnvironmentTest()
218 : client_jid_("user@domain/rest-of-jid"),
219 clipboard_stub_(NULL
),
220 remote_input_injector_(NULL
),
222 client_session_control_factory_(&client_session_control_
) {
225 IpcDesktopEnvironmentTest::~IpcDesktopEnvironmentTest() {
228 void IpcDesktopEnvironmentTest::SetUp() {
229 // Arrange to run |message_loop_| until no components depend on it.
230 task_runner_
= new AutoThreadTaskRunner(
231 message_loop_
.message_loop_proxy(), main_run_loop_
.QuitClosure());
233 io_task_runner_
= AutoThread::CreateWithType(
234 "IPC thread", task_runner_
, base::MessageLoop::TYPE_IO
);
236 setup_run_loop_
.reset(new base::RunLoop());
238 // Set expectation that the DaemonProcess will send DesktopAttached message
240 EXPECT_CALL(desktop_listener_
, OnChannelConnected(_
))
242 EXPECT_CALL(desktop_listener_
, OnDesktopAttached(_
))
244 .WillRepeatedly(Invoke(this,
245 &IpcDesktopEnvironmentTest::OnDesktopAttached
));
246 EXPECT_CALL(desktop_listener_
, OnChannelError())
248 .WillOnce(Invoke(this,
249 &IpcDesktopEnvironmentTest::DestoyDesktopProcess
));
251 // Intercept requests to connect and disconnect a terminal.
252 EXPECT_CALL(daemon_channel_
, ConnectTerminal(_
, _
, _
))
254 .WillRepeatedly(Invoke(this,
255 &IpcDesktopEnvironmentTest::ConnectTerminal
));
256 EXPECT_CALL(daemon_channel_
, DisconnectTerminal(_
))
258 .WillRepeatedly(Invoke(this,
259 &IpcDesktopEnvironmentTest::DisconnectTerminal
));
261 EXPECT_CALL(client_session_control_
, client_jid())
263 .WillRepeatedly(ReturnRef(client_jid_
));
264 EXPECT_CALL(client_session_control_
, DisconnectSession())
266 .WillRepeatedly(Invoke(
267 this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment
));
268 EXPECT_CALL(client_session_control_
, OnLocalMouseMoved(_
))
270 EXPECT_CALL(client_session_control_
, SetDisableInputs(_
))
273 // Create a desktop environment instance.
274 desktop_environment_factory_
.reset(new IpcDesktopEnvironmentFactory(
280 desktop_environment_
= desktop_environment_factory_
->Create(
281 client_session_control_factory_
.GetWeakPtr());
283 screen_controls_
= desktop_environment_
->CreateScreenControls();
285 // Create the input injector.
286 input_injector_
= desktop_environment_
->CreateInputInjector();
288 // Create the screen capturer.
290 desktop_environment_
->CreateVideoCapturer();
292 desktop_environment_
->SetCapabilities(std::string());
295 void IpcDesktopEnvironmentTest::ConnectTerminal(
297 const ScreenResolution
& resolution
,
298 bool virtual_terminal
) {
299 EXPECT_NE(terminal_id_
, terminal_id
);
301 terminal_id_
= terminal_id
;
302 CreateDesktopProcess();
305 void IpcDesktopEnvironmentTest::DisconnectTerminal(int terminal_id
) {
306 EXPECT_EQ(terminal_id_
, terminal_id
);
308 // The IPC desktop environment is fully destroyed now. Release the remaining
310 desktop_environment_factory_
.reset();
313 DesktopEnvironment
* IpcDesktopEnvironmentTest::CreateDesktopEnvironment() {
314 MockDesktopEnvironment
* desktop_environment
= new MockDesktopEnvironment();
315 EXPECT_CALL(*desktop_environment
, CreateAudioCapturerPtr())
317 EXPECT_CALL(*desktop_environment
, CreateInputInjectorPtr())
320 this, &IpcDesktopEnvironmentTest::CreateInputInjector
));
321 EXPECT_CALL(*desktop_environment
, CreateScreenControlsPtr())
323 EXPECT_CALL(*desktop_environment
, CreateVideoCapturerPtr())
326 this, &IpcDesktopEnvironmentTest::CreateVideoCapturer
));
327 EXPECT_CALL(*desktop_environment
, GetCapabilities())
329 EXPECT_CALL(*desktop_environment
, SetCapabilities(_
))
332 // Let tests know that the remote desktop environment is created.
333 message_loop_
.PostTask(FROM_HERE
, setup_run_loop_
->QuitClosure());
335 return desktop_environment
;
338 InputInjector
* IpcDesktopEnvironmentTest::CreateInputInjector() {
339 EXPECT_TRUE(remote_input_injector_
== NULL
);
340 remote_input_injector_
= new testing::StrictMock
<MockInputInjector
>();
342 EXPECT_CALL(*remote_input_injector_
, StartPtr(_
));
343 return remote_input_injector_
;
346 webrtc::ScreenCapturer
* IpcDesktopEnvironmentTest::CreateVideoCapturer() {
347 return new ScreenCapturerFake();
350 void IpcDesktopEnvironmentTest::DeleteDesktopEnvironment() {
351 input_injector_
.reset();
352 screen_controls_
.reset();
353 video_capturer_
.reset();
355 // Trigger DisconnectTerminal().
356 desktop_environment_
.reset();
359 void IpcDesktopEnvironmentTest::ReflectClipboardEvent(
360 const protocol::ClipboardEvent
& event
) {
361 clipboard_stub_
->InjectClipboardEvent(event
);
364 void IpcDesktopEnvironmentTest::CreateDesktopProcess() {
365 EXPECT_TRUE(task_runner_
.get());
366 EXPECT_TRUE(io_task_runner_
.get());
368 // Create the daemon end of the daemon-to-desktop channel.
369 desktop_channel_name_
= IPC::Channel::GenerateUniqueRandomChannelID();
370 desktop_channel_
.reset(
371 new IPC::ChannelProxy(IPC::ChannelHandle(desktop_channel_name_
),
372 IPC::Channel::MODE_SERVER
,
374 io_task_runner_
.get()));
376 // Create and start the desktop process.
377 desktop_process_
.reset(new DesktopProcess(task_runner_
,
379 desktop_channel_name_
));
381 scoped_ptr
<MockDesktopEnvironmentFactory
> desktop_environment_factory(
382 new MockDesktopEnvironmentFactory());
383 EXPECT_CALL(*desktop_environment_factory
, CreatePtr())
385 .WillRepeatedly(Invoke(
386 this, &IpcDesktopEnvironmentTest::CreateDesktopEnvironment
));
387 EXPECT_CALL(*desktop_environment_factory
, SupportsAudioCapture())
389 .WillRepeatedly(Return(false));
391 EXPECT_TRUE(desktop_process_
->Start(
392 desktop_environment_factory
.PassAs
<DesktopEnvironmentFactory
>()));
395 void IpcDesktopEnvironmentTest::DestoyDesktopProcess() {
396 desktop_channel_
.reset();
397 if (desktop_process_
) {
398 desktop_process_
->OnChannelError();
399 desktop_process_
.reset();
401 remote_input_injector_
= NULL
;
404 void IpcDesktopEnvironmentTest::OnDisconnectCallback() {
405 DeleteDesktopEnvironment();
408 void IpcDesktopEnvironmentTest::OnDesktopAttached(
409 IPC::PlatformFileForTransit desktop_pipe
) {
411 // Instruct DesktopSessionProxy to connect to the network-to-desktop pipe.
412 desktop_environment_factory_
->OnDesktopSessionAgentAttached(
413 terminal_id_
, base::GetCurrentProcessHandle(), desktop_pipe
);
416 // Runs until the desktop is attached and exits immediately after that.
417 TEST_F(IpcDesktopEnvironmentTest
, Basic
) {
418 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
419 new protocol::MockClipboardStub());
420 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
423 // Start the input injector and screen capturer.
424 input_injector_
->Start(clipboard_stub
.PassAs
<protocol::ClipboardStub
>());
426 // Run the message loop until the desktop is attached.
427 setup_run_loop_
->Run();
430 DeleteDesktopEnvironment();
433 io_task_runner_
= NULL
;
434 main_run_loop_
.Run();
437 // Tests that the video capturer receives a frame over IPC.
438 TEST_F(IpcDesktopEnvironmentTest
, CaptureFrame
) {
439 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
440 new protocol::MockClipboardStub());
441 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
444 // Start the input injector and screen capturer.
445 input_injector_
->Start(clipboard_stub
.PassAs
<protocol::ClipboardStub
>());
446 video_capturer_
->Start(&screen_capturer_callback_
);
448 // Run the message loop until the desktop is attached.
449 setup_run_loop_
->Run();
451 // Stop the test when the first frame is captured.
452 EXPECT_CALL(screen_capturer_callback_
, OnCaptureCompleted(_
))
456 this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment
)));
458 // Capture a single frame.
459 video_capturer_
->Capture(webrtc::DesktopRegion());
462 io_task_runner_
= NULL
;
463 main_run_loop_
.Run();
466 // Tests that attaching to a new desktop works.
467 TEST_F(IpcDesktopEnvironmentTest
, Reattach
) {
468 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
469 new protocol::MockClipboardStub());
470 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
473 // Start the input injector and screen capturer.
474 input_injector_
->Start(clipboard_stub
.PassAs
<protocol::ClipboardStub
>());
475 video_capturer_
->Start(&screen_capturer_callback_
);
477 // Run the message loop until the desktop is attached.
478 setup_run_loop_
->Run();
480 // Create and start a new desktop process object.
481 setup_run_loop_
.reset(new base::RunLoop());
482 DestoyDesktopProcess();
483 CreateDesktopProcess();
484 setup_run_loop_
->Run();
487 DeleteDesktopEnvironment();
490 io_task_runner_
= NULL
;
491 main_run_loop_
.Run();
494 // Tests injection of clipboard events.
495 TEST_F(IpcDesktopEnvironmentTest
, InjectClipboardEvent
) {
496 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
497 new protocol::MockClipboardStub());
498 clipboard_stub_
= clipboard_stub
.get();
500 // Stop the test when a clipboard event is received from the desktop process.
501 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
503 .WillOnce(InvokeWithoutArgs(
504 this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment
));
506 // Start the input injector and screen capturer.
507 input_injector_
->Start(clipboard_stub
.PassAs
<protocol::ClipboardStub
>());
508 video_capturer_
->Start(&screen_capturer_callback_
);
510 // Run the message loop until the desktop is attached.
511 setup_run_loop_
->Run();
513 // Expect a single clipboard event.
514 EXPECT_CALL(*remote_input_injector_
, InjectClipboardEvent(_
))
516 .WillOnce(Invoke(this,
517 &IpcDesktopEnvironmentTest::ReflectClipboardEvent
));
519 // Send a clipboard event.
520 protocol::ClipboardEvent event
;
521 event
.set_mime_type(kMimeTypeTextUtf8
);
523 input_injector_
->InjectClipboardEvent(event
);
526 io_task_runner_
= NULL
;
527 main_run_loop_
.Run();
530 // Tests injection of key events.
531 TEST_F(IpcDesktopEnvironmentTest
, InjectKeyEvent
) {
532 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
533 new protocol::MockClipboardStub());
534 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
537 // Start the input injector and screen capturer.
538 input_injector_
->Start(clipboard_stub
.PassAs
<protocol::ClipboardStub
>());
539 video_capturer_
->Start(&screen_capturer_callback_
);
541 // Run the message loop until the desktop is attached.
542 setup_run_loop_
->Run();
544 // Expect a single key event.
545 EXPECT_CALL(*remote_input_injector_
, InjectKeyEvent(_
))
547 .WillRepeatedly(InvokeWithoutArgs(
548 this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment
));
551 protocol::KeyEvent event
;
552 event
.set_usb_keycode(0x070004);
553 event
.set_pressed(true);
554 input_injector_
->InjectKeyEvent(event
);
557 io_task_runner_
= NULL
;
558 main_run_loop_
.Run();
561 // Tests injection of text events.
562 TEST_F(IpcDesktopEnvironmentTest
, InjectTextEvent
) {
563 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
564 new protocol::MockClipboardStub());
565 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
568 // Start the input injector and screen capturer.
569 input_injector_
->Start(clipboard_stub
.PassAs
<protocol::ClipboardStub
>());
570 video_capturer_
->Start(&screen_capturer_callback_
);
572 // Run the message loop until the desktop is attached.
573 setup_run_loop_
->Run();
575 // Expect a single text event.
576 EXPECT_CALL(*remote_input_injector_
, InjectTextEvent(_
))
578 .WillRepeatedly(InvokeWithoutArgs(
579 this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment
));
581 // Send a text event.
582 protocol::TextEvent event
;
583 event
.set_text("hello");
584 input_injector_
->InjectTextEvent(event
);
587 io_task_runner_
= NULL
;
588 main_run_loop_
.Run();
591 // Tests injection of mouse events.
592 TEST_F(IpcDesktopEnvironmentTest
, InjectMouseEvent
) {
593 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
594 new protocol::MockClipboardStub());
595 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
598 // Start the input injector and screen capturer.
599 input_injector_
->Start(clipboard_stub
.PassAs
<protocol::ClipboardStub
>());
600 video_capturer_
->Start(&screen_capturer_callback_
);
602 // Run the message loop until the desktop is attached.
603 setup_run_loop_
->Run();
605 // Expect a single mouse event.
606 EXPECT_CALL(*remote_input_injector_
, InjectMouseEvent(_
))
608 .WillOnce(InvokeWithoutArgs(
609 this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment
));
611 // Send a mouse event.
612 protocol::MouseEvent event
;
615 input_injector_
->InjectMouseEvent(event
);
618 io_task_runner_
= NULL
;
619 main_run_loop_
.Run();
622 // Tests that setting the desktop resolution works.
623 TEST_F(IpcDesktopEnvironmentTest
, SetScreenResolution
) {
624 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
625 new protocol::MockClipboardStub());
626 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
629 // Start the input injector and screen capturer.
630 input_injector_
->Start(clipboard_stub
.PassAs
<protocol::ClipboardStub
>());
631 video_capturer_
->Start(&screen_capturer_callback_
);
633 // Run the message loop until the desktop is attached.
634 setup_run_loop_
->Run();
636 EXPECT_CALL(daemon_channel_
, SetScreenResolution(_
, _
))
638 .WillOnce(InvokeWithoutArgs(
639 this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment
));
641 // Change the desktop resolution.
642 screen_controls_
->SetScreenResolution(ScreenResolution(
643 webrtc::DesktopSize(100, 100),
644 webrtc::DesktopVector(96, 96)));
647 io_task_runner_
= NULL
;
648 main_run_loop_
.Run();
651 } // namespace remoting