blob: b4c4c5269d642a622524c6749f8dabe95f8fd6cf [file] [log] [blame]
// 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 "remoting/host/ipc_desktop_environment.h"
#include <stdint.h>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_pump_type.h"
#include "base/process/process.h"
#include "base/process/process_handle.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "build/build_config.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 "mojo/public/cpp/system/message_pipe.h"
#include "remoting/base/auto_thread.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/base/constants.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_keyboard_layout_monitor.h"
#include "remoting/host/fake_mouse_cursor_monitor.h"
#include "remoting/host/host_mock_objects.h"
#include "remoting/host/mojom/desktop_session.mojom.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_capturer.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
using base::test::RunCallback;
using base::test::RunOnceCallback;
using base::test::RunOnceClosure;
using testing::_;
using testing::AnyNumber;
using testing::AtLeast;
using testing::AtMost;
using testing::ByMove;
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;
using SetUpUrlForwarderResponse =
protocol::UrlForwarderControl::SetUpUrlForwarderResponse;
namespace {
class MockScreenCapturerCallback : public webrtc::DesktopCapturer::Callback {
public:
MockScreenCapturerCallback() = default;
MockScreenCapturerCallback(const MockScreenCapturerCallback&) = delete;
MockScreenCapturerCallback& operator=(const MockScreenCapturerCallback&) =
delete;
~MockScreenCapturerCallback() override = default;
MOCK_METHOD(void,
OnCaptureResultPtr,
(webrtc::DesktopCapturer::Result,
std::unique_ptr<webrtc::DesktopFrame>*));
void OnCaptureResult(webrtc::DesktopCapturer::Result result,
std::unique_ptr<webrtc::DesktopFrame> frame) override {
OnCaptureResultPtr(result, &frame);
}
};
// Receives messages sent from the desktop process to the daemon.
class MockDaemonListener : public IPC::Listener,
public mojom::DesktopSessionRequestHandler {
public:
MockDaemonListener() = default;
MockDaemonListener(const MockDaemonListener&) = delete;
MockDaemonListener& operator=(const MockDaemonListener&) = delete;
~MockDaemonListener() override = default;
bool OnMessageReceived(const IPC::Message& message) override;
void OnAssociatedInterfaceRequest(
const std::string& interface_name,
mojo::ScopedInterfaceEndpointHandle handle) override;
MOCK_METHOD(void,
ConnectDesktopChannel,
(mojo::ScopedMessagePipeHandle handle),
(override));
MOCK_METHOD(void, InjectSecureAttentionSequence, (), (override));
MOCK_METHOD(void, CrashNetworkProcess, (), (override));
MOCK_METHOD(void, OnChannelConnected, (int32_t), (override));
MOCK_METHOD(void, OnChannelError, (), (override));
void Disconnect();
private:
mojo::AssociatedReceiver<mojom::DesktopSessionRequestHandler>
desktop_session_request_handler_{this};
};
bool MockDaemonListener::OnMessageReceived(const IPC::Message& message) {
ADD_FAILURE() << "Unexpected call to OnMessageReceived()";
return false;
}
void MockDaemonListener::OnAssociatedInterfaceRequest(
const std::string& interface_name,
mojo::ScopedInterfaceEndpointHandle handle) {
EXPECT_EQ(mojom::DesktopSessionRequestHandler::Name_, interface_name);
mojo::PendingAssociatedReceiver<mojom::DesktopSessionRequestHandler>
pending_receiver(std::move(handle));
desktop_session_request_handler_.Bind(std::move(pending_receiver));
}
void MockDaemonListener::Disconnect() {
desktop_session_request_handler_.reset();
}
class MockDesktopSessionManager : public mojom::DesktopSessionManager {
public:
MockDesktopSessionManager() = default;
~MockDesktopSessionManager() override = default;
void BindNewReceiver(
mojo::PendingAssociatedReceiver<mojom::DesktopSessionManager> receiver);
// mojom::DesktopSessionManager implementation.
MOCK_METHOD(void,
CreateDesktopSession,
(int, const ScreenResolution&, bool),
(override));
MOCK_METHOD(void, CloseDesktopSession, (int), (override));
MOCK_METHOD(void,
SetScreenResolution,
(int, const ScreenResolution&),
(override));
private:
mojo::AssociatedReceiver<mojom::DesktopSessionManager>
desktop_session_manager_{this};
};
void MockDesktopSessionManager::BindNewReceiver(
mojo::PendingAssociatedReceiver<mojom::DesktopSessionManager> receiver) {
desktop_session_manager_.reset();
// EnableUnassociatedUsage() sets up a private message pipe for the remote /
// receiver pair used in this test which simplifies our test setup and
// doesn't change any behaviors being tested.
receiver.EnableUnassociatedUsage();
desktop_session_manager_.Bind(std::move(receiver));
}
} // namespace
class IpcDesktopEnvironmentTest : public testing::Test {
public:
IpcDesktopEnvironmentTest();
~IpcDesktopEnvironmentTest() override;
void SetUp() override;
void TearDown() override;
void CreateDesktopSession(int terminal_id,
const ScreenResolution& resolution,
bool virtual_terminal);
void CloseDesktopSession(int terminal_id);
// Creates a DesktopEnvironment with a fake webrtc::DesktopCapturer, to mock
// DesktopEnvironmentFactory::Create().
std::unique_ptr<DesktopEnvironment> CreateDesktopEnvironment();
// Creates a fake InputInjector, to mock
// DesktopEnvironment::CreateInputInjector().
std::unique_ptr<InputInjector> CreateInputInjector();
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 DestroyDesktopProcess();
// Creates a new remote URL forwarder configurator for the desktop process.
void ResetRemoteUrlForwarderConfigurator();
void OnDisconnectCallback();
// Invoked when ConnectDesktopChannel() is called over IPC.
void ConnectDesktopChannel(mojo::ScopedMessagePipeHandle desktop_pipe);
// Runs until there are no references to |task_runner_|. Calls after the main
// loop has been run are no-op.
void RunMainLoopUntilDone();
// Some tests require |setup_run_loop_| to be reset so we need a method which
// can be bound that will quit the current run loop.
void QuitSetupRunLoop();
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::MainThreadType::UI};
// Runs until |desktop_session_proxy_| is connected to the desktop.
std::unique_ptr<base::RunLoop> setup_run_loop_;
scoped_refptr<AutoThreadTaskRunner> task_runner_;
scoped_refptr<AutoThreadTaskRunner> io_task_runner_;
std::string client_jid_;
// Clipboard stub that receives clipboard events from the desktop process.
raw_ptr<protocol::ClipboardStub> clipboard_stub_;
// The daemons's end of the daemon-to-desktop channel.
std::unique_ptr<IPC::ChannelProxy> desktop_channel_;
MockDesktopSessionManager mock_desktop_session_manager_;
// Delegate that is passed to |desktop_channel_|.
MockDaemonListener desktop_listener_;
std::unique_ptr<IpcDesktopEnvironmentFactory> desktop_environment_factory_;
std::unique_ptr<DesktopEnvironment> desktop_environment_;
// The IPC input injector.
std::unique_ptr<InputInjector> input_injector_;
// The IPC screen controls.
std::unique_ptr<ScreenControls> screen_controls_;
// The IPC screen capturer.
std::unique_ptr<webrtc::DesktopCapturer> video_capturer_;
// Represents the desktop process running in a user session.
std::unique_ptr<DesktopProcess> desktop_process_;
// Input injector owned by |desktop_process_|.
raw_ptr<MockInputInjector> remote_input_injector_;
// Will be transferred to the caller of
// MockDesktopEnvironment::CreateUrlForwarderConfigurator().
// We create the configurator in advance to allow setting expectations before
// the desktop process is being created, during which the configurator will be
// used.
std::unique_ptr<MockUrlForwarderConfigurator>
owned_remote_url_forwarder_configurator_;
raw_ptr<MockUrlForwarderConfigurator> remote_url_forwarder_configurator_;
std::unique_ptr<UrlForwarderConfigurator> url_forwarder_configurator_;
// The last |terminal_id| passed to ConnectTermina();
int terminal_id_;
MockScreenCapturerCallback desktop_capturer_callback_;
MockClientSessionControl client_session_control_;
base::WeakPtrFactory<ClientSessionControl> client_session_control_factory_;
MockClientSessionEvents client_session_events_;
base::WeakPtrFactory<MockClientSessionEvents> client_session_events_factory_{
&client_session_events_};
private:
// Runs until there are no 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_) {
}
IpcDesktopEnvironmentTest::~IpcDesktopEnvironmentTest() = default;
void IpcDesktopEnvironmentTest::SetUp() {
// Arrange to run |message_loop_| until no components depend on it.
task_runner_ =
new AutoThreadTaskRunner(task_environment_.GetMainThreadTaskRunner(),
main_run_loop_.QuitClosure());
io_task_runner_ = AutoThread::CreateWithType("IPC thread", task_runner_,
base::MessagePumpType::IO);
setup_run_loop_ = std::make_unique<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_, ConnectDesktopChannel(_))
.Times(AnyNumber())
.WillRepeatedly([&](mojo::ScopedMessagePipeHandle desktop_pipe) {
ConnectDesktopChannel(std::move(desktop_pipe));
});
EXPECT_CALL(desktop_listener_, OnChannelError())
.Times(AnyNumber())
.WillOnce(
Invoke(this, &IpcDesktopEnvironmentTest::DestroyDesktopProcess));
// Intercept requests to connect and disconnect a terminal.
EXPECT_CALL(mock_desktop_session_manager_, CreateDesktopSession(_, _, _))
.Times(AnyNumber())
.WillRepeatedly(
Invoke(this, &IpcDesktopEnvironmentTest::CreateDesktopSession));
EXPECT_CALL(mock_desktop_session_manager_, CloseDesktopSession(_))
.Times(AnyNumber())
.WillRepeatedly(
Invoke(this, &IpcDesktopEnvironmentTest::CloseDesktopSession));
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_, OnLocalPointerMoved(_, _)).Times(0);
EXPECT_CALL(client_session_control_, SetDisableInputs(_))
.Times(0);
// Most tests will only call this once but reattach will call multiple times.
EXPECT_CALL(client_session_events_, OnDesktopAttached(_))
.Times(AnyNumber())
.WillRepeatedly(InvokeWithoutArgs(
this, &IpcDesktopEnvironmentTest::QuitSetupRunLoop));
EXPECT_CALL(client_session_events_, OnDesktopDetached()).Times(AnyNumber());
// Create a desktop environment instance.
mojo::AssociatedRemote<mojom::DesktopSessionManager> remote;
mock_desktop_session_manager_.BindNewReceiver(
remote.BindNewEndpointAndPassReceiver());
desktop_environment_factory_ = std::make_unique<IpcDesktopEnvironmentFactory>(
task_runner_, task_runner_, io_task_runner_, std::move(remote));
desktop_environment_ = desktop_environment_factory_->Create(
client_session_control_factory_.GetWeakPtr(),
client_session_events_factory_.GetWeakPtr(), DesktopEnvironmentOptions());
screen_controls_ = desktop_environment_->CreateScreenControls();
// Create the input injector.
input_injector_ = desktop_environment_->CreateInputInjector();
// Create the screen capturer.
video_capturer_ = desktop_environment_->CreateVideoCapturer(nullptr);
desktop_environment_->SetCapabilities(std::string());
url_forwarder_configurator_ =
desktop_environment_->CreateUrlForwarderConfigurator();
ResetRemoteUrlForwarderConfigurator();
}
void IpcDesktopEnvironmentTest::TearDown() {
RunMainLoopUntilDone();
}
void IpcDesktopEnvironmentTest::CreateDesktopSession(
int terminal_id,
const ScreenResolution& resolution,
bool virtual_terminal) {
EXPECT_NE(terminal_id_, terminal_id);
terminal_id_ = terminal_id;
CreateDesktopProcess();
}
void IpcDesktopEnvironmentTest::CloseDesktopSession(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();
}
std::unique_ptr<DesktopEnvironment>
IpcDesktopEnvironmentTest::CreateDesktopEnvironment() {
auto desktop_environment = std::make_unique<MockDesktopEnvironment>();
EXPECT_CALL(*desktop_environment, CreateAudioCapturer()).Times(0);
EXPECT_CALL(*desktop_environment, CreateInputInjector())
.Times(AtMost(1))
.WillOnce(Invoke(this, &IpcDesktopEnvironmentTest::CreateInputInjector));
EXPECT_CALL(*desktop_environment, CreateScreenControls()).Times(AtMost(1));
EXPECT_CALL(*desktop_environment, CreateVideoCapturer(_))
.Times(AtMost(1))
.WillOnce(
Return(ByMove(std::make_unique<protocol::FakeDesktopCapturer>())));
EXPECT_CALL(*desktop_environment, CreateActionExecutor()).Times(AtMost(1));
EXPECT_CALL(*desktop_environment, CreateFileOperations()).Times(AtMost(1));
EXPECT_CALL(*desktop_environment, CreateMouseCursorMonitor())
.Times(AtMost(1))
.WillOnce(Return(ByMove(std::make_unique<FakeMouseCursorMonitor>())));
EXPECT_CALL(*desktop_environment, CreateKeyboardLayoutMonitor(_))
.Times(AtMost(1))
.WillOnce(Return(ByMove(std::make_unique<FakeKeyboardLayoutMonitor>())));
EXPECT_CALL(*desktop_environment, GetCapabilities())
.Times(AtMost(1));
EXPECT_CALL(*desktop_environment, SetCapabilities(_))
.Times(AtMost(1));
DCHECK(owned_remote_url_forwarder_configurator_);
EXPECT_CALL(*desktop_environment, CreateUrlForwarderConfigurator())
.Times(AtMost(1))
.WillOnce(
Return(ByMove(std::move(owned_remote_url_forwarder_configurator_))));
return desktop_environment;
}
std::unique_ptr<InputInjector>
IpcDesktopEnvironmentTest::CreateInputInjector() {
auto remote_input_injector =
std::make_unique<testing::StrictMock<MockInputInjector>>();
EXPECT_TRUE(remote_input_injector_ == nullptr);
remote_input_injector_ = remote_input_injector.get();
EXPECT_CALL(*remote_input_injector_, Start(_));
return remote_input_injector;
}
void IpcDesktopEnvironmentTest::DeleteDesktopEnvironment() {
input_injector_.reset();
screen_controls_.reset();
video_capturer_.reset();
url_forwarder_configurator_.reset();
// Trigger CloseDesktopSession().
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.
mojo::MessagePipe pipe;
desktop_channel_ = IPC::ChannelProxy::Create(
pipe.handle0.release(), IPC::Channel::MODE_SERVER, &desktop_listener_,
io_task_runner_.get(), base::ThreadTaskRunnerHandle::Get());
// Create and start the desktop process.
desktop_process_ = std::make_unique<DesktopProcess>(
task_runner_, io_task_runner_, io_task_runner_, std::move(pipe.handle1));
std::unique_ptr<MockDesktopEnvironmentFactory> desktop_environment_factory(
new MockDesktopEnvironmentFactory());
EXPECT_CALL(*desktop_environment_factory, Create(_, _, _))
.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::DestroyDesktopProcess() {
desktop_channel_.reset();
if (desktop_process_) {
desktop_process_->OnChannelError();
desktop_process_.reset();
}
desktop_listener_.Disconnect();
remote_input_injector_ = nullptr;
}
void IpcDesktopEnvironmentTest::ResetRemoteUrlForwarderConfigurator() {
owned_remote_url_forwarder_configurator_ =
std::make_unique<MockUrlForwarderConfigurator>();
remote_url_forwarder_configurator_ =
owned_remote_url_forwarder_configurator_.get();
ON_CALL(*remote_url_forwarder_configurator_, IsUrlForwarderSetUp(_))
.WillByDefault(RunOnceCallback<0>(false));
}
void IpcDesktopEnvironmentTest::OnDisconnectCallback() {
DeleteDesktopEnvironment();
}
void IpcDesktopEnvironmentTest::ConnectDesktopChannel(
mojo::ScopedMessagePipeHandle desktop_pipe) {
// Instruct DesktopSessionProxy to connect to the network-to-desktop pipe.
desktop_environment_factory_->OnDesktopSessionAgentAttached(
terminal_id_, /*session_id=*/0, std::move(desktop_pipe));
}
void IpcDesktopEnvironmentTest::RunMainLoopUntilDone() {
bool should_run_loop = task_runner_ != nullptr;
task_runner_ = nullptr;
io_task_runner_ = nullptr;
if (should_run_loop) {
main_run_loop_.Run();
}
}
void IpcDesktopEnvironmentTest::QuitSetupRunLoop() {
setup_run_loop_->Quit();
}
// Runs until the desktop is attached and exits immediately after that.
TEST_F(IpcDesktopEnvironmentTest, Basic) {
std::unique_ptr<protocol::MockClipboardStub> 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 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_ = desktop_environment_factory_->Create(
client_session_control_factory_.GetWeakPtr(),
client_session_events_factory_.GetWeakPtr(), DesktopEnvironmentOptions());
std::unique_ptr<protocol::MockClipboardStub> clipboard_stub(
new protocol::MockClipboardStub());
EXPECT_CALL(*clipboard_stub, InjectClipboardEvent(_))
.Times(0);
std::string expected_capabilities = "rateLimitResizeRequests";
if (InputInjector::SupportsTouchEvents())
expected_capabilities += " touchEvents";
EXPECT_EQ(expected_capabilities, 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) {
std::unique_ptr<protocol::MockClipboardStub> 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_, OnCaptureResultPtr(_, _))
.WillOnce(InvokeWithoutArgs(
this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment));
// Capture a single frame.
video_capturer_->CaptureFrame();
}
// Tests that attaching to a new desktop works.
TEST_F(IpcDesktopEnvironmentTest, Reattach) {
std::unique_ptr<protocol::MockClipboardStub> 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_ = std::make_unique<base::RunLoop>();
DestroyDesktopProcess();
ResetRemoteUrlForwarderConfigurator();
CreateDesktopProcess();
setup_run_loop_->Run();
// Stop the test.
DeleteDesktopEnvironment();
}
// Tests injection of clipboard events.
TEST_F(IpcDesktopEnvironmentTest, InjectClipboardEvent) {
std::unique_ptr<protocol::MockClipboardStub> 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) {
std::unique_ptr<protocol::MockClipboardStub> 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) {
std::unique_ptr<protocol::MockClipboardStub> 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) {
std::unique_ptr<protocol::MockClipboardStub> 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) {
std::unique_ptr<protocol::MockClipboardStub> 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) {
std::unique_ptr<protocol::MockClipboardStub> 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(mock_desktop_session_manager_, SetScreenResolution(_, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(
this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment));
// Change the desktop resolution.
screen_controls_->SetScreenResolution(ScreenResolution(
webrtc::DesktopSize(100, 100),
webrtc::DesktopVector(96, 96)));
}
TEST_F(IpcDesktopEnvironmentTest, CheckUrlForwarderState) {
EXPECT_CALL(*remote_url_forwarder_configurator_, IsUrlForwarderSetUp(_))
.WillOnce(RunOnceCallback<0>(true));
base::MockCallback<UrlForwarderConfigurator::IsUrlForwarderSetUpCallback>
callback;
EXPECT_CALL(callback, Run(true))
.WillOnce([&]() {
// Do it again when the state is already known.
url_forwarder_configurator_->IsUrlForwarderSetUp(callback.Get());
})
.WillOnce(InvokeWithoutArgs(
this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment));
// Run the message loop until the desktop is attached.
setup_run_loop_->Run();
url_forwarder_configurator_->IsUrlForwarderSetUp(callback.Get());
// Run now rather than in TearDown() so that we can verify |callback|.
RunMainLoopUntilDone();
}
TEST_F(IpcDesktopEnvironmentTest, SetUpUrlForwarderHappyPath) {
EXPECT_CALL(*remote_url_forwarder_configurator_, IsUrlForwarderSetUp(_))
.WillOnce(RunOnceCallback<0>(false));
EXPECT_CALL(*remote_url_forwarder_configurator_, SetUpUrlForwarder(_))
.WillOnce([](auto& callback) {
callback.Run(SetUpUrlForwarderResponse::USER_INTERVENTION_REQUIRED);
callback.Run(SetUpUrlForwarderResponse::COMPLETE);
});
base::MockCallback<UrlForwarderConfigurator::SetUpUrlForwarderCallback>
setup_state_callback;
base::MockCallback<UrlForwarderConfigurator::IsUrlForwarderSetUpCallback>
is_set_up_callback;
{
InSequence s;
EXPECT_CALL(is_set_up_callback, Run(false)).WillOnce([&]() {
// Post task to prevent reentrant issue.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&UrlForwarderConfigurator::SetUpUrlForwarder,
base::Unretained(url_forwarder_configurator_.get()),
setup_state_callback.Get()));
});
EXPECT_CALL(setup_state_callback,
Run(SetUpUrlForwarderResponse::USER_INTERVENTION_REQUIRED))
.Times(1);
EXPECT_CALL(setup_state_callback, Run(SetUpUrlForwarderResponse::COMPLETE))
.WillOnce([&]() {
url_forwarder_configurator_->IsUrlForwarderSetUp(
is_set_up_callback.Get());
});
EXPECT_CALL(is_set_up_callback, Run(true))
.WillOnce(InvokeWithoutArgs(
this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment));
}
// Run the message loop until the desktop is attached.
setup_run_loop_->Run();
url_forwarder_configurator_->IsUrlForwarderSetUp(is_set_up_callback.Get());
// Run now rather than in TearDown() so that we can verify |callback|.
RunMainLoopUntilDone();
}
TEST_F(IpcDesktopEnvironmentTest, SetUpUrlForwarderFailed) {
EXPECT_CALL(*remote_url_forwarder_configurator_, IsUrlForwarderSetUp(_))
.WillOnce(RunOnceCallback<0>(false));
EXPECT_CALL(*remote_url_forwarder_configurator_, SetUpUrlForwarder(_))
.WillOnce(RunOnceCallback<0>(SetUpUrlForwarderResponse::FAILED));
base::MockCallback<UrlForwarderConfigurator::SetUpUrlForwarderCallback>
setup_state_callback;
base::MockCallback<UrlForwarderConfigurator::IsUrlForwarderSetUpCallback>
is_set_up_callback;
{
InSequence s;
EXPECT_CALL(is_set_up_callback, Run(false)).WillOnce([&]() {
// Post task to prevent reentrant issue.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&UrlForwarderConfigurator::SetUpUrlForwarder,
base::Unretained(url_forwarder_configurator_.get()),
setup_state_callback.Get()));
});
EXPECT_CALL(setup_state_callback, Run(SetUpUrlForwarderResponse::FAILED))
.WillOnce([&]() {
url_forwarder_configurator_->IsUrlForwarderSetUp(
is_set_up_callback.Get());
});
EXPECT_CALL(is_set_up_callback, Run(false))
.WillOnce(InvokeWithoutArgs(
this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment));
}
// Run the message loop until the desktop is attached.
setup_run_loop_->Run();
url_forwarder_configurator_->IsUrlForwarderSetUp(is_set_up_callback.Get());
// Run now rather than in TearDown() so that we can verify |callback|.
RunMainLoopUntilDone();
}
} // namespace remoting