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 "base/test/test_simple_task_runner.h"
7 #include "remoting/base/auto_thread_task_runner.h"
8 #include "remoting/base/constants.h"
9 #include "remoting/host/audio_capturer.h"
10 #include "remoting/host/client_session.h"
11 #include "remoting/host/desktop_environment.h"
12 #include "remoting/host/host_mock_objects.h"
13 #include "remoting/host/screen_capturer_fake.h"
14 #include "remoting/protocol/protocol_mock_objects.h"
15 #include "testing/gmock/include/gmock/gmock-matchers.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
18 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
19 #include "third_party/webrtc/modules/desktop_capture/screen_capturer_mock_objects.h"
23 using protocol::MockConnectionToClient
;
24 using protocol::MockClientStub
;
25 using protocol::MockHostStub
;
26 using protocol::MockInputStub
;
27 using protocol::MockSession
;
28 using protocol::MockVideoStub
;
29 using protocol::SessionConfig
;
32 using testing::AnyNumber
;
33 using testing::AtMost
;
34 using testing::DeleteArg
;
36 using testing::Expectation
;
37 using testing::Return
;
38 using testing::ReturnRef
;
39 using testing::Sequence
;
41 using testing::StrictMock
;
45 ACTION_P2(InjectClipboardEvent
, connection
, event
) {
46 connection
->clipboard_stub()->InjectClipboardEvent(event
);
49 ACTION_P2(InjectKeyEvent
, connection
, event
) {
50 connection
->input_stub()->InjectKeyEvent(event
);
53 ACTION_P2(InjectMouseEvent
, connection
, event
) {
54 connection
->input_stub()->InjectMouseEvent(event
);
57 ACTION_P2(LocalMouseMoved
, client_session
, event
) {
58 client_session
->OnLocalMouseMoved(
59 webrtc::DesktopVector(event
.x(), event
.y()));
62 ACTION_P2(SetGnubbyAuthHandlerForTesting
, client_session
, gnubby_auth_handler
) {
63 client_session
->SetGnubbyAuthHandlerForTesting(gnubby_auth_handler
);
66 ACTION_P2(DeliverClientMessage
, client_session
, message
) {
67 client_session
->DeliverClientMessage(message
);
72 class ClientSessionTest
: public testing::Test
{
74 ClientSessionTest() : client_jid_("user@domain/rest-of-jid") {}
76 virtual void SetUp() OVERRIDE
;
77 virtual void TearDown() OVERRIDE
;
79 // Disconnects the client session.
80 void DisconnectClientSession();
82 // Stops and releases the ClientSession, allowing the MessageLoop to quit.
83 void StopClientSession();
86 // Creates a DesktopEnvironment with a fake webrtc::ScreenCapturer, to mock
87 // DesktopEnvironmentFactory::Create().
88 DesktopEnvironment
* CreateDesktopEnvironment();
90 // Returns |input_injector_| created and initialized by SetUp(), to mock
91 // DesktopEnvironment::CreateInputInjector().
92 InputInjector
* CreateInputInjector();
94 // Creates a fake webrtc::ScreenCapturer, to mock
95 // DesktopEnvironment::CreateVideoCapturer().
96 webrtc::ScreenCapturer
* CreateVideoCapturer();
98 // Notifies the client session that the client connection has been
99 // authenticated and channels have been connected. This effectively enables
100 // the input pipe line and starts video capturing.
101 void ConnectClientSession();
103 // Invoked when the last reference to the AutoThreadTaskRunner has been
104 // released and quits the message loop to finish the test.
105 void QuitMainMessageLoop();
107 // Message loop passed to |client_session_| to perform all functions on.
108 base::MessageLoop message_loop_
;
110 // ClientSession instance under test.
111 scoped_ptr
<ClientSession
> client_session_
;
113 // ClientSession::EventHandler mock for use in tests.
114 MockClientSessionEventHandler session_event_handler_
;
116 // Storage for values to be returned by the protocol::Session mock.
117 SessionConfig session_config_
;
118 const std::string client_jid_
;
120 // Stubs returned to |client_session_| components by |connection_|.
121 MockClientStub client_stub_
;
122 MockVideoStub video_stub_
;
124 // DesktopEnvironment owns |input_injector_|, but input injection tests need
125 // to express expectations on it.
126 scoped_ptr
<MockInputInjector
> input_injector_
;
128 // ClientSession owns |connection_| but tests need it to inject fake events.
129 MockConnectionToClient
* connection_
;
131 scoped_ptr
<MockDesktopEnvironmentFactory
> desktop_environment_factory_
;
134 void ClientSessionTest::SetUp() {
135 // Arrange to run |message_loop_| until no components depend on it.
136 scoped_refptr
<AutoThreadTaskRunner
> ui_task_runner
= new AutoThreadTaskRunner(
137 message_loop_
.message_loop_proxy(),
138 base::Bind(&ClientSessionTest::QuitMainMessageLoop
,
139 base::Unretained(this)));
141 desktop_environment_factory_
.reset(new MockDesktopEnvironmentFactory());
142 EXPECT_CALL(*desktop_environment_factory_
, CreatePtr())
144 .WillRepeatedly(Invoke(this,
145 &ClientSessionTest::CreateDesktopEnvironment
));
146 EXPECT_CALL(*desktop_environment_factory_
, SupportsAudioCapture())
148 .WillRepeatedly(Return(false));
150 input_injector_
.reset(new MockInputInjector());
152 session_config_
= SessionConfig::ForTest();
154 // Mock protocol::Session APIs called directly by ClientSession.
155 protocol::MockSession
* session
= new MockSession();
156 EXPECT_CALL(*session
, config()).WillRepeatedly(ReturnRef(session_config_
));
157 EXPECT_CALL(*session
, jid()).WillRepeatedly(ReturnRef(client_jid_
));
158 EXPECT_CALL(*session
, SetEventHandler(_
));
160 // Mock protocol::ConnectionToClient APIs called directly by ClientSession.
161 // HostStub is not touched by ClientSession, so we can safely pass NULL.
162 scoped_ptr
<MockConnectionToClient
> connection(
163 new MockConnectionToClient(session
, NULL
));
164 EXPECT_CALL(*connection
, session()).WillRepeatedly(Return(session
));
165 EXPECT_CALL(*connection
, client_stub())
166 .WillRepeatedly(Return(&client_stub_
));
167 EXPECT_CALL(*connection
, video_stub()).WillRepeatedly(Return(&video_stub_
));
168 EXPECT_CALL(*connection
, Disconnect());
169 connection_
= connection
.get();
171 client_session_
.reset(new ClientSession(
172 &session_event_handler_
,
173 ui_task_runner
, // Audio thread.
174 ui_task_runner
, // Input thread.
175 ui_task_runner
, // Capture thread.
176 ui_task_runner
, // Encode thread.
177 ui_task_runner
, // Network thread.
178 ui_task_runner
, // UI thread.
179 connection
.PassAs
<protocol::ConnectionToClient
>(),
180 desktop_environment_factory_
.get(),
185 void ClientSessionTest::TearDown() {
186 // Verify that the client session has been stopped.
187 EXPECT_TRUE(!client_session_
);
190 void ClientSessionTest::DisconnectClientSession() {
191 client_session_
->DisconnectSession();
192 // MockSession won't trigger OnConnectionClosed, so fake it.
193 client_session_
->OnConnectionClosed(client_session_
->connection(),
197 void ClientSessionTest::StopClientSession() {
198 client_session_
.reset();
200 desktop_environment_factory_
.reset();
203 DesktopEnvironment
* ClientSessionTest::CreateDesktopEnvironment() {
204 MockDesktopEnvironment
* desktop_environment
= new MockDesktopEnvironment();
205 EXPECT_CALL(*desktop_environment
, CreateAudioCapturerPtr())
207 EXPECT_CALL(*desktop_environment
, CreateInputInjectorPtr())
208 .WillOnce(Invoke(this, &ClientSessionTest::CreateInputInjector
));
209 EXPECT_CALL(*desktop_environment
, CreateScreenControlsPtr())
211 EXPECT_CALL(*desktop_environment
, CreateVideoCapturerPtr())
212 .WillOnce(Invoke(this, &ClientSessionTest::CreateVideoCapturer
));
213 EXPECT_CALL(*desktop_environment
, GetCapabilities())
215 EXPECT_CALL(*desktop_environment
, SetCapabilities(_
))
218 return desktop_environment
;
221 InputInjector
* ClientSessionTest::CreateInputInjector() {
222 EXPECT_TRUE(input_injector_
);
223 return input_injector_
.release();
226 webrtc::ScreenCapturer
* ClientSessionTest::CreateVideoCapturer() {
227 return new ScreenCapturerFake();
230 void ClientSessionTest::ConnectClientSession() {
231 client_session_
->OnConnectionAuthenticated(client_session_
->connection());
232 client_session_
->OnConnectionChannelsConnected(client_session_
->connection());
235 void ClientSessionTest::QuitMainMessageLoop() {
236 message_loop_
.PostTask(FROM_HERE
, base::MessageLoop::QuitClosure());
239 MATCHER_P2(EqualsClipboardEvent
, m
, d
, "") {
240 return (strcmp(arg
.mime_type().c_str(), m
) == 0 &&
241 memcmp(arg
.data().data(), d
, arg
.data().size()) == 0);
244 TEST_F(ClientSessionTest
, ClipboardStubFilter
) {
245 protocol::ClipboardEvent clipboard_event1
;
246 clipboard_event1
.set_mime_type(kMimeTypeTextUtf8
);
247 clipboard_event1
.set_data("a");
249 protocol::ClipboardEvent clipboard_event2
;
250 clipboard_event2
.set_mime_type(kMimeTypeTextUtf8
);
251 clipboard_event2
.set_data("b");
253 protocol::ClipboardEvent clipboard_event3
;
254 clipboard_event3
.set_mime_type(kMimeTypeTextUtf8
);
255 clipboard_event3
.set_data("c");
257 Expectation authenticated
=
258 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
259 .WillOnce(Return(true));
260 EXPECT_CALL(*input_injector_
, StartPtr(_
))
261 .After(authenticated
);
262 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
263 .After(authenticated
);
265 // Wait for the first video packet to be captured to make sure that
266 // the injected input will go though. Otherwise mouse events will be blocked
267 // by the mouse clamping filter.
269 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
271 .After(authenticated
)
273 // This event should get through to the clipboard stub.
274 InjectClipboardEvent(connection_
, clipboard_event2
),
275 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
276 // This event should not get through to the clipboard stub,
277 // because the client has disconnected.
278 InjectClipboardEvent(connection_
, clipboard_event3
),
279 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
280 EXPECT_CALL(*input_injector_
, InjectClipboardEvent(EqualsClipboardEvent(
281 kMimeTypeTextUtf8
, "b")))
283 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
286 // This event should not get through to the clipboard stub,
287 // because the client isn't authenticated yet.
288 connection_
->clipboard_stub()->InjectClipboardEvent(clipboard_event1
);
290 ConnectClientSession();
296 MATCHER_P2(EqualsUsbEvent
, usb_keycode
, pressed
, "") {
297 return arg
.usb_keycode() == (unsigned int)usb_keycode
&&
298 arg
.pressed() == pressed
;
301 MATCHER_P2(EqualsMouseEvent
, x
, y
, "") {
302 return arg
.x() == x
&& arg
.y() == y
;
305 MATCHER_P2(EqualsMouseButtonEvent
, button
, down
, "") {
306 return arg
.button() == button
&& arg
.button_down() == down
;
311 TEST_F(ClientSessionTest
, InputStubFilter
) {
312 protocol::KeyEvent key_event1
;
313 key_event1
.set_pressed(true);
314 key_event1
.set_usb_keycode(1);
316 protocol::KeyEvent key_event2_down
;
317 key_event2_down
.set_pressed(true);
318 key_event2_down
.set_usb_keycode(2);
320 protocol::KeyEvent key_event2_up
;
321 key_event2_up
.set_pressed(false);
322 key_event2_up
.set_usb_keycode(2);
324 protocol::KeyEvent key_event3
;
325 key_event3
.set_pressed(true);
326 key_event3
.set_usb_keycode(3);
328 protocol::MouseEvent mouse_event1
;
329 mouse_event1
.set_x(100);
330 mouse_event1
.set_y(101);
332 protocol::MouseEvent mouse_event2
;
333 mouse_event2
.set_x(200);
334 mouse_event2
.set_y(201);
336 protocol::MouseEvent mouse_event3
;
337 mouse_event3
.set_x(300);
338 mouse_event3
.set_y(301);
340 Expectation authenticated
=
341 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
342 .WillOnce(Return(true));
343 EXPECT_CALL(*input_injector_
, StartPtr(_
))
344 .After(authenticated
);
345 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
346 .After(authenticated
);
348 // Wait for the first video packet to be captured to make sure that
349 // the injected input will go though. Otherwise mouse events will be blocked
350 // by the mouse clamping filter.
352 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
354 .After(authenticated
)
356 // These events should get through to the input stub.
357 InjectKeyEvent(connection_
, key_event2_down
),
358 InjectKeyEvent(connection_
, key_event2_up
),
359 InjectMouseEvent(connection_
, mouse_event2
),
360 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
361 // These events should not get through to the input stub,
362 // because the client has disconnected.
363 InjectKeyEvent(connection_
, key_event3
),
364 InjectMouseEvent(connection_
, mouse_event3
),
365 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
366 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(2, true)))
368 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(2, false)))
370 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseEvent(200, 201)))
372 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
375 // These events should not get through to the input stub,
376 // because the client isn't authenticated yet.
377 connection_
->input_stub()->InjectKeyEvent(key_event1
);
378 connection_
->input_stub()->InjectMouseEvent(mouse_event1
);
380 ConnectClientSession();
384 TEST_F(ClientSessionTest
, LocalInputTest
) {
385 protocol::MouseEvent mouse_event1
;
386 mouse_event1
.set_x(100);
387 mouse_event1
.set_y(101);
388 protocol::MouseEvent mouse_event2
;
389 mouse_event2
.set_x(200);
390 mouse_event2
.set_y(201);
391 protocol::MouseEvent mouse_event3
;
392 mouse_event3
.set_x(300);
393 mouse_event3
.set_y(301);
395 Expectation authenticated
=
396 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
397 .WillOnce(Return(true));
398 EXPECT_CALL(*input_injector_
, StartPtr(_
))
399 .After(authenticated
);
400 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
401 .After(authenticated
);
403 // Wait for the first video packet to be captured to make sure that
404 // the injected input will go though. Otherwise mouse events will be blocked
405 // by the mouse clamping filter.
407 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
409 .After(authenticated
)
411 // This event should get through to the input stub.
412 InjectMouseEvent(connection_
, mouse_event1
),
414 // The OS echoes the injected event back.
415 LocalMouseMoved(client_session_
.get(), mouse_event1
),
416 #endif // !defined(OS_WIN)
417 // This one should get throught as well.
418 InjectMouseEvent(connection_
, mouse_event2
),
419 // Now this is a genuine local event.
420 LocalMouseMoved(client_session_
.get(), mouse_event1
),
421 // This one should be blocked because of the previous local input
423 InjectMouseEvent(connection_
, mouse_event3
),
424 // TODO(jamiewalch): Verify that remote inputs are re-enabled
425 // eventually (via dependency injection, not sleep!)
426 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
427 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
428 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseEvent(100, 101)))
430 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseEvent(200, 201)))
432 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
435 ConnectClientSession();
439 TEST_F(ClientSessionTest
, RestoreEventState
) {
440 protocol::KeyEvent key1
;
441 key1
.set_pressed(true);
442 key1
.set_usb_keycode(1);
444 protocol::KeyEvent key2
;
445 key2
.set_pressed(true);
446 key2
.set_usb_keycode(2);
448 protocol::MouseEvent mousedown
;
449 mousedown
.set_button(protocol::MouseEvent::BUTTON_LEFT
);
450 mousedown
.set_button_down(true);
452 Expectation authenticated
=
453 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
454 .WillOnce(Return(true));
455 EXPECT_CALL(*input_injector_
, StartPtr(_
))
456 .After(authenticated
);
457 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
458 .After(authenticated
);
460 // Wait for the first video packet to be captured to make sure that
461 // the injected input will go though. Otherwise mouse events will be blocked
462 // by the mouse clamping filter.
464 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
466 .After(authenticated
)
468 InjectKeyEvent(connection_
, key1
),
469 InjectKeyEvent(connection_
, key2
),
470 InjectMouseEvent(connection_
, mousedown
),
471 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
472 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
473 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(1, true)))
475 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(2, true)))
477 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseButtonEvent(
478 protocol::MouseEvent::BUTTON_LEFT
, true)))
480 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(1, false)))
482 EXPECT_CALL(*input_injector_
, InjectKeyEvent(EqualsUsbEvent(2, false)))
484 EXPECT_CALL(*input_injector_
, InjectMouseEvent(EqualsMouseButtonEvent(
485 protocol::MouseEvent::BUTTON_LEFT
, false)))
487 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
490 ConnectClientSession();
494 TEST_F(ClientSessionTest
, ClampMouseEvents
) {
495 Expectation authenticated
=
496 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
497 .WillOnce(Return(true));
498 EXPECT_CALL(*input_injector_
, StartPtr(_
))
499 .After(authenticated
);
500 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
501 .After(authenticated
);
502 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
))
503 .After(authenticated
);
505 Expectation connected
= authenticated
;
507 int input_x
[3] = { -999, 100, 999 };
508 int expected_x
[3] = { 0, 100, ScreenCapturerFake::kWidth
- 1 };
509 int input_y
[3] = { -999, 50, 999 };
510 int expected_y
[3] = { 0, 50, ScreenCapturerFake::kHeight
- 1 };
512 protocol::MouseEvent expected_event
;
513 for (int j
= 0; j
< 3; j
++) {
514 for (int i
= 0; i
< 3; i
++) {
515 protocol::MouseEvent injected_event
;
516 injected_event
.set_x(input_x
[i
]);
517 injected_event
.set_y(input_y
[j
]);
519 if (i
== 0 && j
== 0) {
520 // Inject the 1st event once a video packet has been received.
522 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
524 .WillOnce(InjectMouseEvent(connection_
, injected_event
));
526 // Every next event is injected once the previous event has been
529 EXPECT_CALL(*input_injector_
,
530 InjectMouseEvent(EqualsMouseEvent(expected_event
.x(),
531 expected_event
.y())))
533 .WillOnce(InjectMouseEvent(connection_
, injected_event
));
536 expected_event
.set_x(expected_x
[i
]);
537 expected_event
.set_y(expected_y
[j
]);
541 // Shutdown the connection once the last event has been received.
542 EXPECT_CALL(*input_injector_
,
543 InjectMouseEvent(EqualsMouseEvent(expected_event
.x(),
544 expected_event
.y())))
547 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
548 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
550 ConnectClientSession();
554 TEST_F(ClientSessionTest
, NoGnubbyAuth
) {
555 protocol::ExtensionMessage message
;
556 message
.set_type("gnubby-auth");
557 message
.set_data("test");
559 Expectation authenticated
=
560 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
561 .WillOnce(Return(true));
562 EXPECT_CALL(*input_injector_
, StartPtr(_
)).After(authenticated
);
563 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
564 .After(authenticated
)
566 DeliverClientMessage(client_session_
.get(), message
),
567 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
568 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
569 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
));
571 ConnectClientSession();
575 TEST_F(ClientSessionTest
, EnableGnubbyAuth
) {
576 // Lifetime controlled by object under test.
577 MockGnubbyAuthHandler
* gnubby_auth_handler
= new MockGnubbyAuthHandler();
579 protocol::ExtensionMessage message
;
580 message
.set_type("gnubby-auth");
581 message
.set_data("test");
583 Expectation authenticated
=
584 EXPECT_CALL(session_event_handler_
, OnSessionAuthenticated(_
))
585 .WillOnce(Return(true));
586 EXPECT_CALL(*input_injector_
, StartPtr(_
)).After(authenticated
);
587 EXPECT_CALL(session_event_handler_
, OnSessionChannelsConnected(_
))
588 .After(authenticated
)
590 SetGnubbyAuthHandlerForTesting(client_session_
.get(),
591 gnubby_auth_handler
),
592 DeliverClientMessage(client_session_
.get(), message
),
593 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession
),
594 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession
)));
595 EXPECT_CALL(*gnubby_auth_handler
, DeliverClientMessage(_
));
596 EXPECT_CALL(session_event_handler_
, OnSessionClosed(_
));
598 ConnectClientSession();
602 } // namespace remoting