普通文本  |  710行  |  25.87 KB

// 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/bind_helpers.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop_proxy.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/host/audio_capturer.h"
#include "remoting/host/chromoting_host.h"
#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/desktop_environment.h"
#include "remoting/host/host_mock_objects.h"
#include "remoting/host/screen_capturer_fake.h"
#include "remoting/jingle_glue/mock_objects.h"
#include "remoting/proto/video.pb.h"
#include "remoting/protocol/errors.h"
#include "remoting/protocol/protocol_mock_objects.h"
#include "remoting/protocol/session_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gmock_mutant.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::Session;
using ::remoting::protocol::SessionConfig;

using testing::_;
using testing::AnyNumber;
using testing::AtLeast;
using testing::AtMost;
using testing::CreateFunctor;
using testing::DeleteArg;
using testing::DoAll;
using testing::Expectation;
using testing::InSequence;
using testing::Invoke;
using testing::InvokeArgument;
using testing::InvokeWithoutArgs;
using testing::Return;
using testing::ReturnRef;
using testing::SaveArg;
using testing::Sequence;

namespace remoting {

namespace {

void PostQuitTask(base::MessageLoop* message_loop) {
  message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
}

// Run the task and delete it afterwards. This action is used to deal with
// done callbacks.
ACTION(RunDoneTask) {
  arg1.Run();
}

}  // namespace

class ChromotingHostTest : public testing::Test {
 public:
  ChromotingHostTest() {
  }

  virtual void SetUp() OVERRIDE {
    task_runner_ = new AutoThreadTaskRunner(
        message_loop_.message_loop_proxy(),
        base::Bind(&ChromotingHostTest::QuitMainMessageLoop,
                   base::Unretained(this)));

    desktop_environment_factory_.reset(new MockDesktopEnvironmentFactory());
    EXPECT_CALL(*desktop_environment_factory_, CreatePtr())
        .Times(AnyNumber())
        .WillRepeatedly(Invoke(this,
                               &ChromotingHostTest::CreateDesktopEnvironment));
    EXPECT_CALL(*desktop_environment_factory_, SupportsAudioCapture())
        .Times(AnyNumber())
        .WillRepeatedly(Return(false));

    session_manager_ = new protocol::MockSessionManager();

    host_.reset(new ChromotingHost(
        &signal_strategy_,
        desktop_environment_factory_.get(),
        scoped_ptr<protocol::SessionManager>(session_manager_),
        task_runner_,   // Audio
        task_runner_,   // Input
        task_runner_,   // Video capture
        task_runner_,   // Video encode
        task_runner_,   // Network
        task_runner_)); // UI
    host_->AddStatusObserver(&host_status_observer_);

    xmpp_login_ = "host@domain";
    session1_ = new MockSession();
    session2_ = new MockSession();
    session_unowned1_.reset(new MockSession());
    session_unowned2_.reset(new MockSession());
    session_config1_ = SessionConfig::ForTest();
    session_jid1_ = "user@domain/rest-of-jid";
    session_config2_ = SessionConfig::ForTest();
    session_jid2_ = "user2@domain/rest-of-jid";
    session_unowned_config1_ = SessionConfig::ForTest();
    session_unowned_jid1_ = "user3@doman/rest-of-jid";
    session_unowned_config2_ = SessionConfig::ForTest();
    session_unowned_jid2_ = "user4@doman/rest-of-jid";

    EXPECT_CALL(*session1_, jid())
        .WillRepeatedly(ReturnRef(session_jid1_));
    EXPECT_CALL(*session2_, jid())
        .WillRepeatedly(ReturnRef(session_jid2_));
    EXPECT_CALL(*session_unowned1_, jid())
        .WillRepeatedly(ReturnRef(session_unowned_jid1_));
    EXPECT_CALL(*session_unowned2_, jid())
        .WillRepeatedly(ReturnRef(session_unowned_jid2_));
    EXPECT_CALL(*session1_, SetEventHandler(_))
        .Times(AnyNumber());
    EXPECT_CALL(*session2_, SetEventHandler(_))
        .Times(AnyNumber());
    EXPECT_CALL(*session_unowned1_, SetEventHandler(_))
        .Times(AnyNumber())
        .WillRepeatedly(SaveArg<0>(&session_unowned1_event_handler_));
    EXPECT_CALL(*session_unowned2_, SetEventHandler(_))
        .Times(AnyNumber())
        .WillRepeatedly(SaveArg<0>(&session_unowned2_event_handler_));
    EXPECT_CALL(*session1_, config())
        .WillRepeatedly(ReturnRef(session_config1_));
    EXPECT_CALL(*session2_, config())
        .WillRepeatedly(ReturnRef(session_config2_));

    owned_connection1_.reset(new MockConnectionToClient(session1_,
                                                        &host_stub1_));
    connection1_ = owned_connection1_.get();
    owned_connection2_.reset(new MockConnectionToClient(session2_,
                                                        &host_stub2_));
    connection2_ = owned_connection2_.get();

    ON_CALL(video_stub1_, ProcessVideoPacketPtr(_, _))
        .WillByDefault(DeleteArg<0>());
    ON_CALL(video_stub2_, ProcessVideoPacketPtr(_, _))
        .WillByDefault(DeleteArg<0>());
    ON_CALL(*connection1_, video_stub())
        .WillByDefault(Return(&video_stub1_));
    ON_CALL(*connection1_, client_stub())
        .WillByDefault(Return(&client_stub1_));
    ON_CALL(*connection1_, session())
        .WillByDefault(Return(session1_));
    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(*connection1_, video_stub())
        .Times(AnyNumber());
    EXPECT_CALL(*connection1_, client_stub())
        .Times(AnyNumber());
    EXPECT_CALL(*connection1_, session())
        .Times(AnyNumber());
    EXPECT_CALL(*connection2_, video_stub())
        .Times(AnyNumber());
    EXPECT_CALL(*connection2_, client_stub())
        .Times(AnyNumber());
    EXPECT_CALL(*connection2_, session())
        .Times(AnyNumber());

    empty_candidate_config_ =
        protocol::CandidateSessionConfig::CreateEmpty();
    default_candidate_config_ =
        protocol::CandidateSessionConfig::CreateDefault();
  }

