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.
5 #include "base/message_loop/message_loop.h"
6 #include "remoting/base/auto_thread_task_runner.h"
7 #include "remoting/base/constants.h"
8 #include "remoting/host/audio_capturer.h"
9 #include "remoting/host/client_session.h"
10 #include "remoting/host/desktop_environment.h"
11 #include "remoting/host/host_mock_objects.h"
12 #include "remoting/host/screen_capturer_fake.h"
13 #include "remoting/protocol/protocol_mock_objects.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
16 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
17 #include "third_party/webrtc/modules/desktop_capture/screen_capturer_mock_objects.h"
21 using protocol::MockConnectionToClient
;
22 using protocol::MockClientStub
;
23 using protocol::MockHostStub
;
24 using protocol::MockInputStub
;
25 using protocol::MockSession
;
26 using protocol::MockVideoStub
;
27 using protocol::SessionConfig
;
30 using testing::AnyNumber
;
31 using testing::AtMost
;
32 using testing::DeleteArg
;
34 using testing::Expectation
;
35 using testing::Return
;
36 using testing::ReturnRef
;
37 using testing::Sequence
;
41 ACTION_P2(InjectClipboardEvent
, connection
, event
) {
42 connection
->clipboard_stub()->InjectClipboardEvent(event
);
45 ACTION_P2(InjectKeyEvent
, connection
, event
) {
46 connection
->input_stub()->InjectKeyEvent(event
);
49 ACTION_P2(InjectMouseEvent
, connection
, event
) {
50 connection
->input_stub()->InjectMouseEvent(event
);
53 ACTION_P2(LocalMouseMoved
, client_session
, event
) {
54 client_session
->OnLocalMouseMoved(
55 webrtc::DesktopVector(event
.x(), event
.y()));
60 class ClientSessionTest
: public testing::Test
{
62 ClientSessionTest() : client_jid_("user@domain/rest-of-jid") {}
64 virtual void SetUp() OVERRIDE
;
65 virtual void TearDown() OVERRIDE
;
67 // Disconnects the client session.
68 void DisconnectClientSession();
70 // Stops and releases the ClientSession, allowing the MessageLoop to quit.
71 void StopClientSession();
74 // Creates a DesktopEnvironment with a fake webrtc::ScreenCapturer, to mock
75 // DesktopEnvironmentFactory::Create().
76 DesktopEnvironment
* CreateDesktopEnvironment();
78 // Returns |input_injector_| created and initialized by SetUp(), to mock
79 // DesktopEnvironment::CreateInputInjector().
80 InputInjector
* CreateInputInjector();
82 // Creates a fake webrtc::ScreenCapturer, to mock
83 // DesktopEnvironment::CreateVideoCapturer().
84 webrtc::ScreenCapturer
* CreateVideoCapturer();
86 // Notifies the client session that the client connection has been
87 // authenticated and channels have been connected. This effectively enables
88 // the input pipe line and starts video capturing.
89 void ConnectClientSession();
91 // Invoked when the last reference to the AutoThreadTaskRunner has been
92 // released and quits the message loop to finish the test.
93 void QuitMainMessageLoop();
95 // Message loop passed to |client_session_| to perform all functions on.
96 base::MessageLoop message_loop_
;
98 // ClientSession instance under test.
99 scoped_ptr
<ClientSession
> client_session_
;
101 // ClientSession::EventHandler mock for use in tests.
102 MockClientSessionEventHandler session_event_handler_
;
104 // Storage for values to be returned by the protocol::Session mock.
105 SessionConfig session_config_
;
106 const std::string client_jid_
;
108 // Stubs returned to |client_session_| components by |connection_|.
109 MockClientStub client_stub_
;
110 MockVideoStub video_stub_
;
112 // DesktopEnvironment owns |input_injector_|, but input injection tests need
113 // to express expectations on it.
114 scoped_ptr
<MockInputInjector
> input_injector_
;
116 // ClientSession owns |connection_| but tests need it to inject fake events.
117 MockConnectionToClient
* connection_
;
119 scoped_ptr
<MockDesktopEnvironmentFactory
> desktop_environment_factory_
;
122 void ClientSessionTest::SetUp() {
123 // Arrange to run |message_loop_| until no components depend on it.
124 scoped_refptr
<AutoThreadTaskRunner
> ui_task_runner
= new AutoThreadTaskRunner(
125 message_loop_
.message_loop_proxy(),
126 base::Bind(&ClientSessionTest::QuitMainMessageLoop
,
127 base::Unretained(this)));
129 desktop_environment_factory_
.reset(new MockDesktopEnvironmentFactory());
130 EXPECT_CALL(*desktop_environment_factory_
, CreatePtr())
132 .WillRepeatedly(Invoke(this,
133 &ClientSessionTest::CreateDesktopEnvironment
));
134 EXPECT_CALL(*desktop_environment_factory_
, SupportsAudioCapture())
136 .WillRepeatedly(Return(false));
138 input_injector_
.reset(new MockInputInjector());
140 session_config_
= SessionConfig::ForTest();
142 // Mock protocol::Session APIs called directly by ClientSession.
143 protocol::MockSession
* session
= new MockSession();
144 EXPECT_CALL(*session
, config()).WillRepeatedly(ReturnRef(session_config_
));
145 EXPECT_CALL(*session
, jid()).WillRepeatedly(ReturnRef(client_jid_
));
146 EXPECT_CALL(*session
, SetEventHandler(_
));
148 // Mock protocol::ConnectionToClient APIs called directly by ClientSession.
149 // HostStub is not touched by ClientSession, so we can safely pass NULL.
150 scoped_ptr
<MockConnectionToClient
> connection(
151 new MockConnectionToClient(session
, NULL
));
152 EXPECT_CALL(*connection
, session()).WillRepeatedly(Return(session
));
153 EXPECT_CALL(*connection
, client_stub())
154 .WillRepeatedly(Return(&client_stub_
));
155 EXPECT_CALL(*connection
, video_stub()).WillRepeatedly(Return(&video_stub_
));
156 EXPECT_CALL(*connection
, Disconnect());
157 connection_
= connection
.get();
159 client_session_
.reset(new ClientSession(
160 &session_event_handler_
,
161 ui_task_runner
, // Audio thread.
162 ui_task_runner
, // Input thread.
163 ui_task_runner
, // Capture thread.
164 ui_task_runner
, // Encode thread.
165 ui_task_runner
, // Network thread.
166 ui_task_runner
, // UI thread.
167 connection
.PassAs
<protocol::ConnectionToClient
>(),
168 desktop_environment_factory_
.get(),
173 void ClientSessionTest::TearDown() {
174 // Verify that the client session has been stopped.
175 EXPECT_TRUE(!client_session_
);
178 void ClientSessionTest::DisconnectClientSession() {
179 client_session_
->DisconnectSession();
180 // MockSession won't trigger OnConnectionClosed, so fake it.
181 client_session_
->OnConnectionClosed(client_session_
->connection(),
185 void ClientSessionTest::StopClientSession() {
186 client_session_
.reset();
188 desktop_environment_factory_
.reset();
191 DesktopEnvironment
* ClientSessionTest::CreateDesktopEnvironment() {
192 MockDesktopEnvironment
* desktop_environment
= new MockDesktopEnvironment();
193 EXPECT_CALL(*desktop_environment
, CreateAudioCapturerPtr())
195 EXPECT_CALL(*desktop_environment
, CreateInputInjectorPtr())
196 .WillOnce(Invoke(this, &ClientSessionTest::CreateInputInjector
));
197 EXPECT_CALL(*desktop_environment
, CreateScreenControlsPtr())
199 EXPECT_CALL(*desktop_environment
, CreateVideoCapturerPtr())
200 .WillOnce(Invoke(this, &ClientSessionTest::CreateVideoCapturer
));
201 EXPECT_CALL(*desktop_environment
, GetCapabilities())
203 EXPECT_CALL(*desktop_environment
, SetCapabilities(_
))
206 return desktop_environment
;
209 InputInjector
* ClientSessionTest::CreateInputInjector() {
210 EXPECT_TRUE(input_injector_
);
211 return input_injector_
.release();
214 webrtc::ScreenCapturer
* ClientSessionTest::CreateVideoCapturer() {
215 return new ScreenCapturerFake();
218 void ClientSessionTest::ConnectClientSession() {
219 client_session_
->OnConnectionAuthenticated(client_session_
->connection());
220 client_session_
->OnConnectionChannelsConnected(client_session_
->connection());
223 void ClientSessionTest::QuitMainMessageLoop() {
224 message_loop_
.PostTask(FROM_HERE
, base::MessageLoop::QuitClosure());
227 MATCHER_P2(EqualsClipboardEvent
, m
, d
, "") {
228 return (strcmp(arg
.mime_type().c_str(), m
) == 0 &&
229 memcmp(arg
.data().data(), d
, arg
.data().size()) == 0);
232 TEST_F(ClientSessionTest
, ClipboardStubFilter
) {
233 protocol::ClipboardEvent clipboard_event1
;
234 clipboard_event1
.set_mime_type(kMimeTypeTextUtf8
);
235 clipboard_event1
.set_data("a");
237 protocol::ClipboardEvent clipboard_event2
;
238 clipboard_event2
.set_mime_type(kMimeTypeTextUtf8
);
239 clipboard_event2
.set_data("b");
241 protocol::ClipboardEvent clipboard_event3
;
242 clipboard_event3
.set_mime_type(kMimeTypeTextUtf8
);
243 clipboard_event3
.set_data("c");
245 Expectation authenticated
=
246 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
247 .WillOnce(Return(true));
248 EXPECT_CALL(*input_injector_
, StartPtr(_
))
249 .After(authenticated
);
250 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
251 .After(authenticated
);
253 // Wait for the first video packet to be captured to make sure that
254 // the injected input will go though. Otherwise mouse events will be blocked
255 // by the mouse clamping filter.
257 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
259 .After(authenticated
)
261 // This event should get through to the clipboard stub.
262 InjectClipboardEvent(connection_
, clipboard_event2
),
263 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
264 // This event should not get through to the clipboard stub,
265 // because the client has disconnected.
266 InjectClipboardEvent(connection_
, clipboard_event3
),
267 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
268 EXPECT_CALL(*input_injector_
, InjectClipboardEvent(EqualsClipboardEvent(
269 kMimeTypeTextUtf8
, "b")))
271 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
274 // This event should not get through to the clipboard stub,
275 // because the client isn't authenticated yet.
276 connection_
->clipboard_stub()->InjectClipboardEvent(clipboard_event1
);
278 ConnectClientSession();
284 MATCHER_P2(EqualsUsbEvent
, usb_keycode
, pressed
, "") {
285 return arg
.usb_keycode() == (unsigned int)usb_keycode
&&
286 arg
.pressed() == pressed
;
289 MATCHER_P2(EqualsMouseEvent
, x
, y
, "") {
290 return arg
.x() == x
&& arg
.y() == y
;
293 MATCHER_P2(EqualsMouseButtonEvent
, button
, down
, "") {
294 return arg
.button() == button
&& arg
.button_down() == down
;
299 TEST_F(ClientSessionTest
, InputStubFilter
) {
300 protocol::KeyEvent key_event1
;
301 key_event1
.set_pressed(true);
302 key_event1
.set_usb_keycode(1);
304 protocol::KeyEvent key_event2_down
;
305 key_event2_down
.set_pressed(true);
306 key_event2_down
.set_usb_keycode(2);
308 protocol::KeyEvent key_event2_up
;
309 key_event2_up
.set_pressed(false);
310 key_event2_up
.set_usb_keycode(2);
312 protocol::KeyEvent key_event3
;
313 key_event3
.set_pressed(true);
314 key_event3
.set_usb_keycode(3);
316 protocol::MouseEvent mouse_event1
;
317 mouse_event1
.set_x(100);
318 mouse_event1
.set_y(101);
320 protocol::MouseEvent mouse_event2
;
321 mouse_event2
.set_x(200);
322 mouse_event2
.set_y(201);
324 protocol::MouseEvent mouse_event3
;
325 mouse_event3
.set_x(300);
326 mouse_event3
.set_y(301);
328 Expectation authenticated
=
329 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
330 .WillOnce(Return(true));
331 EXPECT_CALL(*input_injector_
, StartPtr(_
))
332 .After(authenticated
);
333 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
334 .After(authenticated
);
336 // Wait for the first video packet to be captured to make sure that
337 // the injected input will go though. Otherwise mouse events will be blocked
338 // by the mouse clamping filter.
340 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
342 .After(authenticated
)
344 // These events should get through to the input stub.
345 InjectKeyEvent(connection_
, key_event2_down
),
346 InjectKeyEvent(connection_
, key_event2_up
),
347 InjectMouseEvent(connection_
, mouse_event2
),
348 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
349 // These events should not get through to the input stub,
350 // because the client has disconnected.
351 InjectKeyEvent(connection_
, key_event3
),
352 InjectMouseEvent(connection_
, mouse_event3
),
353 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
354 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(2, true)))
356 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(2, false)))
358 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseEvent(200, 201)))
360 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
363 // These events should not get through to the input stub,
364 // because the client isn't authenticated yet.
365 connection_
->input_stub()->InjectKeyEvent(key_event1
);
366 connection_
->input_stub()->InjectMouseEvent(mouse_event1
);
368 ConnectClientSession();
372 TEST_F(ClientSessionTest
, LocalInputTest
) {
373 protocol::MouseEvent mouse_event1
;
374 mouse_event1
.set_x(100);
375 mouse_event1
.set_y(101);
376 protocol::MouseEvent mouse_event2
;
377 mouse_event2
.set_x(200);
378 mouse_event2
.set_y(201);
379 protocol::MouseEvent mouse_event3
;
380 mouse_event3
.set_x(300);
381 mouse_event3
.set_y(301);
383 Expectation authenticated
=
384 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
385 .WillOnce(Return(true));
386 EXPECT_CALL(*input_injector_
, StartPtr(_
))
387 .After(authenticated
);
388 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
389 .After(authenticated
);
391 // Wait for the first video packet to be captured to make sure that
392 // the injected input will go though. Otherwise mouse events will be blocked
393 // by the mouse clamping filter.
395 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
397 .After(authenticated
)
399 // This event should get through to the input stub.
400 InjectMouseEvent(connection_
, mouse_event1
),
402 // The OS echoes the injected event back.
403 LocalMouseMoved(client_session_
.get(), mouse_event1
),
404 #endif // !defined(OS_WIN)
405 // This one should get throught as well.
406 InjectMouseEvent(connection_
, mouse_event2
),
407 // Now this is a genuine local event.
408 LocalMouseMoved(client_session_
.get(), mouse_event1
),
409 // This one should be blocked because of the previous local input
411 InjectMouseEvent(connection_
, mouse_event3
),
412 // TODO(jamiewalch): Verify that remote inputs are re-enabled
413 // eventually (via dependency injection, not sleep!)
414 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
415 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
416 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseEvent(100, 101)))
418 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseEvent(200, 201)))
420 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
423 ConnectClientSession();
427 TEST_F(ClientSessionTest
, RestoreEventState
) {
428 protocol::KeyEvent key1
;
429 key1
.set_pressed(true);
430 key1
.set_usb_keycode(1);
432 protocol::KeyEvent key2
;
433 key2
.set_pressed(true);
434 key2
.set_usb_keycode(2);
436 protocol::MouseEvent mousedown
;
437 mousedown
.set_button(protocol::MouseEvent::BUTTON_LEFT
);
438 mousedown
.set_button_down(true);
440 Expectation authenticated
=
441 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
442 .WillOnce(Return(true));
443 EXPECT_CALL(*input_injector_
, StartPtr(_
))
444 .After(authenticated
);
445 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
446 .After(authenticated
);
448 // Wait for the first video packet to be captured to make sure that
449 // the injected input will go though. Otherwise mouse events will be blocked
450 // by the mouse clamping filter.
452 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
454 .After(authenticated
)
456 InjectKeyEvent(connection_
, key1
),
457 InjectKeyEvent(connection_
, key2
),
458 InjectMouseEvent(connection_
, mousedown
),
459 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
460 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
461 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(1, true)))
463 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(2, true)))
465 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseButtonEvent(
466 protocol::MouseEvent::BUTTON_LEFT
, true)))
468 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(1, false)))
470 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(2, false)))
472 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseButtonEvent(
473 protocol::MouseEvent::BUTTON_LEFT
, false)))
475 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
478 ConnectClientSession();
482 TEST_F(ClientSessionTest
, ClampMouseEvents
) {
483 Expectation authenticated
=
484 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
485 .WillOnce(Return(true));
486 EXPECT_CALL(*input_injector_
, StartPtr(_
))
487 .After(authenticated
);
488 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
489 .After(authenticated
);
490 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
491 .After(authenticated
);
493 Expectation connected
= authenticated
;
495 int input_x
[3] = { -999, 100, 999 };
496 int expected_x
[3] = { 0, 100, ScreenCapturerFake::kWidth
- 1 };
497 int input_y
[3] = { -999, 50, 999 };
498 int expected_y
[3] = { 0, 50, ScreenCapturerFake::kHeight
- 1 };
500 protocol::MouseEvent expected_event
;
501 for (int j
= 0; j
< 3; j
++) {
502 for (int i
= 0; i
< 3; i
++) {
503 protocol::MouseEvent injected_event
;
504 injected_event
.set_x(input_x
[i
]);
505 injected_event
.set_y(input_y
[j
]);
507 if (i
== 0 && j
== 0) {
508 // Inject the 1st event once a video packet has been received.
510 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
512 .WillOnce(InjectMouseEvent(connection_
, injected_event
));
514 // Every next event is injected once the previous event has been
517 EXPECT_CALL(*input_injector_
,
518 InjectMouseEvent(EqualsMouseEvent(expected_event
.x(),
519 expected_event
.y())))
521 .WillOnce(InjectMouseEvent(connection_
, injected_event
));
524 expected_event
.set_x(expected_x
[i
]);
525 expected_event
.set_y(expected_y
[j
]);
529 // Shutdown the connection once the last event has been received.
530 EXPECT_CALL(*input_injector_
,
531 InjectMouseEvent(EqualsMouseEvent(expected_event
.x(),
532 expected_event
.y())))
535 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
536 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
538 ConnectClientSession();
542 } // namespace remoting