blob: 696f83f6b911f60fcc16378556fc22e864e94275 [file] [log] [blame]
// Copyright (c) 2012 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 <algorithm>
#include <string>
#include <vector>
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/test/test_simple_task_runner.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/base/constants.h"
#include "remoting/host/audio_capturer.h"
#include "remoting/host/client_session.h"
#include "remoting/host/desktop_environment.h"
#include "remoting/host/fake_desktop_capturer.h"
#include "remoting/host/fake_host_extension.h"
#include "remoting/host/fake_mouse_cursor_monitor.h"
#include "remoting/host/host_extension.h"
#include "remoting/host/host_extension_session.h"
#include "remoting/host/host_mock_objects.h"
#include "remoting/protocol/protocol_mock_objects.h"
#include "remoting/protocol/test_event_matchers.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock_mutant.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"
namespace remoting {
using protocol::MockClientStub;
using protocol::MockConnectionToClient;
using protocol::MockHostStub;
using protocol::MockInputStub;
using protocol::MockSession;
using protocol::MockVideoStub;
using protocol::SessionConfig;
using protocol::test::EqualsClipboardEvent;
using protocol::test::EqualsMouseButtonEvent;
using protocol::test::EqualsMouseMoveEvent;
using protocol::test::EqualsKeyEvent;
using testing::_;
using testing::AnyNumber;
using testing::AtMost;
using testing::AtLeast;
using testing::CreateFunctor;
using testing::DeleteArg;
using testing::DoAll;
using testing::Expectation;
using testing::Invoke;
using testing::Return;
using testing::ReturnRef;
using testing::Sequence;
using testing::StrEq;
using testing::StrictMock;
namespace {
const char kDefaultTestCapability[] = "default";
ACTION_P2(InjectClipboardEvent, connection, event) {
connection->clipboard_stub()->InjectClipboardEvent(event);
}
ACTION_P2(InjectKeyEvent, connection, event) {
connection->input_stub()->InjectKeyEvent(event);
}
ACTION_P2(InjectMouseEvent, connection, event) {
connection->input_stub()->InjectMouseEvent(event);
}
ACTION_P2(LocalMouseMoved, client_session, event) {
client_session->OnLocalMouseMoved(
webrtc::DesktopVector(event.x(), event.y()));
}
ACTION_P2(SetGnubbyAuthHandlerForTesting, client_session, gnubby_auth_handler) {
client_session->SetGnubbyAuthHandlerForTesting(gnubby_auth_handler);
}
ACTION_P2(DeliverClientMessage, client_session, message) {
client_session->DeliverClientMessage(message);
}
ACTION_P2(SetCapabilities, client_session, capabilities) {
protocol::Capabilities capabilities_message;
capabilities_message.set_capabilities(capabilities);
client_session->SetCapabilities(capabilities_message);
}
// Matches a |protocol::Capabilities| argument against a list of capabilities
// formatted as a space-separated string.
MATCHER_P(EqCapabilities, expected_capabilities, "") {
if (!arg.has_capabilities())
return false;
std::vector<std::string> words_args = base::SplitString(
arg.capabilities(), " ", base::KEEP_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
std::vector<std::string> words_expected = base::SplitString(
expected_capabilities, " ", base::KEEP_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
std::sort(words_args.begin(), words_args.end());
std::sort(words_expected.begin(), words_expected.end());
return words_args == words_expected;
}
} // namespace
class ClientSessionTest : public testing::Test {
public:
ClientSessionTest() : client_jid_("user@domain/rest-of-jid") {}
void SetUp() override;
void TearDown() override;
// Creates the client session.
void CreateClientSession();
// Disconnects the client session.
void DisconnectClientSession();
// Stops and releases the ClientSession, allowing the MessageLoop to quit.
void StopClientSession();
protected:
// Creates a DesktopEnvironment with a fake webrtc::DesktopCapturer, to mock
// DesktopEnvironmentFactory::Create().
DesktopEnvironment* CreateDesktopEnvironment();
// Returns |input_injector_| created and initialized by SetUp(), 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();
// Notifies the client session that the client connection has been
// authenticated and channels have been connected. This effectively enables
// the input pipe line and starts video capturing.
void ConnectClientSession();
// Creates expectation that simulates client supporting same capabilities as
// host.
void SetMatchCapabilitiesExpectation();
// Creates expectations to send an extension message and to disconnect
// afterwards.
void SetSendMessageAndDisconnectExpectation(const std::string& message_type);
// Message loop that will process all ClientSession tasks.
base::MessageLoop message_loop_;
// AutoThreadTaskRunner on which |client_session_| will be run.
scoped_refptr<AutoThreadTaskRunner> task_runner_;
// Used to run |message_loop_| after each test, until no objects remain that
// require it.
base::RunLoop run_loop_;
// HostExtensions to pass when creating the ClientSession. Caller retains
// ownership of the HostExtensions themselves.
std::vector<HostExtension*> extensions_;
// ClientSession instance under test.
scoped_ptr<ClientSession> client_session_;
// ClientSession::EventHandler mock for use in tests.
MockClientSessionEventHandler session_event_handler_;
// Storage for values to be returned by the protocol::Session mock.
scoped_ptr<SessionConfig> session_config_;
const std::string client_jid_;
// Stubs returned to |client_session_| components by |connection_|.
MockClientStub client_stub_;
MockVideoStub video_stub_;
// DesktopEnvironment owns |input_injector_|, but input injection tests need
// to express expectations on it.
scoped_ptr<MockInputInjector> input_injector_;
// ClientSession owns |connection_| but tests need it to inject fake events.
MockConnectionToClient* connection_;
scoped_ptr<MockDesktopEnvironmentFactory> desktop_environment_factory_;
};
void ClientSessionTest::SetUp() {
// Arrange to run |message_loop_| until no components depend on it.
task_runner_ = new AutoThreadTaskRunner(
message_loop_.task_runner(), run_loop_.QuitClosure());
desktop_environment_factory_.reset(new MockDesktopEnvironmentFactory());
EXPECT_CALL(*desktop_environment_factory_, CreatePtr())
.Times(AnyNumber())
.WillRepeatedly(Invoke(this,
&ClientSessionTest::CreateDesktopEnvironment));
EXPECT_CALL(*desktop_environment_factory_, SupportsAudioCapture())
.Times(AnyNumber())
.WillRepeatedly(Return(false));
input_injector_.reset(new MockInputInjector());
session_config_ = SessionConfig::ForTest();
}
void ClientSessionTest::TearDown() {
// Clear out |task_runner_| reference so the loop can quit, and run it until
// it does.
task_runner_ = nullptr;
run_loop_.Run();
}
void ClientSessionTest::CreateClientSession() {
// Mock protocol::Session APIs called directly by ClientSession.
protocol::MockSession* session = new MockSession();
EXPECT_CALL(*session, config()).WillRepeatedly(ReturnRef(*session_config_));
EXPECT_CALL(*session, jid()).WillRepeatedly(ReturnRef(client_jid_));
EXPECT_CALL(*session, SetEventHandler(_));
// Mock protocol::ConnectionToClient APIs called directly by ClientSession.
// HostStub is not touched by ClientSession, so we can safely pass nullptr.
scoped_ptr<MockConnectionToClient> connection(
new MockConnectionToClient(session, nullptr));
EXPECT_CALL(*connection, session()).WillRepeatedly(Return(session));
EXPECT_CALL(*connection, client_stub())
.WillRepeatedly(Return(&client_stub_));
EXPECT_CALL(*connection, video_stub()).WillRepeatedly(Return(&video_stub_));
EXPECT_CALL(*connection, Disconnect());
connection_ = connection.get();
client_session_.reset(new ClientSession(
&session_event_handler_,
task_runner_, // Audio thread.
task_runner_, // Input thread.
task_runner_, // Capture thread.
task_runner_, // Encode thread.
task_runner_, // Network thread.
task_runner_, // UI thread.
connection.Pass(),
desktop_environment_factory_.get(),
base::TimeDelta(),
nullptr,
extensions_));
}
void ClientSessionTest::DisconnectClientSession() {
client_session_->DisconnectSession();
// MockSession won't trigger OnConnectionClosed, so fake it.
client_session_->OnConnectionClosed(client_session_->connection(),
protocol::OK);
}
void ClientSessionTest::StopClientSession() {
client_session_.reset();
desktop_environment_factory_.reset();
}
DesktopEnvironment* ClientSessionTest::CreateDesktopEnvironment() {
MockDesktopEnvironment* desktop_environment = new MockDesktopEnvironment();
EXPECT_CALL(*desktop_environment, CreateAudioCapturerPtr())
.Times(0);
EXPECT_CALL(*desktop_environment, CreateInputInjectorPtr())
.WillOnce(Invoke(this, &ClientSessionTest::CreateInputInjector));
EXPECT_CALL(*desktop_environment, CreateScreenControlsPtr())
.Times(AtMost(1));
EXPECT_CALL(*desktop_environment, CreateVideoCapturerPtr())
.WillRepeatedly(Invoke(this, &ClientSessionTest::CreateVideoCapturer));
EXPECT_CALL(*desktop_environment, CreateMouseCursorMonitorPtr())
.WillRepeatedly(
Invoke(this, &ClientSessionTest::CreateMouseCursorMonitor));
EXPECT_CALL(*desktop_environment, GetCapabilities())
.Times(AtMost(1))
.WillOnce(Return(kDefaultTestCapability));
EXPECT_CALL(*desktop_environment, SetCapabilities(_))
.Times(AtMost(1));
return desktop_environment;
}
InputInjector* ClientSessionTest::CreateInputInjector() {
EXPECT_TRUE(input_injector_);
return input_injector_.release();
}
webrtc::DesktopCapturer* ClientSessionTest::CreateVideoCapturer() {
return new FakeDesktopCapturer();
}
webrtc::MouseCursorMonitor* ClientSessionTest::CreateMouseCursorMonitor() {
return new FakeMouseCursorMonitor();
}
void ClientSessionTest::ConnectClientSession() {
// Stubs should be set only after connection is authenticated.
EXPECT_FALSE(connection_->clipboard_stub());
EXPECT_FALSE(connection_->input_stub());
client_session_->OnConnectionAuthenticated(client_session_->connection());
EXPECT_TRUE(connection_->clipboard_stub());
EXPECT_TRUE(connection_->input_stub());
client_session_->OnConnectionChannelsConnected(client_session_->connection());
}
void ClientSessionTest::SetMatchCapabilitiesExpectation() {
// Set the client to report the same capabilities as the host.
EXPECT_CALL(client_stub_, SetCapabilities(_))
.Times(AtMost(1))
.WillOnce(Invoke(client_session_.get(), &ClientSession::SetCapabilities));
}
void ClientSessionTest::SetSendMessageAndDisconnectExpectation(
const std::string& message_type) {
protocol::ExtensionMessage message;
message.set_type(message_type);
message.set_data("data");
Expectation authenticated =
EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_))
.WillOnce(Return(true));
EXPECT_CALL(session_event_handler_, OnSessionChannelsConnected(_))
.After(authenticated)
.WillOnce(DoAll(
DeliverClientMessage(client_session_.get(), message),
InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession),
InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession)));
}
TEST_F(ClientSessionTest, ClipboardStubFilter) {
CreateClientSession();
EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_))
.WillOnce(Return(true));
EXPECT_CALL(*input_injector_, StartPtr(_));
EXPECT_CALL(session_event_handler_, OnSessionChannelsConnected(_));
// Wait for the first video packet to be captured to make sure that
// the injected input will go though. Otherwise mouse events will be blocked
// by the mouse clamping filter.
base::RunLoop run_loop;
EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.Times(AtLeast(1))
.WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
{
EXPECT_CALL(*input_injector_, InjectClipboardEvent(EqualsClipboardEvent(
kMimeTypeTextUtf8, "a")));
EXPECT_CALL(*input_injector_, InjectKeyEvent(EqualsKeyEvent(1, true)));
EXPECT_CALL(*input_injector_, InjectKeyEvent(EqualsKeyEvent(1, false)));
EXPECT_CALL(*input_injector_,
InjectMouseEvent(EqualsMouseMoveEvent(100, 101)));
EXPECT_CALL(*input_injector_, InjectClipboardEvent(EqualsClipboardEvent(
kMimeTypeTextUtf8, "c")));
EXPECT_CALL(*input_injector_, InjectKeyEvent(EqualsKeyEvent(3, true)));
EXPECT_CALL(*input_injector_,
InjectMouseEvent(EqualsMouseMoveEvent(300, 301)));
EXPECT_CALL(*input_injector_, InjectKeyEvent(EqualsKeyEvent(3, false)));
}
ConnectClientSession();
// Wait for the first frame.
run_loop.Run();
// Inject test events that are expected to be injected.
protocol::ClipboardEvent clipboard_event;
clipboard_event.set_mime_type(kMimeTypeTextUtf8);
clipboard_event.set_data("a");
connection_->clipboard_stub()->InjectClipboardEvent(clipboard_event);
protocol::KeyEvent key_event;
key_event.set_pressed(true);
key_event.set_usb_keycode(1);
connection_->input_stub()->InjectKeyEvent(key_event);
protocol::MouseEvent mouse_event;
mouse_event.set_x(100);
mouse_event.set_y(101);
connection_->input_stub()->InjectMouseEvent(mouse_event);
base::RunLoop().RunUntilIdle();
// Disable input.
client_session_->SetDisableInputs(true);
// These event shouldn't get though to the input injector.
clipboard_event.set_data("b");
connection_->clipboard_stub()->InjectClipboardEvent(clipboard_event);
key_event.set_pressed(true);
key_event.set_usb_keycode(2);
connection_->input_stub()->InjectKeyEvent(key_event);
key_event.set_pressed(false);
key_event.set_usb_keycode(2);
connection_->input_stub()->InjectKeyEvent(key_event);
mouse_event.set_x(200);
mouse_event.set_y(201);
connection_->input_stub()->InjectMouseEvent(mouse_event);
base::RunLoop().RunUntilIdle();
// Enable input again.
client_session_->SetDisableInputs(false);
clipboard_event.set_data("c");
connection_->clipboard_stub()->InjectClipboardEvent(clipboard_event);
base::RunLoop().RunUntilIdle();
key_event.set_pressed(true);
key_event.set_usb_keycode(3);
connection_->input_stub()->InjectKeyEvent(key_event);
mouse_event.set_x(300);
mouse_event.set_y(301);
connection_->input_stub()->InjectMouseEvent(mouse_event);
client_session_->DisconnectSession();
client_session_->OnConnectionClosed(connection_, protocol::OK);
client_session_.reset();
}
TEST_F(ClientSessionTest, LocalInputTest) {
CreateClientSession();
protocol::MouseEvent mouse_event1;
mouse_event1.set_x(100);
mouse_event1.set_y(101);
protocol::MouseEvent mouse_event2;
mouse_event2.set_x(200);
mouse_event2.set_y(201);
protocol::MouseEvent mouse_event3;
mouse_event3.set_x(300);
mouse_event3.set_y(301);
Expectation authenticated =
EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_))
.WillOnce(Return(true));
EXPECT_CALL(*input_injector_, StartPtr(_))
.After(authenticated);
EXPECT_CALL(session_event_handler_, OnSessionChannelsConnected(_))
.After(authenticated);
// Wait for the first video packet to be captured to make sure that
// the injected input will go though. Otherwise mouse events will be blocked
// by the mouse clamping filter.
Sequence s;
EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.InSequence(s)
.After(authenticated)
.WillOnce(DoAll(
// This event should get through to the input stub.
InjectMouseEvent(connection_, mouse_event1),
#if !defined(OS_WIN)
// The OS echoes the injected event back.
LocalMouseMoved(client_session_.get(), mouse_event1),
#endif // !defined(OS_WIN)
// This one should get throught as well.
InjectMouseEvent(connection_, mouse_event2),
// Now this is a genuine local event.
LocalMouseMoved(client_session_.get(), mouse_event1),
// This one should be blocked because of the previous local input
// event.
InjectMouseEvent(connection_, mouse_event3),
// TODO(jamiewalch): Verify that remote inputs are re-enabled
// eventually (via dependency injection, not sleep!)
InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession),
InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession)));
EXPECT_CALL(*input_injector_,
InjectMouseEvent(EqualsMouseMoveEvent(100, 101))).InSequence(s);
EXPECT_CALL(*input_injector_,
InjectMouseEvent(EqualsMouseMoveEvent(200, 201))).InSequence(s);
EXPECT_CALL(session_event_handler_, OnSessionClosed(_)).InSequence(s);
ConnectClientSession();
}
TEST_F(ClientSessionTest, RestoreEventState) {
CreateClientSession();
protocol::KeyEvent key1;
key1.set_pressed(true);
key1.set_usb_keycode(1);
protocol::KeyEvent key2;
key2.set_pressed(true);
key2.set_usb_keycode(2);
protocol::MouseEvent mousedown;
mousedown.set_button(protocol::MouseEvent::BUTTON_LEFT);
mousedown.set_button_down(true);
Expectation authenticated =
EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_))
.WillOnce(Return(true));
EXPECT_CALL(*input_injector_, StartPtr(_)).After(authenticated);
EXPECT_CALL(session_event_handler_, OnSessionChannelsConnected(_))
.After(authenticated);
// Wait for the first video packet to be captured to make sure that
// the injected input will go though. Otherwise mouse events will be blocked
// by the mouse clamping filter.
Sequence s;
EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.InSequence(s)
.After(authenticated)
.WillOnce(DoAll(
InjectKeyEvent(connection_, key1), InjectKeyEvent(connection_, key2),
InjectMouseEvent(connection_, mousedown),
InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession),
InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession)));
EXPECT_CALL(*input_injector_, InjectKeyEvent(EqualsKeyEvent(1, true)))
.InSequence(s);
EXPECT_CALL(*input_injector_, InjectKeyEvent(EqualsKeyEvent(2, true)))
.InSequence(s);
EXPECT_CALL(*input_injector_, InjectMouseEvent(EqualsMouseButtonEvent(
protocol::MouseEvent::BUTTON_LEFT, true)))
.InSequence(s);
EXPECT_CALL(*input_injector_, InjectKeyEvent(EqualsKeyEvent(1, false)))
.InSequence(s);
EXPECT_CALL(*input_injector_, InjectKeyEvent(EqualsKeyEvent(2, false)))
.InSequence(s);
EXPECT_CALL(*input_injector_, InjectMouseEvent(EqualsMouseButtonEvent(
protocol::MouseEvent::BUTTON_LEFT, false)))
.InSequence(s);
EXPECT_CALL(session_event_handler_, OnSessionClosed(_)).InSequence(s);
ConnectClientSession();
}
TEST_F(ClientSessionTest, ClampMouseEvents) {
CreateClientSession();
Expectation authenticated =
EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_))
.WillOnce(Return(true));
EXPECT_CALL(*input_injector_, StartPtr(_)).After(authenticated);
EXPECT_CALL(session_event_handler_, OnSessionChannelsConnected(_))
.After(authenticated);
EXPECT_CALL(session_event_handler_, OnSessionClosed(_)).After(authenticated);
Expectation connected = authenticated;
int input_x[3] = { -999, 100, 999 };
int expected_x[3] = { 0, 100, FakeDesktopCapturer::kWidth - 1 };
int input_y[3] = { -999, 50, 999 };
int expected_y[3] = { 0, 50, FakeDesktopCapturer::kHeight - 1 };
protocol::MouseEvent expected_event;
for (int j = 0; j < 3; j++) {
for (int i = 0; i < 3; i++) {
protocol::MouseEvent injected_event;
injected_event.set_x(input_x[i]);
injected_event.set_y(input_y[j]);
if (i == 0 && j == 0) {
// Inject the 1st event once a video packet has been received.
connected =
EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.After(connected)
.WillOnce(InjectMouseEvent(connection_, injected_event));
} else {
// Every next event is injected once the previous event has been
// received.
connected =
EXPECT_CALL(*input_injector_,
InjectMouseEvent(EqualsMouseMoveEvent(
expected_event.x(), expected_event.y())))
.After(connected)
.WillOnce(InjectMouseEvent(connection_, injected_event));
}
expected_event.set_x(expected_x[i]);
expected_event.set_y(expected_y[j]);
}
}
// Shutdown the connection once the last event has been received.
EXPECT_CALL(*input_injector_, InjectMouseEvent(EqualsMouseMoveEvent(
expected_event.x(), expected_event.y())))
.After(connected)
.WillOnce(DoAll(
InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession),
InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession)));
ConnectClientSession();
}
TEST_F(ClientSessionTest, NoGnubbyAuth) {
CreateClientSession();
protocol::ExtensionMessage message;
message.set_type("gnubby-auth");
message.set_data("test");
Expectation authenticated =
EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_))
.WillOnce(Return(true));
EXPECT_CALL(*input_injector_, StartPtr(_)).After(authenticated);
EXPECT_CALL(session_event_handler_, OnSessionChannelsConnected(_))
.After(authenticated)
.WillOnce(DoAll(
DeliverClientMessage(client_session_.get(), message),
InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession),
InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession)));
EXPECT_CALL(session_event_handler_, OnSessionClosed(_));
ConnectClientSession();
}
TEST_F(ClientSessionTest, EnableGnubbyAuth) {
CreateClientSession();
// Lifetime controlled by object under test.
MockGnubbyAuthHandler* gnubby_auth_handler = new MockGnubbyAuthHandler();
protocol::ExtensionMessage message;
message.set_type("gnubby-auth");
message.set_data("test");
Expectation authenticated =
EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_))
.WillOnce(Return(true));
EXPECT_CALL(*input_injector_, StartPtr(_)).After(authenticated);
EXPECT_CALL(session_event_handler_, OnSessionChannelsConnected(_))
.After(authenticated)
.WillOnce(DoAll(
SetGnubbyAuthHandlerForTesting(client_session_.get(),
gnubby_auth_handler),
DeliverClientMessage(client_session_.get(), message),
InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession),
InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession)));
EXPECT_CALL(*gnubby_auth_handler, DeliverClientMessage(_));
EXPECT_CALL(session_event_handler_, OnSessionClosed(_));
ConnectClientSession();
}
// Verifies that the client's video pipeline can be reset mid-session.
TEST_F(ClientSessionTest, ResetVideoPipeline) {
CreateClientSession();
EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_))
.WillOnce(Return(true));
EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.WillOnce(DoAll(
InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession),
InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession)));
ConnectClientSession();
client_session_->ResetVideoPipeline();
}
// Verifies that clients can have extensions registered, resulting in the
// correct capabilities being reported, and messages delivered correctly.
// The extension system is tested more extensively in the
// HostExtensionSessionManager unit-tests.
TEST_F(ClientSessionTest, Extensions) {
// Configure fake extensions for testing.
FakeExtension extension1("ext1", "cap1");
extensions_.push_back(&extension1);
FakeExtension extension2("ext2", "");
extensions_.push_back(&extension2);
FakeExtension extension3("ext3", "cap3");
extensions_.push_back(&extension3);
// Set the second extension to request to modify the video pipeline.
extension2.set_steal_video_capturer(true);
CreateClientSession();
Expectation authenticated =
EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_))
.WillOnce(Return(true));
// Verify that the ClientSession reports the correct capabilities, and mimic
// the client reporting an overlapping set of capabilities.
EXPECT_CALL(client_stub_,
SetCapabilities(EqCapabilities("cap1 cap3 default")))
.After(authenticated)
.WillOnce(SetCapabilities(client_session_.get(), "cap1 cap4 default"));
// Verify that the correct extension messages are delivered, and dropped.
protocol::ExtensionMessage message1;
message1.set_type("ext1");
message1.set_data("data");
protocol::ExtensionMessage message3;
message3.set_type("ext3");
message3.set_data("data");
protocol::ExtensionMessage message4;
message4.set_type("ext4");
message4.set_data("data");
EXPECT_CALL(session_event_handler_, OnSessionChannelsConnected(_))
.WillOnce(DoAll(
DeliverClientMessage(client_session_.get(), message1),
DeliverClientMessage(client_session_.get(), message3),
DeliverClientMessage(client_session_.get(), message4),
InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession),
InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession)));
// Simulate the ClientSession connect and extension negotiation.
ConnectClientSession();
base::RunLoop().RunUntilIdle();
// ext1 was instantiated and sent a message, and did not wrap anything.
EXPECT_TRUE(extension1.was_instantiated());
EXPECT_TRUE(extension1.has_handled_message());
EXPECT_FALSE(extension1.has_wrapped_video_encoder());
// ext2 was instantiated but not sent a message, and wrapped video encoder.
EXPECT_TRUE(extension2.was_instantiated());
EXPECT_FALSE(extension2.has_handled_message());
EXPECT_TRUE(extension2.has_wrapped_video_encoder());
// ext3 was sent a message but not instantiated.
EXPECT_FALSE(extension3.was_instantiated());
}
// Verifies that an extension can "steal" the video capture, in which case no
// VideoFramePump is instantiated.
TEST_F(ClientSessionTest, StealVideoCapturer) {
FakeExtension extension("ext1", "cap1");
extensions_.push_back(&extension);
CreateClientSession();
SetMatchCapabilitiesExpectation();
EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_))
.WillOnce(Return(true));
ConnectClientSession();
base::RunLoop().RunUntilIdle();
extension.set_steal_video_capturer(true);
client_session_->ResetVideoPipeline();
base::RunLoop().RunUntilIdle();
// Verify that video control messages received while there is no video
// scheduler active won't crash things.
protocol::VideoControl video_control;
video_control.set_enable(false);
video_control.set_lossless_encode(true);
video_control.set_lossless_color(true);
client_session_->ControlVideo(video_control);
// TODO(wez): Find a way to verify that the ClientSession never captures any
// frames in this case.
DisconnectClientSession();
StopClientSession();
// ext1 was instantiated and wrapped the video capturer.
EXPECT_TRUE(extension.was_instantiated());
EXPECT_TRUE(extension.has_wrapped_video_capturer());
}
} // namespace remoting