  // Helper method to pretend a client is connected to ChromotingHost.
  void SimulateClientConnection(int connection_index, bool authenticate,
                                bool reject) {
    scoped_ptr<protocol::ConnectionToClient> connection =
        ((connection_index == 0) ? owned_connection1_ : owned_connection2_).
        PassAs<protocol::ConnectionToClient>();
    protocol::ConnectionToClient* connection_ptr = connection.get();
    scoped_ptr<ClientSession> client(new ClientSession(
        host_.get(),
        task_runner_, // Audio
        task_runner_, // Input
        task_runner_, // Video capture
        task_runner_, // Video encode
        task_runner_, // Network
        task_runner_, // UI
        connection.Pass(),
        desktop_environment_factory_.get(),
        base::TimeDelta(),
        NULL));

    connection_ptr->set_host_stub(client.get());

    if (authenticate) {
      task_runner_->PostTask(
          FROM_HERE,
          base::Bind(&ClientSession::OnConnectionAuthenticated,
                     base::Unretained(client.get()), connection_ptr));
      if (!reject) {
        task_runner_->PostTask(
            FROM_HERE,
            base::Bind(&ClientSession::OnConnectionChannelsConnected,
                       base::Unretained(client.get()), connection_ptr));
      }
    } else {
      task_runner_->PostTask(
          FROM_HERE, base::Bind(&ClientSession::OnConnectionClosed,
                                base::Unretained(client.get()), connection_ptr,
                                protocol::AUTHENTICATION_FAILED));
    }

    get_client(connection_index) = client.get();

    // |host| is responsible for deleting |client| from now on.
    host_->clients_.push_back(client.release());
  }

  virtual void TearDown() OVERRIDE {
    // Make sure that the host has been properly deleted.
    DCHECK(host_.get() == NULL);
  }

