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 "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" 18 19 namespace remoting { 20 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; 28 29 using testing::_; 30 using testing::AnyNumber; 31 using testing::AtMost; 32 using testing::DeleteArg; 33 using testing::DoAll; 34 using testing::Expectation; 35 using testing::Return; 36 using testing::ReturnRef; 37 using testing::Sequence; 38 39 namespace { 40 41 ACTION_P2(InjectClipboardEvent, connection, event) { 42 connection->clipboard_stub()->InjectClipboardEvent(event); 43 } 44 45 ACTION_P2(InjectKeyEvent, connection, event) { 46 connection->input_stub()->InjectKeyEvent(event); 47 } 48 49 ACTION_P2(InjectMouseEvent, connection, event) { 50 connection->input_stub()->InjectMouseEvent(event); 51 } 52 53 ACTION_P2(LocalMouseMoved, client_session, event) { 54 client_session->OnLocalMouseMoved( 55 webrtc::DesktopVector(event.x(), event.y())); 56 } 57 58 } // namespace 59 60 class ClientSessionTest : public testing::Test { 61 public: 62 ClientSessionTest() : client_jid_("user@domain/rest-of-jid") {} 63 64 virtual void SetUp() OVERRIDE; 65 virtual void TearDown() OVERRIDE; 66 67 // Disconnects the client session. 68 void DisconnectClientSession(); 69 70 // Stops and releases the ClientSession, allowing the MessageLoop to quit. 71 void StopClientSession(); 72 73 protected: 74 // Creates a DesktopEnvironment with a fake webrtc::ScreenCapturer, to mock 75 // DesktopEnvironmentFactory::Create(). 76 DesktopEnvironment* CreateDesktopEnvironment(); 77 78 // Returns |input_injector_| created and initialized by SetUp(), to mock 79 // DesktopEnvironment::CreateInputInjector(). 80 InputInjector* CreateInputInjector(); 81 82 // Creates a fake webrtc::ScreenCapturer, to mock 83 // DesktopEnvironment::CreateVideoCapturer(). 84 webrtc::ScreenCapturer* CreateVideoCapturer(); 85 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(); 90 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(); 94 95 // Message loop passed to |client_session_| to perform all functions on. 96 base::MessageLoop message_loop_; 97 98 // ClientSession instance under test. 99 scoped_ptr<ClientSession> client_session_; 100 101 // ClientSession::EventHandler mock for use in tests. 102 MockClientSessionEventHandler session_event_handler_; 103 104 // Storage for values to be returned by the protocol::Session mock. 105 SessionConfig session_config_; 106 const std::string client_jid_; 107 108 // Stubs returned to |client_session_| components by |connection_|. 109 MockClientStub client_stub_; 110 MockVideoStub video_stub_; 111 112 // DesktopEnvironment owns |input_injector_|, but input injection tests need 113 // to express expectations on it. 114 scoped_ptr<MockInputInjector> input_injector_; 115 116 // ClientSession owns |connection_| but tests need it to inject fake events. 117 MockConnectionToClient* connection_; 118 119 scoped_ptr<MockDesktopEnvironmentFactory> desktop_environment_factory_; 120 }; 121 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))); 128 129 desktop_environment_factory_.reset(new MockDesktopEnvironmentFactory()); 130 EXPECT_CALL(*desktop_environment_factory_, CreatePtr()) 131 .Times(AnyNumber()) 132 .WillRepeatedly(Invoke(this, 133 &ClientSessionTest::CreateDesktopEnvironment)); 134 EXPECT_CALL(*desktop_environment_factory_, SupportsAudioCapture()) 135 .Times(AnyNumber()) 136 .WillRepeatedly(Return(false)); 137 138 input_injector_.reset(new MockInputInjector()); 139 140 session_config_ = SessionConfig::ForTest(); 141 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(_)); 147 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(); 158 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(), 169 base::TimeDelta(), 170 NULL)); 171 } 172 173 void ClientSessionTest::TearDown() { 174 // Verify that the client session has been stopped. 175 EXPECT_TRUE(!client_session_); 176 } 177 178 void ClientSessionTest::DisconnectClientSession() { 179 client_session_->DisconnectSession(); 180 // MockSession won't trigger OnConnectionClosed, so fake it. 181 client_session_->OnConnectionClosed(client_session_->connection(), 182 protocol::OK); 183 } 184 185 void ClientSessionTest::StopClientSession() { 186 client_session_.reset(); 187 188 desktop_environment_factory_.reset(); 189 } 190 191 DesktopEnvironment* ClientSessionTest::CreateDesktopEnvironment() { 192 MockDesktopEnvironment* desktop_environment = new MockDesktopEnvironment(); 193 EXPECT_CALL(*desktop_environment, CreateAudioCapturerPtr()) 194 .Times(0); 195 EXPECT_CALL(*desktop_environment, CreateInputInjectorPtr()) 196 .WillOnce(Invoke(this, &ClientSessionTest::CreateInputInjector)); 197 EXPECT_CALL(*desktop_environment, CreateScreenControlsPtr()) 198 .Times(AtMost(1)); 199 EXPECT_CALL(*desktop_environment, CreateVideoCapturerPtr()) 200 .WillOnce(Invoke(this, &ClientSessionTest::CreateVideoCapturer)); 201 EXPECT_CALL(*desktop_environment, GetCapabilities()) 202 .Times(AtMost(1)); 203 EXPECT_CALL(*desktop_environment, SetCapabilities(_)) 204 .Times(AtMost(1)); 205 206 return desktop_environment; 207 } 208 209 InputInjector* ClientSessionTest::CreateInputInjector() { 210 EXPECT_TRUE(input_injector_); 211 return input_injector_.release(); 212 } 213 214 webrtc::ScreenCapturer* ClientSessionTest::CreateVideoCapturer() { 215 return new ScreenCapturerFake(); 216 } 217 218 void ClientSessionTest::ConnectClientSession() { 219 client_session_->OnConnectionAuthenticated(client_session_->connection()); 220 client_session_->OnConnectionChannelsConnected(client_session_->connection()); 221 } 222 223 void ClientSessionTest::QuitMainMessageLoop() { 224 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 225 } 226 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); 230 } 231 232 TEST_F(ClientSessionTest, ClipboardStubFilter) { 233 protocol::ClipboardEvent clipboard_event1; 234 clipboard_event1.set_mime_type(kMimeTypeTextUtf8); 235 clipboard_event1.set_data("a"); 236 237 protocol::ClipboardEvent clipboard_event2; 238 clipboard_event2.set_mime_type(kMimeTypeTextUtf8); 239 clipboard_event2.set_data("b"); 240 241 protocol::ClipboardEvent clipboard_event3; 242 clipboard_event3.set_mime_type(kMimeTypeTextUtf8); 243 clipboard_event3.set_data("c"); 244 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); 252 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. 256 Sequence s; 257 EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _)) 258 .InSequence(s) 259 .After(authenticated) 260 .WillOnce(DoAll( 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"))) 270 .InSequence(s); 271 EXPECT_CALL(session_event_handler_, OnSessionClosed(_)) 272 .InSequence(s); 273 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); 277 278 ConnectClientSession(); 279 message_loop_.Run(); 280 } 281 282 namespace { 283 284 MATCHER_P2(EqualsUsbEvent, usb_keycode, pressed, "") { 285 return arg.usb_keycode() == (unsigned int)usb_keycode && 286 arg.pressed() == pressed; 287 } 288 289 MATCHER_P2(EqualsMouseEvent, x, y, "") { 290 return arg.x() == x && arg.y() == y; 291 } 292 293 MATCHER_P2(EqualsMouseButtonEvent, button, down, "") { 294 return arg.button() == button && arg.button_down() == down; 295 } 296 297 } 298 299 TEST_F(ClientSessionTest, InputStubFilter) { 300 protocol::KeyEvent key_event1; 301 key_event1.set_pressed(true); 302 key_event1.set_usb_keycode(1); 303 304 protocol::KeyEvent key_event2_down; 305 key_event2_down.set_pressed(true); 306 key_event2_down.set_usb_keycode(2); 307 308 protocol::KeyEvent key_event2_up; 309 key_event2_up.set_pressed(false); 310 key_event2_up.set_usb_keycode(2); 311 312 protocol::KeyEvent key_event3; 313 key_event3.set_pressed(true); 314 key_event3.set_usb_keycode(3); 315 316 protocol::MouseEvent mouse_event1; 317 mouse_event1.set_x(100); 318 mouse_event1.set_y(101); 319 320 protocol::MouseEvent mouse_event2; 321 mouse_event2.set_x(200); 322 mouse_event2.set_y(201); 323 324 protocol::MouseEvent mouse_event3; 325 mouse_event3.set_x(300); 326 mouse_event3.set_y(301); 327 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); 335 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. 339 Sequence s; 340 EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _)) 341 .InSequence(s) 342 .After(authenticated) 343 .WillOnce(DoAll( 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))) 355 .InSequence(s); 356 EXPECT_CALL(*input_injector_, InjectKeyEvent(EqualsUsbEvent(2, false))) 357 .InSequence(s); 358 EXPECT_CALL(*input_injector_, InjectMouseEvent(EqualsMouseEvent(200, 201))) 359 .InSequence(s); 360 EXPECT_CALL(session_event_handler_, OnSessionClosed(_)) 361 .InSequence(s); 362 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); 367 368 ConnectClientSession(); 369 message_loop_.Run(); 370 } 371 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); 382 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); 390 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. 394 Sequence s; 395 EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _)) 396 .InSequence(s) 397 .After(authenticated) 398 .WillOnce(DoAll( 399 // This event should get through to the input stub. 400 InjectMouseEvent(connection_, mouse_event1), 401 #if !defined(OS_WIN) 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 410 // event. 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))) 417 .InSequence(s); 418 EXPECT_CALL(*input_injector_, InjectMouseEvent(EqualsMouseEvent(200, 201))) 419 .InSequence(s); 420 EXPECT_CALL(session_event_handler_, OnSessionClosed(_)) 421 .InSequence(s); 422 423 ConnectClientSession(); 424 message_loop_.Run(); 425 } 426 427 TEST_F(ClientSessionTest, RestoreEventState) { 428 protocol::KeyEvent key1; 429 key1.set_pressed(true); 430 key1.set_usb_keycode(1); 431 432 protocol::KeyEvent key2; 433 key2.set_pressed(true); 434 key2.set_usb_keycode(2); 435 436 protocol::MouseEvent mousedown; 437 mousedown.set_button(protocol::MouseEvent::BUTTON_LEFT); 438 mousedown.set_button_down(true); 439 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); 447 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. 451 Sequence s; 452 EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _)) 453 .InSequence(s) 454 .After(authenticated) 455 .WillOnce(DoAll( 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))) 462 .InSequence(s); 463 EXPECT_CALL(*input_injector_, InjectKeyEvent(EqualsUsbEvent(2, true))) 464 .InSequence(s); 465 EXPECT_CALL(*input_injector_, InjectMouseEvent(EqualsMouseButtonEvent( 466 protocol::MouseEvent::BUTTON_LEFT, true))) 467 .InSequence(s); 468 EXPECT_CALL(*input_injector_, InjectKeyEvent(EqualsUsbEvent(1, false))) 469 .InSequence(s); 470 EXPECT_CALL(*input_injector_, InjectKeyEvent(EqualsUsbEvent(2, false))) 471 .InSequence(s); 472 EXPECT_CALL(*input_injector_, InjectMouseEvent(EqualsMouseButtonEvent( 473 protocol::MouseEvent::BUTTON_LEFT, false))) 474 .InSequence(s); 475 EXPECT_CALL(session_event_handler_, OnSessionClosed(_)) 476 .InSequence(s); 477 478 ConnectClientSession(); 479 message_loop_.Run(); 480 } 481 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); 492 493 Expectation connected = authenticated; 494 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 }; 499 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]); 506 507 if (i == 0 && j == 0) { 508 // Inject the 1st event once a video packet has been received. 509 connected = 510 EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _)) 511 .After(connected) 512 .WillOnce(InjectMouseEvent(connection_, injected_event)); 513 } else { 514 // Every next event is injected once the previous event has been 515 // received. 516 connected = 517 EXPECT_CALL(*input_injector_, 518 InjectMouseEvent(EqualsMouseEvent(expected_event.x(), 519 expected_event.y()))) 520 .After(connected) 521 .WillOnce(InjectMouseEvent(connection_, injected_event)); 522 } 523 524 expected_event.set_x(expected_x[i]); 525 expected_event.set_y(expected_y[j]); 526 } 527 } 528 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()))) 533 .After(connected) 534 .WillOnce(DoAll( 535 InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession), 536 InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession))); 537 538 ConnectClientSession(); 539 message_loop_.Run(); 540 } 541 542 } // namespace remoting 543