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_region.h"
16 #include "third_party/webrtc/modules/desktop_capture/screen_capturer_mock_objects.h"
20 using protocol::MockConnectionToClient
;
21 using protocol::MockClientStub
;
22 using protocol::MockHostStub
;
23 using protocol::MockInputStub
;
24 using protocol::MockSession
;
25 using protocol::MockVideoStub
;
26 using protocol::SessionConfig
;
29 using testing::AnyNumber
;
30 using testing::AtMost
;
31 using testing::DeleteArg
;
33 using testing::Expectation
;
34 using testing::Return
;
35 using testing::ReturnRef
;
36 using testing::Sequence
;
40 ACTION_P2(InjectClipboardEvent
, connection
, event
) {
41 connection
->clipboard_stub()->InjectClipboardEvent(event
);
44 ACTION_P2(InjectKeyEvent
, connection
, event
) {
45 connection
->input_stub()->InjectKeyEvent(event
);
48 ACTION_P2(InjectMouseEvent
, connection
, event
) {
49 connection
->input_stub()->InjectMouseEvent(event
);
52 ACTION_P2(LocalMouseMoved
, client_session
, event
) {
53 client_session
->OnLocalMouseMoved(SkIPoint::Make(event
.x(), event
.y()));
58 class ClientSessionTest
: public testing::Test
{
60 ClientSessionTest() : client_jid_("user@domain/rest-of-jid") {}
62 virtual void SetUp() OVERRIDE
;
63 virtual void TearDown() OVERRIDE
;
65 // Disconnects the client session.
66 void DisconnectClientSession();
68 // Stops and releases the ClientSession, allowing the MessageLoop to quit.
69 void StopClientSession();
72 // Creates a DesktopEnvironment with a fake webrtc::ScreenCapturer, to mock
73 // DesktopEnvironmentFactory::Create().
74 DesktopEnvironment
* CreateDesktopEnvironment();
76 // Returns |input_injector_| created and initialized by SetUp(), to mock
77 // DesktopEnvironment::CreateInputInjector().
78 InputInjector
* CreateInputInjector();
80 // Creates a fake webrtc::ScreenCapturer, to mock
81 // DesktopEnvironment::CreateVideoCapturer().
82 webrtc::ScreenCapturer
* CreateVideoCapturer();
84 // Notifies the client session that the client connection has been
85 // authenticated and channels have been connected. This effectively enables
86 // the input pipe line and starts video capturing.
87 void ConnectClientSession();
89 // Invoked when the last reference to the AutoThreadTaskRunner has been
90 // released and quits the message loop to finish the test.
91 void QuitMainMessageLoop();
93 // Message loop passed to |client_session_| to perform all functions on.
94 base::MessageLoop message_loop_
;
96 // ClientSession instance under test.
97 scoped_ptr
<ClientSession
> client_session_
;
99 // ClientSession::EventHandler mock for use in tests.
100 MockClientSessionEventHandler session_event_handler_
;
102 // Storage for values to be returned by the protocol::Session mock.
103 SessionConfig session_config_
;
104 const std::string client_jid_
;
106 // Stubs returned to |client_session_| components by |connection_|.
107 MockClientStub client_stub_
;
108 MockVideoStub video_stub_
;
110 // DesktopEnvironment owns |input_injector_|, but input injection tests need
111 // to express expectations on it.
112 scoped_ptr
<MockInputInjector
> input_injector_
;
114 // ClientSession owns |connection_| but tests need it to inject fake events.
115 MockConnectionToClient
* connection_
;
117 scoped_ptr
<MockDesktopEnvironmentFactory
> desktop_environment_factory_
;
120 void ClientSessionTest::SetUp() {
121 // Arrange to run |message_loop_| until no components depend on it.
122 scoped_refptr
<AutoThreadTaskRunner
> ui_task_runner
= new AutoThreadTaskRunner(
123 message_loop_
.message_loop_proxy(),
124 base::Bind(&ClientSessionTest::QuitMainMessageLoop
,
125 base::Unretained(this)));
127 desktop_environment_factory_
.reset(new MockDesktopEnvironmentFactory());
128 EXPECT_CALL(*desktop_environment_factory_
, CreatePtr())
130 .WillRepeatedly(Invoke(this,
131 &ClientSessionTest::CreateDesktopEnvironment
));
132 EXPECT_CALL(*desktop_environment_factory_
, SupportsAudioCapture())
134 .WillRepeatedly(Return(false));
136 input_injector_
.reset(new MockInputInjector());
138 session_config_
= SessionConfig::ForTest();
140 // Mock protocol::Session APIs called directly by ClientSession.
141 protocol::MockSession
* session
= new MockSession();
142 EXPECT_CALL(*session
, config()).WillRepeatedly(ReturnRef(session_config_
));
143 EXPECT_CALL(*session
, jid()).WillRepeatedly(ReturnRef(client_jid_
));
144 EXPECT_CALL(*session
, SetEventHandler(_
));
146 // Mock protocol::ConnectionToClient APIs called directly by ClientSession.
147 // HostStub is not touched by ClientSession, so we can safely pass NULL.
148 scoped_ptr
<MockConnectionToClient
> connection(
149 new MockConnectionToClient(session
, NULL
));
150 EXPECT_CALL(*connection
, session()).WillRepeatedly(Return(session
));
151 EXPECT_CALL(*connection
, client_stub())
152 .WillRepeatedly(Return(&client_stub_
));
153 EXPECT_CALL(*connection
, video_stub()).WillRepeatedly(Return(&video_stub_
));
154 EXPECT_CALL(*connection
, Disconnect());
155 connection_
= connection
.get();
157 client_session_
.reset(new ClientSession(
158 &session_event_handler_
,
159 ui_task_runner
, // Audio thread.
160 ui_task_runner
, // Input thread.
161 ui_task_runner
, // Capture thread.
162 ui_task_runner
, // Encode thread.
163 ui_task_runner
, // Network thread.
164 ui_task_runner
, // UI thread.
165 connection
.PassAs
<protocol::ConnectionToClient
>(),
166 desktop_environment_factory_
.get(),
171 void ClientSessionTest::TearDown() {
172 // Verify that the client session has been stopped.
173 EXPECT_TRUE(!client_session_
);
176 void ClientSessionTest::DisconnectClientSession() {
177 client_session_
->DisconnectSession();
178 // MockSession won't trigger OnConnectionClosed, so fake it.
179 client_session_
->OnConnectionClosed(client_session_
->connection(),
183 void ClientSessionTest::StopClientSession() {
184 client_session_
.reset();
186 desktop_environment_factory_
.reset();
189 DesktopEnvironment
* ClientSessionTest::CreateDesktopEnvironment() {
190 MockDesktopEnvironment
* desktop_environment
= new MockDesktopEnvironment();
191 EXPECT_CALL(*desktop_environment
, CreateAudioCapturerPtr())
193 EXPECT_CALL(*desktop_environment
, CreateInputInjectorPtr())
194 .WillOnce(Invoke(this, &ClientSessionTest::CreateInputInjector
));
195 EXPECT_CALL(*desktop_environment
, CreateScreenControlsPtr())
197 EXPECT_CALL(*desktop_environment
, CreateVideoCapturerPtr())
198 .WillOnce(Invoke(this, &ClientSessionTest::CreateVideoCapturer
));
199 EXPECT_CALL(*desktop_environment
, GetCapabilities())
201 EXPECT_CALL(*desktop_environment
, SetCapabilities(_
))
204 return desktop_environment
;
207 InputInjector
* ClientSessionTest::CreateInputInjector() {
208 EXPECT_TRUE(input_injector_
);
209 return input_injector_
.release();
212 webrtc::ScreenCapturer
* ClientSessionTest::CreateVideoCapturer() {
213 return new ScreenCapturerFake();
216 void ClientSessionTest::ConnectClientSession() {
217 client_session_
->OnConnectionAuthenticated(client_session_
->connection());
218 client_session_
->OnConnectionChannelsConnected(client_session_
->connection());
221 void ClientSessionTest::QuitMainMessageLoop() {
222 message_loop_
.PostTask(FROM_HERE
, base::MessageLoop::QuitClosure());
225 MATCHER_P2(EqualsClipboardEvent
, m
, d
, "") {
226 return (strcmp(arg
.mime_type().c_str(), m
) == 0 &&
227 memcmp(arg
.data().data(), d
, arg
.data().size()) == 0);
230 TEST_F(ClientSessionTest
, ClipboardStubFilter
) {
231 protocol::ClipboardEvent clipboard_event1
;
232 clipboard_event1
.set_mime_type(kMimeTypeTextUtf8
);
233 clipboard_event1
.set_data("a");
235 protocol::ClipboardEvent clipboard_event2
;
236 clipboard_event2
.set_mime_type(kMimeTypeTextUtf8
);
237 clipboard_event2
.set_data("b");
239 protocol::ClipboardEvent clipboard_event3
;
240 clipboard_event3
.set_mime_type(kMimeTypeTextUtf8
);
241 clipboard_event3
.set_data("c");
243 Expectation authenticated
=
244 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
245 .WillOnce(Return(true));
246 EXPECT_CALL(*input_injector_
, StartPtr(_
))
247 .After(authenticated
);
248 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
249 .After(authenticated
);
251 // Wait for the first video packet to be captured to make sure that
252 // the injected input will go though. Otherwise mouse events will be blocked
253 // by the mouse clamping filter.
255 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
257 .After(authenticated
)
259 // This event should get through to the clipboard stub.
260 InjectClipboardEvent(connection_
, clipboard_event2
),
261 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
262 // This event should not get through to the clipboard stub,
263 // because the client has disconnected.
264 InjectClipboardEvent(connection_
, clipboard_event3
),
265 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
266 EXPECT_CALL(*input_injector_
, InjectClipboardEvent(EqualsClipboardEvent(
267 kMimeTypeTextUtf8
, "b")))
269 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
272 // This event should not get through to the clipboard stub,
273 // because the client isn't authenticated yet.
274 connection_
->clipboard_stub()->InjectClipboardEvent(clipboard_event1
);
276 ConnectClientSession();
282 MATCHER_P2(EqualsUsbEvent
, usb_keycode
, pressed
, "") {
283 return arg
.usb_keycode() == (unsigned int)usb_keycode
&&
284 arg
.pressed() == pressed
;
287 MATCHER_P2(EqualsMouseEvent
, x
, y
, "") {
288 return arg
.x() == x
&& arg
.y() == y
;
291 MATCHER_P2(EqualsMouseButtonEvent
, button
, down
, "") {
292 return arg
.button() == button
&& arg
.button_down() == down
;
297 TEST_F(ClientSessionTest
, InputStubFilter
) {
298 protocol::KeyEvent key_event1
;
299 key_event1
.set_pressed(true);
300 key_event1
.set_usb_keycode(1);
302 protocol::KeyEvent key_event2_down
;
303 key_event2_down
.set_pressed(true);
304 key_event2_down
.set_usb_keycode(2);
306 protocol::KeyEvent key_event2_up
;
307 key_event2_up
.set_pressed(false);
308 key_event2_up
.set_usb_keycode(2);
310 protocol::KeyEvent key_event3
;
311 key_event3
.set_pressed(true);
312 key_event3
.set_usb_keycode(3);
314 protocol::MouseEvent mouse_event1
;
315 mouse_event1
.set_x(100);
316 mouse_event1
.set_y(101);
318 protocol::MouseEvent mouse_event2
;
319 mouse_event2
.set_x(200);
320 mouse_event2
.set_y(201);
322 protocol::MouseEvent mouse_event3
;
323 mouse_event3
.set_x(300);
324 mouse_event3
.set_y(301);
326 Expectation authenticated
=
327 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
328 .WillOnce(Return(true));
329 EXPECT_CALL(*input_injector_
, StartPtr(_
))
330 .After(authenticated
);
331 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
332 .After(authenticated
);
334 // Wait for the first video packet to be captured to make sure that
335 // the injected input will go though. Otherwise mouse events will be blocked
336 // by the mouse clamping filter.
338 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
340 .After(authenticated
)
342 // These events should get through to the input stub.
343 InjectKeyEvent(connection_
, key_event2_down
),
344 InjectKeyEvent(connection_
, key_event2_up
),
345 InjectMouseEvent(connection_
, mouse_event2
),
346 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
347 // These events should not get through to the input stub,
348 // because the client has disconnected.
349 InjectKeyEvent(connection_
, key_event3
),
350 InjectMouseEvent(connection_
, mouse_event3
),
351 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
352 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(2, true)))
354 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(2, false)))
356 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseEvent(200, 201)))
358 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
361 // These events should not get through to the input stub,
362 // because the client isn't authenticated yet.
363 connection_
->input_stub()->InjectKeyEvent(key_event1
);
364 connection_
->input_stub()->InjectMouseEvent(mouse_event1
);
366 ConnectClientSession();
370 TEST_F(ClientSessionTest
, LocalInputTest
) {
371 protocol::MouseEvent mouse_event1
;
372 mouse_event1
.set_x(100);
373 mouse_event1
.set_y(101);
374 protocol::MouseEvent mouse_event2
;
375 mouse_event2
.set_x(200);
376 mouse_event2
.set_y(201);
377 protocol::MouseEvent mouse_event3
;
378 mouse_event3
.set_x(300);
379 mouse_event3
.set_y(301);
381 Expectation authenticated
=
382 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
383 .WillOnce(Return(true));
384 EXPECT_CALL(*input_injector_
, StartPtr(_
))
385 .After(authenticated
);
386 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
387 .After(authenticated
);
389 // Wait for the first video packet to be captured to make sure that
390 // the injected input will go though. Otherwise mouse events will be blocked
391 // by the mouse clamping filter.
393 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
395 .After(authenticated
)
397 // This event should get through to the input stub.
398 InjectMouseEvent(connection_
, mouse_event1
),
400 // The OS echoes the injected event back.
401 LocalMouseMoved(client_session_
.get(), mouse_event1
),
402 #endif // !defined(OS_WIN)
403 // This one should get throught as well.
404 InjectMouseEvent(connection_
, mouse_event2
),
405 // Now this is a genuine local event.
406 LocalMouseMoved(client_session_
.get(), mouse_event1
),
407 // This one should be blocked because of the previous local input
409 InjectMouseEvent(connection_
, mouse_event3
),
410 // TODO(jamiewalch): Verify that remote inputs are re-enabled
411 // eventually (via dependency injection, not sleep!)
412 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
413 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
414 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseEvent(100, 101)))
416 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseEvent(200, 201)))
418 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
421 ConnectClientSession();
425 TEST_F(ClientSessionTest
, RestoreEventState
) {
426 protocol::KeyEvent key1
;
427 key1
.set_pressed(true);
428 key1
.set_usb_keycode(1);
430 protocol::KeyEvent key2
;
431 key2
.set_pressed(true);
432 key2
.set_usb_keycode(2);
434 protocol::MouseEvent mousedown
;
435 mousedown
.set_button(protocol::MouseEvent::BUTTON_LEFT
);
436 mousedown
.set_button_down(true);
438 Expectation authenticated
=
439 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
440 .WillOnce(Return(true));
441 EXPECT_CALL(*input_injector_
, StartPtr(_
))
442 .After(authenticated
);
443 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
444 .After(authenticated
);
446 // Wait for the first video packet to be captured to make sure that
447 // the injected input will go though. Otherwise mouse events will be blocked
448 // by the mouse clamping filter.
450 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
452 .After(authenticated
)
454 InjectKeyEvent(connection_
, key1
),
455 InjectKeyEvent(connection_
, key2
),
456 InjectMouseEvent(connection_
, mousedown
),
457 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
458 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
459 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(1, true)))
461 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(2, true)))
463 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseButtonEvent(
464 protocol::MouseEvent::BUTTON_LEFT
, true)))
466 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(1, false)))
468 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(2, false)))
470 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseButtonEvent(
471 protocol::MouseEvent::BUTTON_LEFT
, false)))
473 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
476 ConnectClientSession();
480 TEST_F(ClientSessionTest
, ClampMouseEvents
) {
481 Expectation authenticated
=
482 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
483 .WillOnce(Return(true));
484 EXPECT_CALL(*input_injector_
, StartPtr(_
))
485 .After(authenticated
);
486 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
487 .After(authenticated
);
488 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
489 .After(authenticated
);
491 Expectation connected
= authenticated
;
493 int input_x
[3] = { -999, 100, 999 };
494 int expected_x
[3] = { 0, 100, ScreenCapturerFake::kWidth
- 1 };
495 int input_y
[3] = { -999, 50, 999 };
496 int expected_y
[3] = { 0, 50, ScreenCapturerFake::kHeight
- 1 };
498 protocol::MouseEvent expected_event
;
499 for (int j
= 0; j
< 3; j
++) {
500 for (int i
= 0; i
< 3; i
++) {
501 protocol::MouseEvent injected_event
;
502 injected_event
.set_x(input_x
[i
]);
503 injected_event
.set_y(input_y
[j
]);
505 if (i
== 0 && j
== 0) {
506 // Inject the 1st event once a video packet has been received.
508 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
510 .WillOnce(InjectMouseEvent(connection_
, injected_event
));
512 // Every next event is injected once the previous event has been
515 EXPECT_CALL(*input_injector_
,
516 InjectMouseEvent(EqualsMouseEvent(expected_event
.x(),
517 expected_event
.y())))
519 .WillOnce(InjectMouseEvent(connection_
, injected_event
));
522 expected_event
.set_x(expected_x
[i
]);
523 expected_event
.set_y(expected_y
[j
]);
527 // Shutdown the connection once the last event has been received.
528 EXPECT_CALL(*input_injector_
,
529 InjectMouseEvent(EqualsMouseEvent(expected_event
.x(),
530 expected_event
.y())))
533 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
534 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
536 ConnectClientSession();
540 } // namespace remoting