  // Change the session route for |client1_|.
  void ChangeSessionRoute(const std::string& channel_name,
                          const protocol::TransportRoute& route) {
    host_->OnSessionRouteChange(get_client(0), channel_name, route);
  }

  // Creates a DesktopEnvironment with a fake webrtc::ScreenCapturer, to mock
  // DesktopEnvironmentFactory::Create().
  DesktopEnvironment* CreateDesktopEnvironment() {
    MockDesktopEnvironment* desktop_environment = new MockDesktopEnvironment();
    EXPECT_CALL(*desktop_environment, CreateAudioCapturerPtr())
        .Times(0);
    EXPECT_CALL(*desktop_environment, CreateInputInjectorPtr())
        .Times(AtMost(1))
        .WillOnce(Invoke(this, &ChromotingHostTest::CreateInputInjector));
    EXPECT_CALL(*desktop_environment, CreateScreenControlsPtr())
        .Times(AtMost(1));
    EXPECT_CALL(*desktop_environment, CreateVideoCapturerPtr())
        .Times(AtMost(1))
        .WillOnce(Invoke(this, &ChromotingHostTest::CreateVideoCapturer));
    EXPECT_CALL(*desktop_environment, GetCapabilities())
        .Times(AtMost(1));
    EXPECT_CALL(*desktop_environment, SetCapabilities(_))
        .Times(AtMost(1));

    return desktop_environment;
  }

  // Creates a dummy InputInjector, to mock
  // DesktopEnvironment::CreateInputInjector().
  InputInjector* CreateInputInjector() {
    MockInputInjector* input_injector = new MockInputInjector();
    EXPECT_CALL(*input_injector, StartPtr(_));
    return input_injector;
  }

  // Creates a fake webrtc::ScreenCapturer, to mock
  // DesktopEnvironment::CreateVideoCapturer().
  webrtc::ScreenCapturer* CreateVideoCapturer() {
    return new ScreenCapturerFake();
  }

  void DisconnectAllClients() {
    host_->DisconnectAllClients();
  }

  // Helper method to disconnect client 1 from the host.
  void DisconnectClient1() {
    NotifyClientSessionClosed(0);
  }

  // Notify |host_| that the authenticating client has been rejected.
  void RejectAuthenticatingClient() {
    host_->RejectAuthenticatingClient();
  }

  // Notify |host_| that a client session has closed.
  void NotifyClientSessionClosed(int connection_index) {
    get_client(connection_index)->OnConnectionClosed(
        get_connection(connection_index), protocol::OK);
  }

  void NotifyConnectionClosed1() {
    if (session_unowned1_event_handler_) {
      session_unowned1_event_handler_->OnSessionStateChange(Session::CLOSED);
    }
  }

  void NotifyConnectionClosed2() {
    if (session_unowned2_event_handler_) {
      session_unowned2_event_handler_->OnSessionStateChange(Session::CLOSED);
    }
  }

