// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/process/process.h" #include "base/process/process_handle.h" #include "base/run_loop.h" #include "build/build_config.h" #include "ipc/attachment_broker_privileged.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_channel_proxy.h" #include "ipc/ipc_listener.h" #include "ipc/ipc_message.h" #include "ipc/ipc_platform_file.h" #include "remoting/base/auto_thread.h" #include "remoting/base/auto_thread_task_runner.h" #include "remoting/base/constants.h" #include "remoting/host/chromoting_messages.h" #include "remoting/host/desktop_process.h" #include "remoting/host/desktop_session.h" #include "remoting/host/desktop_session_connector.h" #include "remoting/host/desktop_session_proxy.h" #include "remoting/host/fake_mouse_cursor_monitor.h" #include "remoting/host/host_mock_objects.h" #include "remoting/host/ipc_desktop_environment.h" #include "remoting/protocol/fake_desktop_capturer.h" #include "remoting/protocol/protocol_mock_objects.h" #include "remoting/protocol/test_event_matchers.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" #include "third_party/webrtc/modules/desktop_capture/desktop_region.h" #include "third_party/webrtc/modules/desktop_capture/screen_capturer_mock_objects.h" using testing::_; using testing::AnyNumber; using testing::AtLeast; using testing::AtMost; using testing::DeleteArg; using testing::DoAll; using testing::InSequence; using testing::Return; using testing::ReturnRef; namespace remoting { using protocol::test::EqualsTouchEvent; using protocol::test::EqualsTouchEventTypeAndId; namespace { // Receives messages sent from the network process to the daemon. class FakeDaemonSender : public IPC::Sender { public: FakeDaemonSender() {} ~FakeDaemonSender() override {} // IPC::Sender implementation. bool Send(IPC::Message* message) override; MOCK_METHOD3(ConnectTerminal, void(int, const ScreenResolution&, bool)); MOCK_METHOD1(DisconnectTerminal, void(int)); MOCK_METHOD2(SetScreenResolution, void(int, const ScreenResolution&)); private: void OnMessageReceived(const IPC::Message& message); DISALLOW_COPY_AND_ASSIGN(FakeDaemonSender); }; // Receives messages sent from the desktop process to the daemon. class MockDaemonListener : public IPC::Listener { public: MockDaemonListener() {} ~MockDaemonListener() override {} bool OnMessageReceived(const IPC::Message& message) override; MOCK_METHOD1(OnDesktopAttached, void(IPC::PlatformFileForTransit)); MOCK_METHOD1(OnChannelConnected, void(int32_t)); MOCK_METHOD0(OnChannelError, void()); private: DISALLOW_COPY_AND_ASSIGN(MockDaemonListener); }; bool FakeDaemonSender::Send(IPC::Message* message) { OnMessageReceived(*message); delete message; return true; } void FakeDaemonSender::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(FakeDaemonSender, message) IPC_MESSAGE_HANDLER(ChromotingNetworkHostMsg_ConnectTerminal, ConnectTerminal) IPC_MESSAGE_HANDLER(ChromotingNetworkHostMsg_DisconnectTerminal, DisconnectTerminal) IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_SetScreenResolution, SetScreenResolution) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() EXPECT_TRUE(handled); } bool MockDaemonListener::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(MockDaemonListener, message) IPC_MESSAGE_HANDLER(ChromotingDesktopDaemonMsg_DesktopAttached, OnDesktopAttached) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() EXPECT_TRUE(handled); return handled; } } // namespace class IpcDesktopEnvironmentTest : public testing::Test { public: IpcDesktopEnvironmentTest(); ~IpcDesktopEnvironmentTest() override; void SetUp() override; void TearDown() override; void ConnectTerminal(int terminal_id, const ScreenResolution& resolution, bool virtual_terminal); void DisconnectTerminal(int terminal_id); // Creates a DesktopEnvironment with a fake webrtc::DesktopCapturer, to mock // DesktopEnvironmentFactory::Create(). DesktopEnvironment* CreateDesktopEnvironment(); // Creates a dummy InputInjector, to mock // DesktopEnvironment::CreateInputInjector(). InputInjector* CreateInputInjector(); // Creates a fake webrtc::DesktopCapturer, to mock // DesktopEnvironment::CreateVideoCapturer(). webrtc::DesktopCapturer* CreateVideoCapturer(); // Creates a MockMouseCursorMonitor, to mock // DesktopEnvironment::CreateMouseCursorMonitor webrtc::MouseCursorMonitor* CreateMouseCursorMonitor(); void DeleteDesktopEnvironment(); // Forwards |event| to |clipboard_stub_|. void ReflectClipboardEvent(const protocol::ClipboardEvent& event); protected: // Creates and starts an instance of desktop process object. void CreateDesktopProcess(); // Destroys the desktop process object created by CreateDesktopProcess(). void DestoyDesktopProcess(); void OnDisconnectCallback(); // Invoked when ChromotingDesktopDaemonMsg_DesktopAttached message is // received. void OnDesktopAttached(IPC::PlatformFileForTransit desktop_pipe); void RunMainLoopUntilDone(); // The main message loop. base::MessageLoopForUI message_loop_; // Runs until |desktop_session_proxy_| is connected to the desktop. scoped_ptr setup_run_loop_; scoped_refptr task_runner_; scoped_refptr io_task_runner_; std::string client_jid_; // Clipboard stub that receives clipboard events from the desktop process. protocol::ClipboardStub* clipboard_stub_; // The daemons's end of the daemon-to-desktop channel. scoped_ptr desktop_channel_; // Name of the daemon-to-desktop channel. std::string desktop_channel_name_; // Delegate that is passed to |desktop_channel_|. MockDaemonListener desktop_listener_; FakeDaemonSender daemon_channel_; scoped_ptr desktop_environment_factory_; scoped_ptr desktop_environment_; // The IPC input injector. scoped_ptr input_injector_; // The IPC screen controls. scoped_ptr screen_controls_; // The IPC screen capturer. scoped_ptr video_capturer_; // Represents the desktop process running in a user session. scoped_ptr desktop_process_; // Input injector owned by |desktop_process_|. MockInputInjector* remote_input_injector_; // The last |terminal_id| passed to ConnectTermina(); int terminal_id_; webrtc::MockScreenCapturerCallback desktop_capturer_callback_; MockClientSessionControl client_session_control_; base::WeakPtrFactory client_session_control_factory_; private: // Runs until there are references to |task_runner_|. base::RunLoop main_run_loop_; }; IpcDesktopEnvironmentTest::IpcDesktopEnvironmentTest() : client_jid_("user@domain/rest-of-jid"), clipboard_stub_(nullptr), remote_input_injector_(nullptr), terminal_id_(-1), client_session_control_factory_(&client_session_control_) { IPC::AttachmentBrokerPrivileged::CreateBrokerForSingleProcessTests(); } IpcDesktopEnvironmentTest::~IpcDesktopEnvironmentTest() { } void IpcDesktopEnvironmentTest::SetUp() { // Arrange to run |message_loop_| until no components depend on it. task_runner_ = new AutoThreadTaskRunner( message_loop_.task_runner(), main_run_loop_.QuitClosure()); io_task_runner_ = AutoThread::CreateWithType( "IPC thread", task_runner_, base::MessageLoop::TYPE_IO); setup_run_loop_.reset(new base::RunLoop()); // Set expectation that the DaemonProcess will send DesktopAttached message // once it is ready. EXPECT_CALL(desktop_listener_, OnChannelConnected(_)) .Times(AnyNumber()); EXPECT_CALL(desktop_listener_, OnDesktopAttached(_)) .Times(AnyNumber()) .WillRepeatedly(Invoke(this, &IpcDesktopEnvironmentTest::OnDesktopAttached)); EXPECT_CALL(desktop_listener_, OnChannelError()) .Times(AnyNumber()) .WillOnce(Invoke(this, &IpcDesktopEnvironmentTest::DestoyDesktopProcess)); // Intercept requests to connect and disconnect a terminal. EXPECT_CALL(daemon_channel_, ConnectTerminal(_, _, _)) .Times(AnyNumber()) .WillRepeatedly(Invoke(this, &IpcDesktopEnvironmentTest::ConnectTerminal)); EXPECT_CALL(daemon_channel_, DisconnectTerminal(_)) .Times(AnyNumber()) .WillRepeatedly(Invoke(this, &IpcDesktopEnvironmentTest::DisconnectTerminal)); EXPECT_CALL(client_session_control_, client_jid()) .Times(AnyNumber()) .WillRepeatedly(ReturnRef(client_jid_)); EXPECT_CALL(client_session_control_, DisconnectSession(_)) .Times(AnyNumber()) .WillRepeatedly(InvokeWithoutArgs( this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment)); EXPECT_CALL(client_session_control_, OnLocalMouseMoved(_)) .Times(0); EXPECT_CALL(client_session_control_, SetDisableInputs(_)) .Times(0); // Create a desktop environment instance. desktop_environment_factory_.reset(new IpcDesktopEnvironmentFactory( task_runner_, task_runner_, task_runner_, io_task_runner_, &daemon_channel_)); desktop_environment_ = desktop_environment_factory_->Create( client_session_control_factory_.GetWeakPtr()); screen_controls_ = desktop_environment_->CreateScreenControls(); // Create the input injector. input_injector_ = desktop_environment_->CreateInputInjector(); // Create the screen capturer. video_capturer_ = desktop_environment_->CreateVideoCapturer(); desktop_environment_->SetCapabilities(std::string()); } void IpcDesktopEnvironmentTest::TearDown() { RunMainLoopUntilDone(); } void IpcDesktopEnvironmentTest::ConnectTerminal( int terminal_id, const ScreenResolution& resolution, bool virtual_terminal) { EXPECT_NE(terminal_id_, terminal_id); terminal_id_ = terminal_id; CreateDesktopProcess(); } void IpcDesktopEnvironmentTest::DisconnectTerminal(int terminal_id) { EXPECT_EQ(terminal_id_, terminal_id); // The IPC desktop environment is fully destroyed now. Release the remaining // task runners. desktop_environment_factory_.reset(); } DesktopEnvironment* IpcDesktopEnvironmentTest::CreateDesktopEnvironment() { MockDesktopEnvironment* desktop_environment = new MockDesktopEnvironment(); EXPECT_CALL(*desktop_environment, CreateAudioCapturerPtr()) .Times(0); EXPECT_CALL(*desktop_environment, CreateInputInjectorPtr()) .Times(AtMost(1)) .WillOnce(Invoke( this, &IpcDesktopEnvironmentTest::CreateInputInjector)); EXPECT_CALL(*desktop_environment, CreateScreenControlsPtr()) .Times(AtMost(1)); EXPECT_CALL(*desktop_environment, CreateVideoCapturerPtr()) .Times(AtMost(1)) .WillOnce(Invoke( this, &IpcDesktopEnvironmentTest::CreateVideoCapturer)); EXPECT_CALL(*desktop_environment, CreateMouseCursorMonitorPtr()) .Times(AtMost(1)) .WillOnce(Invoke( this, &IpcDesktopEnvironmentTest::CreateMouseCursorMonitor)); EXPECT_CALL(*desktop_environment, GetCapabilities()) .Times(AtMost(1)); EXPECT_CALL(*desktop_environment, SetCapabilities(_)) .Times(AtMost(1)); // Let tests know that the remote desktop environment is created. message_loop_.PostTask(FROM_HERE, setup_run_loop_->QuitClosure()); return desktop_environment; } InputInjector* IpcDesktopEnvironmentTest::CreateInputInjector() { EXPECT_TRUE(remote_input_injector_ == nullptr); remote_input_injector_ = new testing::StrictMock(); EXPECT_CALL(*remote_input_injector_, StartPtr(_)); return remote_input_injector_; } webrtc::DesktopCapturer* IpcDesktopEnvironmentTest::CreateVideoCapturer() { return new protocol::FakeDesktopCapturer(); } webrtc::MouseCursorMonitor* IpcDesktopEnvironmentTest::CreateMouseCursorMonitor() { return new FakeMouseCursorMonitor(); } void IpcDesktopEnvironmentTest::DeleteDesktopEnvironment() { input_injector_.reset(); screen_controls_.reset(); video_capturer_.reset(); // Trigger DisconnectTerminal(). desktop_environment_.reset(); } void IpcDesktopEnvironmentTest::ReflectClipboardEvent( const protocol::ClipboardEvent& event) { clipboard_stub_->InjectClipboardEvent(event); } void IpcDesktopEnvironmentTest::CreateDesktopProcess() { EXPECT_TRUE(task_runner_.get()); EXPECT_TRUE(io_task_runner_.get()); // Create the daemon end of the daemon-to-desktop channel. desktop_channel_name_ = IPC::Channel::GenerateUniqueRandomChannelID(); desktop_channel_ = IPC::ChannelProxy::Create( IPC::ChannelHandle(desktop_channel_name_), IPC::Channel::MODE_SERVER, &desktop_listener_, io_task_runner_.get()); // Create and start the desktop process. desktop_process_.reset(new DesktopProcess(task_runner_, io_task_runner_, desktop_channel_name_)); scoped_ptr desktop_environment_factory( new MockDesktopEnvironmentFactory()); EXPECT_CALL(*desktop_environment_factory, CreatePtr()) .Times(AnyNumber()) .WillRepeatedly(Invoke( this, &IpcDesktopEnvironmentTest::CreateDesktopEnvironment)); EXPECT_CALL(*desktop_environment_factory, SupportsAudioCapture()) .Times(AnyNumber()) .WillRepeatedly(Return(false)); EXPECT_TRUE(desktop_process_->Start(std::move(desktop_environment_factory))); } void IpcDesktopEnvironmentTest::DestoyDesktopProcess() { desktop_channel_.reset(); if (desktop_process_) { desktop_process_->OnChannelError(); desktop_process_.reset(); } remote_input_injector_ = nullptr; } void IpcDesktopEnvironmentTest::OnDisconnectCallback() { DeleteDesktopEnvironment(); } void IpcDesktopEnvironmentTest::OnDesktopAttached( IPC::PlatformFileForTransit desktop_pipe) { base::ProcessHandle process_handle = base::GetCurrentProcessHandle(); #if defined(OS_WIN) ASSERT_NE(FALSE, ::DuplicateHandle(GetCurrentProcess(), process_handle, GetCurrentProcess(), &process_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)); #endif // Instruct DesktopSessionProxy to connect to the network-to-desktop pipe. desktop_environment_factory_->OnDesktopSessionAgentAttached( terminal_id_, process_handle, desktop_pipe); } void IpcDesktopEnvironmentTest::RunMainLoopUntilDone() { task_runner_ = nullptr; io_task_runner_ = nullptr; main_run_loop_.Run(); } // Runs until the desktop is attached and exits immediately after that. TEST_F(IpcDesktopEnvironmentTest, Basic) { scoped_ptr clipboard_stub( new protocol::MockClipboardStub()); EXPECT_CALL(*clipboard_stub, InjectClipboardEvent(_)) .Times(0); // Start the input injector and screen capturer. input_injector_->Start(std::move(clipboard_stub)); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); // Stop the test. DeleteDesktopEnvironment(); } // Check Capabilities. TEST_F(IpcDesktopEnvironmentTest, CapabilitiesNoTouch) { scoped_ptr clipboard_stub( new protocol::MockClipboardStub()); EXPECT_CALL(*clipboard_stub, InjectClipboardEvent(_)) .Times(0); EXPECT_EQ("rateLimitResizeRequests", desktop_environment_->GetCapabilities()); // Start the input injector and screen capturer. input_injector_->Start(std::move(clipboard_stub)); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); // Stop the test. DeleteDesktopEnvironment(); } // Check touchEvents capability is set when the desktop environment can // inject touch events. TEST_F(IpcDesktopEnvironmentTest, TouchEventsCapabilities) { // Create an environment with multi touch enabled. desktop_environment_factory_->set_supports_touch_events(true); desktop_environment_ = desktop_environment_factory_->Create( client_session_control_factory_.GetWeakPtr()); scoped_ptr clipboard_stub( new protocol::MockClipboardStub()); EXPECT_CALL(*clipboard_stub, InjectClipboardEvent(_)) .Times(0); EXPECT_EQ("rateLimitResizeRequests touchEvents", desktop_environment_->GetCapabilities()); // Start the input injector and screen capturer. input_injector_->Start(std::move(clipboard_stub)); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); // Stop the test. DeleteDesktopEnvironment(); } // Tests that the video capturer receives a frame over IPC. TEST_F(IpcDesktopEnvironmentTest, CaptureFrame) { scoped_ptr clipboard_stub( new protocol::MockClipboardStub()); EXPECT_CALL(*clipboard_stub, InjectClipboardEvent(_)) .Times(0); // Start the input injector and screen capturer. input_injector_->Start(std::move(clipboard_stub)); video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); // Stop the test when the first frame is captured. EXPECT_CALL(desktop_capturer_callback_, OnCaptureCompleted(_)) .WillOnce(DoAll( DeleteArg<0>(), InvokeWithoutArgs( this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment))); // Capture a single frame. video_capturer_->Capture(webrtc::DesktopRegion()); } // Tests that attaching to a new desktop works. TEST_F(IpcDesktopEnvironmentTest, Reattach) { scoped_ptr clipboard_stub( new protocol::MockClipboardStub()); EXPECT_CALL(*clipboard_stub, InjectClipboardEvent(_)) .Times(0); // Start the input injector and screen capturer. input_injector_->Start(std::move(clipboard_stub)); video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); // Create and start a new desktop process object. setup_run_loop_.reset(new base::RunLoop()); DestoyDesktopProcess(); CreateDesktopProcess(); setup_run_loop_->Run(); // Stop the test. DeleteDesktopEnvironment(); } // Tests injection of clipboard events. TEST_F(IpcDesktopEnvironmentTest, InjectClipboardEvent) { scoped_ptr clipboard_stub( new protocol::MockClipboardStub()); clipboard_stub_ = clipboard_stub.get(); // Stop the test when a clipboard event is received from the desktop process. EXPECT_CALL(*clipboard_stub, InjectClipboardEvent(_)) .Times(1) .WillOnce(InvokeWithoutArgs( this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment)); // Start the input injector and screen capturer. input_injector_->Start(std::move(clipboard_stub)); video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); // Expect a single clipboard event. EXPECT_CALL(*remote_input_injector_, InjectClipboardEvent(_)) .Times(1) .WillOnce(Invoke(this, &IpcDesktopEnvironmentTest::ReflectClipboardEvent)); // Send a clipboard event. protocol::ClipboardEvent event; event.set_mime_type(kMimeTypeTextUtf8); event.set_data("a"); input_injector_->InjectClipboardEvent(event); } // Tests injection of key events. TEST_F(IpcDesktopEnvironmentTest, InjectKeyEvent) { scoped_ptr clipboard_stub( new protocol::MockClipboardStub()); EXPECT_CALL(*clipboard_stub, InjectClipboardEvent(_)) .Times(0); // Start the input injector and screen capturer. input_injector_->Start(std::move(clipboard_stub)); video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); // Expect a single key event. EXPECT_CALL(*remote_input_injector_, InjectKeyEvent(_)) .Times(AtLeast(1)) .WillRepeatedly(InvokeWithoutArgs( this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment)); // Send a key event. protocol::KeyEvent event; event.set_usb_keycode(0x070004); event.set_pressed(true); input_injector_->InjectKeyEvent(event); } // Tests injection of text events. TEST_F(IpcDesktopEnvironmentTest, InjectTextEvent) { scoped_ptr clipboard_stub( new protocol::MockClipboardStub()); EXPECT_CALL(*clipboard_stub, InjectClipboardEvent(_)) .Times(0); // Start the input injector and screen capturer. input_injector_->Start(std::move(clipboard_stub)); video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); // Expect a single text event. EXPECT_CALL(*remote_input_injector_, InjectTextEvent(_)) .Times(AtLeast(1)) .WillRepeatedly(InvokeWithoutArgs( this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment)); // Send a text event. protocol::TextEvent event; event.set_text("hello"); input_injector_->InjectTextEvent(event); } // Tests injection of mouse events. TEST_F(IpcDesktopEnvironmentTest, InjectMouseEvent) { scoped_ptr clipboard_stub( new protocol::MockClipboardStub()); EXPECT_CALL(*clipboard_stub, InjectClipboardEvent(_)) .Times(0); // Start the input injector and screen capturer. input_injector_->Start(std::move(clipboard_stub)); video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); // Expect a single mouse event. EXPECT_CALL(*remote_input_injector_, InjectMouseEvent(_)) .Times(1) .WillOnce(InvokeWithoutArgs( this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment)); // Send a mouse event. protocol::MouseEvent event; event.set_x(0); event.set_y(0); input_injector_->InjectMouseEvent(event); } // Tests injection of touch events. TEST_F(IpcDesktopEnvironmentTest, InjectTouchEvent) { scoped_ptr clipboard_stub( new protocol::MockClipboardStub()); EXPECT_CALL(*clipboard_stub, InjectClipboardEvent(_)) .Times(0); // Start the input injector and screen capturer. input_injector_->Start(std::move(clipboard_stub)); video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); protocol::TouchEvent event; event.set_event_type(protocol::TouchEvent::TOUCH_POINT_START); protocol::TouchEventPoint* point = event.add_touch_points(); point->set_id(0u); point->set_x(0.0f); point->set_y(0.0f); point->set_radius_x(0.0f); point->set_radius_y(0.0f); point->set_angle(0.0f); point->set_pressure(0.0f); ON_CALL(*remote_input_injector_, InjectTouchEvent(_)) .WillByDefault(InvokeWithoutArgs( this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment)); InSequence s; // Expect that the event gets propagated to remote_input_injector_. // And one more call for ReleaseAll(). EXPECT_CALL(*remote_input_injector_, InjectTouchEvent(EqualsTouchEvent(event))); EXPECT_CALL(*remote_input_injector_, InjectTouchEvent(EqualsTouchEventTypeAndId( protocol::TouchEvent::TOUCH_POINT_CANCEL, 0u))); // Send the touch event. input_injector_->InjectTouchEvent(event); } // Tests that setting the desktop resolution works. TEST_F(IpcDesktopEnvironmentTest, SetScreenResolution) { scoped_ptr clipboard_stub( new protocol::MockClipboardStub()); EXPECT_CALL(*clipboard_stub, InjectClipboardEvent(_)) .Times(0); // Start the input injector and screen capturer. input_injector_->Start(std::move(clipboard_stub)); video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); EXPECT_CALL(daemon_channel_, SetScreenResolution(_, _)) .Times(1) .WillOnce(InvokeWithoutArgs( this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment)); // Change the desktop resolution. screen_controls_->SetScreenResolution(ScreenResolution( webrtc::DesktopSize(100, 100), webrtc::DesktopVector(96, 96))); } } // namespace remoting