blob: 13d80b19df2b240302d0490abfe7636851c9853b [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 "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop_proxy.h"
#include "remoting/jingle_glue/mock_objects.h"
#include "remoting/host/capturer_fake.h"
#include "remoting/host/chromoting_host.h"
#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/host_mock_objects.h"
#include "remoting/host/in_memory_host_config.h"
#include "remoting/host/it2me_host_user_interface.h"
#include "remoting/proto/video.pb.h"
#include "remoting/protocol/protocol_mock_objects.h"
#include "remoting/protocol/session_config.h"
#include "testing/gmock_mutant.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::remoting::protocol::MockClientStub;
using ::remoting::protocol::MockConnectionToClient;
using ::remoting::protocol::MockConnectionToClientEventHandler;
using ::remoting::protocol::MockHostStub;
using ::remoting::protocol::MockSession;
using ::remoting::protocol::MockVideoStub;
using ::remoting::protocol::SessionConfig;
using testing::_;
using testing::AnyNumber;
using testing::AtLeast;
using testing::CreateFunctor;
using testing::DeleteArg;
using testing::DoAll;
using testing::InSequence;
using testing::InvokeArgument;
using testing::InvokeWithoutArgs;
using testing::Return;
using testing::ReturnRef;
using testing::Sequence;
namespace remoting {
namespace {
void PostQuitTask(MessageLoop* message_loop) {
message_loop->PostTask(FROM_HERE, MessageLoop::QuitClosure());
}
// Run the task and delete it afterwards. This action is used to deal with
// done callbacks.
ACTION(RunDoneTask) {
arg1.Run();
}
ACTION_P(QuitMainMessageLoop, message_loop) {
PostQuitTask(message_loop);
}
void DummyDoneTask() {
}
} // namespace
class ChromotingHostTest : public testing::Test {
public:
ChromotingHostTest() {
}
virtual void SetUp() OVERRIDE {
message_loop_proxy_ = base::MessageLoopProxy::current();
config_ = new InMemoryHostConfig();
ON_CALL(context_, main_message_loop())
.WillByDefault(Return(&message_loop_));
ON_CALL(context_, encode_message_loop())
.WillByDefault(Return(&message_loop_));
ON_CALL(context_, network_message_loop())
.WillByDefault(Return(message_loop_proxy_.get()));
ON_CALL(context_, ui_message_loop())
.WillByDefault(Return(message_loop_proxy_.get()));
EXPECT_CALL(context_, main_message_loop())
.Times(AnyNumber());
EXPECT_CALL(context_, encode_message_loop())
.Times(AnyNumber());
EXPECT_CALL(context_, network_message_loop())
.Times(AnyNumber());
EXPECT_CALL(context_, ui_message_loop())
.Times(AnyNumber());
scoped_ptr<Capturer> capturer(new CapturerFake());
event_executor_ = new MockEventExecutor();
desktop_environment_ = DesktopEnvironment::CreateFake(
&context_,
capturer.Pass(),
scoped_ptr<protocol::HostEventStub>(event_executor_));
host_ = new ChromotingHost(
&context_, &signal_strategy_, desktop_environment_.get(),
protocol::NetworkSettings());
disconnect_window_ = new MockDisconnectWindow();
continue_window_ = new MockContinueWindow();
local_input_monitor_ = new MockLocalInputMonitor();
it2me_host_user_interface_.reset(new It2MeHostUserInterface(host_,
&context_));
it2me_host_user_interface_->InitFrom(
scoped_ptr<DisconnectWindow>(disconnect_window_),
scoped_ptr<ContinueWindow>(continue_window_),
scoped_ptr<LocalInputMonitor>(local_input_monitor_));
session_ = new MockSession();
session2_ = new MockSession();
session_config_ = SessionConfig::GetDefault();
session_jid_ = "user@domain/rest-of-jid";
session_config2_ = SessionConfig::GetDefault();
session2_jid_ = "user2@domain/rest-of-jid";
EXPECT_CALL(*session_, jid())
.WillRepeatedly(ReturnRef(session_jid_));
EXPECT_CALL(*session2_, jid())
.WillRepeatedly(ReturnRef(session2_jid_));
EXPECT_CALL(*session_, SetStateChangeCallback(_))
.Times(AnyNumber());
EXPECT_CALL(*session2_, SetStateChangeCallback(_))
.Times(AnyNumber());
EXPECT_CALL(*session_, config())
.WillRepeatedly(ReturnRef(session_config_));
EXPECT_CALL(*session2_, config())
.WillRepeatedly(ReturnRef(session_config2_));
EXPECT_CALL(*session_, Close())
.Times(AnyNumber());
EXPECT_CALL(*session2_, Close())
.Times(AnyNumber());
owned_connection_.reset(new MockConnectionToClient(
session_, &host_stub_, event_executor_));
connection_ = owned_connection_.get();
owned_connection2_.reset(new MockConnectionToClient(
session2_, &host_stub2_, &event_executor2_));
connection2_ = owned_connection2_.get();
ON_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.WillByDefault(DeleteArg<0>());
ON_CALL(video_stub2_, ProcessVideoPacketPtr(_, _))
.WillByDefault(DeleteArg<0>());
ON_CALL(*connection_, video_stub())
.WillByDefault(Return(&video_stub_));
ON_CALL(*connection_, client_stub())
.WillByDefault(Return(&client_stub_));
ON_CALL(*connection_, session())
.WillByDefault(Return(session_));
ON_CALL(*connection2_, video_stub())
.WillByDefault(Return(&video_stub2_));
ON_CALL(*connection2_, client_stub())
.WillByDefault(Return(&client_stub2_));
ON_CALL(*connection2_, session())
.WillByDefault(Return(session2_));
EXPECT_CALL(*connection_, video_stub())
.Times(AnyNumber());
EXPECT_CALL(*connection_, client_stub())
.Times(AnyNumber());
EXPECT_CALL(*connection_, session())
.Times(AnyNumber());
EXPECT_CALL(*connection2_, video_stub())
.Times(AnyNumber());
EXPECT_CALL(*connection2_, client_stub())
.Times(AnyNumber());
EXPECT_CALL(*connection2_, session())
.Times(AnyNumber());
}
virtual void TearDown() OVERRIDE {
owned_connection_.reset();
owned_connection2_.reset();
host_ = NULL;
// Run message loop before destroying because protocol::Session is
// destroyed asynchronously.
message_loop_.RunAllPending();
}
// Helper method to pretend a client is connected to ChromotingHost.
void SimulateClientConnection(int connection_index, bool authenticate) {
scoped_ptr<protocol::ConnectionToClient> connection =
((connection_index == 0) ? owned_connection_ : owned_connection2_).
PassAs<protocol::ConnectionToClient>();
protocol::ConnectionToClient* connection_ptr = connection.get();
ClientSession* client = new ClientSession(
host_.get(), connection.Pass(), event_executor_,
desktop_environment_->capturer());
connection->set_host_stub(client);
context_.network_message_loop()->PostTask(
FROM_HERE, base::Bind(&ChromotingHostTest::AddClientToHost,
host_, client));
if (authenticate) {
context_.network_message_loop()->PostTask(
FROM_HERE, base::Bind(&ClientSession::OnConnectionAuthenticated,
base::Unretained(client), connection_ptr));
context_.network_message_loop()->PostTask(
FROM_HERE,
base::Bind(&ClientSession::OnConnectionChannelsConnected,
base::Unretained(client), connection_ptr));
}
if (connection_index == 0) {
client_ = client;
} else {
client2_ = client;
}
}
// Helper method to remove a client connection from ChromotingHost.
void RemoveClientSession() {
client_->OnConnectionClosed(connection_, protocol::OK);
}
static void AddClientToHost(scoped_refptr<ChromotingHost> host,
ClientSession* session) {
host->clients_.push_back(session);
}
void ShutdownHost() {
message_loop_.PostTask(
FROM_HERE, base::Bind(&ChromotingHost::Shutdown, host_,
base::Bind(&PostQuitTask, &message_loop_)));
}
protected:
MessageLoop message_loop_;
scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
MockConnectionToClientEventHandler handler_;
MockSignalStrategy signal_strategy_;
scoped_ptr<DesktopEnvironment> desktop_environment_;
scoped_ptr<It2MeHostUserInterface> it2me_host_user_interface_;
scoped_refptr<ChromotingHost> host_;
scoped_refptr<InMemoryHostConfig> config_;
MockChromotingHostContext context_;
MockConnectionToClient* connection_;
scoped_ptr<MockConnectionToClient> owned_connection_;
ClientSession* client_;
std::string session_jid_;
MockSession* session_; // Owned by |connection_|.
SessionConfig session_config_;
MockVideoStub video_stub_;
MockClientStub client_stub_;
MockHostStub host_stub_;
MockConnectionToClient* connection2_;
scoped_ptr<MockConnectionToClient> owned_connection2_;
ClientSession* client2_;
std::string session2_jid_;
MockSession* session2_; // Owned by |connection2_|.
SessionConfig session_config2_;
MockVideoStub video_stub2_;
MockClientStub client_stub2_;
MockHostStub host_stub2_;
MockEventExecutor event_executor2_;
// Owned by |host_|.
MockEventExecutor* event_executor_;
MockDisconnectWindow* disconnect_window_;
MockContinueWindow* continue_window_;
MockLocalInputMonitor* local_input_monitor_;
};
TEST_F(ChromotingHostTest, DISABLED_StartAndShutdown) {
host_->Start();
message_loop_.PostTask(
FROM_HERE, base::Bind(
&ChromotingHost::Shutdown, host_.get(),
base::Bind(&PostQuitTask, &message_loop_)));
message_loop_.Run();
}
TEST_F(ChromotingHostTest, DISABLED_Connect) {
host_->Start();
// When the video packet is received we first shutdown ChromotingHost
// then execute the done task.
{
InSequence s;
EXPECT_CALL(*disconnect_window_, Show(_, _))
.Times(0);
EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.WillOnce(DoAll(
InvokeWithoutArgs(this, &ChromotingHostTest::ShutdownHost),
RunDoneTask()))
.RetiresOnSaturation();
EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.Times(AnyNumber());
EXPECT_CALL(*connection_, Disconnect())
.RetiresOnSaturation();
}
SimulateClientConnection(0, true);
message_loop_.Run();
}
TEST_F(ChromotingHostTest, DISABLED_Reconnect) {
host_->Start();
// When the video packet is received we first disconnect the mock
// connection.
{
InSequence s;
EXPECT_CALL(*disconnect_window_, Show(_, _))
.Times(0);
EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.WillOnce(DoAll(
InvokeWithoutArgs(this, &ChromotingHostTest::RemoveClientSession),
RunDoneTask()))
.RetiresOnSaturation();
EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.Times(AnyNumber());
EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.Times(AnyNumber());
}
// If Disconnect() is called we can break the main message loop.
EXPECT_CALL(*connection_, Disconnect())
.WillOnce(QuitMainMessageLoop(&message_loop_))
.RetiresOnSaturation();
SimulateClientConnection(0, true);
message_loop_.Run();
// Connect the client again.
{
InSequence s;
EXPECT_CALL(*disconnect_window_, Show(_, _))
.Times(0);
EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.WillOnce(DoAll(
InvokeWithoutArgs(this, &ChromotingHostTest::ShutdownHost),
RunDoneTask()))
.RetiresOnSaturation();
EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.Times(AnyNumber());
}
EXPECT_CALL(*connection_, Disconnect())
.RetiresOnSaturation();
SimulateClientConnection(0, true);
message_loop_.Run();
}
TEST_F(ChromotingHostTest, DISABLED_ConnectTwice) {
host_->Start();
// When a video packet is received we connect the second mock
// connection.
{
InSequence s;
EXPECT_CALL(*disconnect_window_, Show(_, _))
.Times(0);
EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.WillOnce(DoAll(
InvokeWithoutArgs(
CreateFunctor(
this,
&ChromotingHostTest::SimulateClientConnection, 1, true)),
RunDoneTask()))
.RetiresOnSaturation();
EXPECT_CALL(*disconnect_window_, Show(_, _))
.Times(0);
EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _))
.Times(AnyNumber());
EXPECT_CALL(video_stub2_, ProcessVideoPacketPtr(_, _))
.WillOnce(DoAll(
InvokeWithoutArgs(this, &ChromotingHostTest::ShutdownHost),
RunDoneTask()))
.RetiresOnSaturation();
EXPECT_CALL(video_stub2_, ProcessVideoPacketPtr(_, _))
.Times(AnyNumber());
}
EXPECT_CALL(*connection_, Disconnect())
.RetiresOnSaturation();
EXPECT_CALL(*connection2_, Disconnect())
.RetiresOnSaturation();
SimulateClientConnection(0, true);
message_loop_.Run();
}
} // namespace remoting