  void ShutdownHost() {
    task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&ChromotingHostTest::StopAndReleaseTaskRunner,
                   base::Unretained(this)));
  }

  void StopAndReleaseTaskRunner() {
    host_.reset();
    task_runner_ = NULL;
    desktop_environment_factory_.reset();
  }

  void QuitMainMessageLoop() {
    PostQuitTask(&message_loop_);
  }

  // Expect the host and session manager to start, and return the expectation
  // that the session manager has started.
  Expectation ExpectHostAndSessionManagerStart() {
    EXPECT_CALL(host_status_observer_, OnStart(xmpp_login_));
    return EXPECT_CALL(*session_manager_, Init(_, host_.get()));
  }

  // Expect a client to connect.
  // Return an expectation that a session has started, and that the first
  // video packet has been sent to the client.
  // Do |action| when that happens.
  template <class A>
  Expectation ExpectClientConnected(int connection_index, A action) {
    const std::string& session_jid = get_session_jid(connection_index);
    MockVideoStub& video_stub = get_video_stub(connection_index);

    Expectation client_authenticated =
        EXPECT_CALL(host_status_observer_, OnClientAuthenticated(session_jid));
    EXPECT_CALL(host_status_observer_, OnClientConnected(session_jid))
        .After(client_authenticated);
    Expectation video_packet_sent =
        EXPECT_CALL(video_stub, ProcessVideoPacketPtr(_, _))
        .After(client_authenticated)
        .WillOnce(DoAll(
            action,
            RunDoneTask()))
        .RetiresOnSaturation();
    EXPECT_CALL(video_stub, ProcessVideoPacketPtr(_, _))
        .Times(AnyNumber())
        .After(video_packet_sent)
        .WillRepeatedly(RunDoneTask());
    return video_packet_sent;
  }

  // Return an expectation that a client will disconnect after a given
  // expectation. The given action will be done after the event executor is
  // notified that the session has finished.
  template <class A>
  Expectation ExpectClientDisconnected(int connection_index,
                                       bool expect_host_status_change,
                                       Expectation after,
                                       A action) {
    MockConnectionToClient* connection = get_connection(connection_index);

    Expectation client_disconnected =
        EXPECT_CALL(*connection, Disconnect())
            .After(after)
            .WillOnce(InvokeWithoutArgs(CreateFunctor(
                this, &ChromotingHostTest::NotifyClientSessionClosed,
                connection_index)))
            .RetiresOnSaturation();
    ExpectClientDisconnectEffects(connection_index,
                                  expect_host_status_change,
                                  after,
                                  action);
    return client_disconnected;
  }

  // Expect the side-effects of a client disconnection, after a given
  // expectation. The given action will be done after the event executor is
  // notifed that the session has finished.
  template <class A>
  void ExpectClientDisconnectEffects(int connection_index,
                                     bool expect_host_status_change,
                                     Expectation after,
                                     A action) {
    const std::string& session_jid = get_session_jid(connection_index);

    if (expect_host_status_change) {
      EXPECT_CALL(host_status_observer_, OnClientDisconnected(session_jid))
          .After(after)
          .WillOnce(action)
          .RetiresOnSaturation();
    }
  }

 protected:
  base::MessageLoop message_loop_;
  scoped_refptr<AutoThreadTaskRunner> task_runner_;
  MockConnectionToClientEventHandler handler_;
  MockSignalStrategy signal_strategy_;
  scoped_ptr<MockDesktopEnvironmentFactory> desktop_environment_factory_;
  scoped_ptr<ChromotingHost> host_;
  MockHostStatusObserver host_status_observer_;
  protocol::MockSessionManager* session_manager_;
  std::string xmpp_login_;
  MockConnectionToClient* connection1_;
  scoped_ptr<MockConnectionToClient> owned_connection1_;
  ClientSession* client1_;
  std::string session_jid1_;
  MockSession* session1_;  // Owned by |connection_|.
  SessionConfig session_config1_;
  MockVideoStub video_stub1_;
  MockClientStub client_stub1_;
  MockHostStub host_stub1_;
  MockConnectionToClient* connection2_;
  scoped_ptr<MockConnectionToClient> owned_connection2_;
  ClientSession* client2_;
  std::string session_jid2_;
  MockSession* session2_;  // Owned by |connection2_|.
  SessionConfig session_config2_;
  MockVideoStub video_stub2_;
  MockClientStub client_stub2_;
  MockHostStub host_stub2_;
  scoped_ptr<MockSession> session_unowned1_;  // Not owned by a connection.
  SessionConfig session_unowned_config1_;
  std::string session_unowned_jid1_;
  scoped_ptr<MockSession> session_unowned2_;  // Not owned by a connection.
  SessionConfig session_unowned_config2_;
  std::string session_unowned_jid2_;
  protocol::Session::EventHandler* session_unowned1_event_handler_;
  protocol::Session::EventHandler* session_unowned2_event_handler_;
  scoped_ptr<protocol::CandidateSessionConfig> empty_candidate_config_;
  scoped_ptr<protocol::CandidateSessionConfig> default_candidate_config_;

  MockConnectionToClient*& get_connection(int connection_index) {
    return (connection_index == 0) ? connection1_ : connection2_;
  }

  // Returns the cached client pointers client1_ or client2_.
  ClientSession*& get_client(int connection_index) {
    return (connection_index == 0) ? client1_ : client2_;
  }

  // Returns the list of clients of the host_.
  std::list<ClientSession*>& get_clients_from_host() {
    return host_->clients_;
  }

  const std::string& get_session_jid(int connection_index) {
    return (connection_index == 0) ? session_jid1_ : session_jid2_;
  }

  MockVideoStub& get_video_stub(int connection_index) {
    return (connection_index == 0) ? video_stub1_ : video_stub2_;
  }
};

TEST_F(ChromotingHostTest, StartAndShutdown) {
  Expectation start = ExpectHostAndSessionManagerStart();
  EXPECT_CALL(host_status_observer_, OnShutdown()).After(start);

  host_->Start(xmpp_login_);
  ShutdownHost();
  message_loop_.Run();
}

TEST_F(ChromotingHostTest, Connect) {
  ExpectHostAndSessionManagerStart();

  // Shut down the host when the first video packet is received.
  Expectation video_packet_sent = ExpectClientConnected(
      0, InvokeWithoutArgs(this, &ChromotingHostTest::ShutdownHost));
  Expectation client_disconnected = ExpectClientDisconnected(
      0, true, video_packet_sent, InvokeWithoutArgs(base::DoNothing));
  EXPECT_CALL(host_status_observer_, OnShutdown()).After(client_disconnected);

  host_->Start(xmpp_login_);
  SimulateClientConnection(0, true, false);
  message_loop_.Run();
}

TEST_F(ChromotingHostTest, RejectAuthenticatingClient) {
  Expectation start = ExpectHostAndSessionManagerStart();
  EXPECT_CALL(host_status_observer_, OnClientAuthenticated(session_jid1_))
      .WillOnce(InvokeWithoutArgs(
      this, &ChromotingHostTest::RejectAuthenticatingClient));
  ExpectClientDisconnected(
      0, true, start,
      InvokeWithoutArgs(this, &ChromotingHostTest::ShutdownHost));
  EXPECT_CALL(host_status_observer_, OnShutdown());

  host_->Start(xmpp_login_);
  SimulateClientConnection(0, true, true);
  message_loop_.Run();
}

TEST_F(ChromotingHostTest, AuthenticationFailed) {
  ExpectHostAndSessionManagerStart();
  EXPECT_CALL(host_status_observer_, OnAccessDenied(session_jid1_))
      .WillOnce(InvokeWithoutArgs(this, &ChromotingHostTest::ShutdownHost));
  EXPECT_CALL(host_status_observer_, OnShutdown());

  host_->Start(xmpp_login_);
  SimulateClientConnection(0, false, false);
  message_loop_.Run();
}

TEST_F(ChromotingHostTest, Reconnect) {
  ExpectHostAndSessionManagerStart();

  // When a video packet is received on the first connection, disconnect it,
  // then quit the message loop.
  Expectation video_packet_sent1 = ExpectClientConnected(0, DoAll(
      InvokeWithoutArgs(this, &ChromotingHostTest::DisconnectClient1),
      InvokeWithoutArgs(this, &ChromotingHostTest::QuitMainMessageLoop)));
  ExpectClientDisconnectEffects(
      0, true, video_packet_sent1, InvokeWithoutArgs(base::DoNothing));

  // When a video packet is received on the second connection, shut down the
  // host.
  Expectation video_packet_sent2 = ExpectClientConnected(
      1, InvokeWithoutArgs(this, &ChromotingHostTest::ShutdownHost));
  Expectation client_disconnected2 = ExpectClientDisconnected(
      1, true, video_packet_sent2, InvokeWithoutArgs(base::DoNothing));
  EXPECT_CALL(host_status_observer_, OnShutdown()).After(client_disconnected2);

  host_->Start(xmpp_login_);
  SimulateClientConnection(0, true, false);
  message_loop_.Run();
  SimulateClientConnection(1, true, false);
  message_loop_.Run();
}

TEST_F(ChromotingHostTest, ConnectWhenAnotherClientIsConnected) {
  ExpectHostAndSessionManagerStart();

  // When a video packet is received, connect the second connection.
  // This should disconnect the first connection.
  Expectation video_packet_sent1 = ExpectClientConnected(
      0,
      InvokeWithoutArgs(
          CreateFunctor(
              this,
              &ChromotingHostTest::SimulateClientConnection, 1, true, false)));
  ExpectClientDisconnected(
      0, true, video_packet_sent1, InvokeWithoutArgs(base::DoNothing));
  Expectation video_packet_sent2 = ExpectClientConnected(
      1, InvokeWithoutArgs(this, &ChromotingHostTest::ShutdownHost));
  Expectation client_disconnected2 = ExpectClientDisconnected(
      1, true, video_packet_sent2, InvokeWithoutArgs(base::DoNothing));
  EXPECT_CALL(host_status_observer_, OnShutdown()).After(client_disconnected2);

  host_->Start(xmpp_login_);
  SimulateClientConnection(0, true, false);
  message_loop_.Run();
}

TEST_F(ChromotingHostTest, IncomingSessionDeclined) {
  protocol::SessionManager::IncomingSessionResponse response =
      protocol::SessionManager::ACCEPT;
  host_->OnIncomingSession(session1_, &response);
  EXPECT_EQ(protocol::SessionManager::DECLINE, response);

  ShutdownHost();
  message_loop_.Run();
}

TEST_F(ChromotingHostTest, IncomingSessionIncompatible) {
  ExpectHostAndSessionManagerStart();
  EXPECT_CALL(*session_unowned1_, candidate_config()).WillOnce(Return(
      empty_candidate_config_.get()));
  EXPECT_CALL(host_status_observer_, OnShutdown());

  host_->Start(xmpp_login_);

  protocol::SessionManager::IncomingSessionResponse response =
      protocol::SessionManager::ACCEPT;
  host_->OnIncomingSession(session_unowned1_.get(), &response);
  EXPECT_EQ(protocol::SessionManager::INCOMPATIBLE, response);

  ShutdownHost();
  message_loop_.Run();
}

TEST_F(ChromotingHostTest, IncomingSessionAccepted) {
  ExpectHostAndSessionManagerStart();
  EXPECT_CALL(*session_unowned1_, candidate_config()).WillOnce(Return(
      default_candidate_config_.get()));
  EXPECT_CALL(*session_unowned1_, set_config(_));
  EXPECT_CALL(*session_unowned1_, Close()).WillOnce(InvokeWithoutArgs(
    this, &ChromotingHostTest::NotifyConnectionClosed1));
  EXPECT_CALL(host_status_observer_, OnAccessDenied(_));
  EXPECT_CALL(host_status_observer_, OnShutdown());

  host_->Start(xmpp_login_);

  protocol::SessionManager::IncomingSessionResponse response =
      protocol::SessionManager::DECLINE;
  host_->OnIncomingSession(session_unowned1_.release(), &response);
  EXPECT_EQ(protocol::SessionManager::ACCEPT, response);

  ShutdownHost();
  message_loop_.Run();
}

TEST_F(ChromotingHostTest, LoginBackOffUponConnection) {
  ExpectHostAndSessionManagerStart();
  EXPECT_CALL(*session_unowned1_, candidate_config()).WillOnce(
    Return(default_candidate_config_.get()));
  EXPECT_CALL(*session_unowned1_, set_config(_));
  EXPECT_CALL(*session_unowned1_, Close()).WillOnce(
    InvokeWithoutArgs(this, &ChromotingHostTest::NotifyConnectionClosed1));
  EXPECT_CALL(host_status_observer_, OnAccessDenied(_));
  EXPECT_CALL(host_status_observer_, OnShutdown());

  host_->Start(xmpp_login_);

  protocol::SessionManager::IncomingSessionResponse response =
      protocol::SessionManager::DECLINE;

  host_->OnIncomingSession(session_unowned1_.release(), &response);
  EXPECT_EQ(protocol::SessionManager::ACCEPT, response);

  host_->OnSessionAuthenticating(get_clients_from_host().front());
  host_->OnIncomingSession(session_unowned2_.get(), &response);
  EXPECT_EQ(protocol::SessionManager::OVERLOAD, response);

  ShutdownHost();
  message_loop_.Run();
}

TEST_F(ChromotingHostTest, LoginBackOffUponAuthenticating) {
  Expectation start = ExpectHostAndSessionManagerStart();
  EXPECT_CALL(*session_unowned1_, candidate_config()).WillOnce(
    Return(default_candidate_config_.get()));
  EXPECT_CALL(*session_unowned1_, set_config(_));
  EXPECT_CALL(*session_unowned1_, Close()).WillOnce(
    InvokeWithoutArgs(this, &ChromotingHostTest::NotifyConnectionClosed1));

  EXPECT_CALL(*session_unowned2_, candidate_config()).WillOnce(
    Return(default_candidate_config_.get()));
  EXPECT_CALL(*session_unowned2_, set_config(_));
  EXPECT_CALL(*session_unowned2_, Close()).WillOnce(
    InvokeWithoutArgs(this, &ChromotingHostTest::NotifyConnectionClosed2));

  EXPECT_CALL(host_status_observer_, OnShutdown());

  host_->Start(xmpp_login_);

  protocol::SessionManager::IncomingSessionResponse response =
      protocol::SessionManager::DECLINE;

  host_->OnIncomingSession(session_unowned1_.release(), &response);
  EXPECT_EQ(protocol::SessionManager::ACCEPT, response);

  host_->OnIncomingSession(session_unowned2_.release(), &response);
  EXPECT_EQ(protocol::SessionManager::ACCEPT, response);

  // This will set the backoff.
  host_->OnSessionAuthenticating(get_clients_from_host().front());

  // This should disconnect client2.
  host_->OnSessionAuthenticating(get_clients_from_host().back());

  // Verify that the host only has 1 client at this point.
  EXPECT_EQ(get_clients_from_host().size(), 1U);

  ShutdownHost();
  message_loop_.Run();
}

TEST_F(ChromotingHostTest, OnSessionRouteChange) {
  std::string channel_name("ChannelName");
  protocol::TransportRoute route;

  ExpectHostAndSessionManagerStart();
  Expectation video_packet_sent = ExpectClientConnected(
      0, InvokeWithoutArgs(CreateFunctor(
          this, &ChromotingHostTest::ChangeSessionRoute, channel_name, route)));
  Expectation route_change =
      EXPECT_CALL(host_status_observer_, OnClientRouteChange(
          session_jid1_, channel_name, _))
      .After(video_packet_sent)
      .WillOnce(InvokeWithoutArgs(this, &ChromotingHostTest::ShutdownHost));
  ExpectClientDisconnected(0, true, route_change,
                           InvokeWithoutArgs(base::DoNothing));
  EXPECT_CALL(host_status_observer_, OnShutdown());

  host_->Start(xmpp_login_);
  SimulateClientConnection(0, true, false);
  message_loop_.Run();
}

TEST_F(ChromotingHostTest, DisconnectAllClients) {
  ExpectHostAndSessionManagerStart();
  Expectation video_packet_sent = ExpectClientConnected(
      0, InvokeWithoutArgs(this, &ChromotingHostTest::DisconnectAllClients));
  ExpectClientDisconnected(0, true, video_packet_sent,
      InvokeWithoutArgs(this, &ChromotingHostTest::ShutdownHost));
  EXPECT_CALL(host_status_observer_, OnShutdown());

  host_->Start(xmpp_login_);
  SimulateClientConnection(0, true, false);
  message_loop_.Run();
}

}  // namespace remoting