Home | History | Annotate | Download | only in host
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/bind.h"
      6 #include "base/bind_helpers.h"
      7 #include "base/location.h"
      8 #include "base/memory/ref_counted.h"
      9 #include "base/process/process.h"
     10 #include "ipc/ipc_message.h"
     11 #include "ipc/ipc_message_macros.h"
     12 #include "ipc/ipc_platform_file.h"
     13 #include "remoting/base/auto_thread_task_runner.h"
     14 #include "remoting/host/chromoting_messages.h"
     15 #include "remoting/host/daemon_process.h"
     16 #include "remoting/host/desktop_session.h"
     17 #include "testing/gmock_mutant.h"
     18 #include "testing/gmock/include/gmock/gmock.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 
     21 using testing::_;
     22 using testing::AnyNumber;
     23 using testing::InSequence;
     24 
     25 namespace remoting {
     26 
     27 namespace {
     28 
     29 enum Messages {
     30   kMessageCrash = ChromotingDaemonMsg_Crash::ID,
     31   kMessageConfiguration = ChromotingDaemonNetworkMsg_Configuration::ID,
     32   kMessageConnectTerminal = ChromotingNetworkHostMsg_ConnectTerminal::ID,
     33   kMessageDisconnectTerminal = ChromotingNetworkHostMsg_DisconnectTerminal::ID,
     34   kMessageTerminalDisconnected =
     35       ChromotingDaemonNetworkMsg_TerminalDisconnected::ID
     36 };
     37 
     38 // Provides a public constructor allowing the test to create instances of
     39 // DesktopSession directly.
     40 class FakeDesktopSession : public DesktopSession {
     41  public:
     42   FakeDesktopSession(DaemonProcess* daemon_process, int id);
     43   virtual ~FakeDesktopSession();
     44 
     45   virtual void SetScreenResolution(
     46       const ScreenResolution& resolution) OVERRIDE {}
     47 
     48  private:
     49   DISALLOW_COPY_AND_ASSIGN(FakeDesktopSession);
     50 };
     51 
     52 class MockDaemonProcess : public DaemonProcess {
     53  public:
     54   MockDaemonProcess(
     55       scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
     56       scoped_refptr<AutoThreadTaskRunner> io_task_runner,
     57       const base::Closure& stopped_callback);
     58   virtual ~MockDaemonProcess();
     59 
     60   virtual scoped_ptr<DesktopSession> DoCreateDesktopSession(
     61       int terminal_id,
     62       const ScreenResolution& resolution,
     63       bool virtual_terminal) OVERRIDE;
     64 
     65   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
     66   virtual void SendToNetwork(IPC::Message* message) OVERRIDE;
     67 
     68   MOCK_METHOD1(Received, void(const IPC::Message&));
     69   MOCK_METHOD1(Sent, void(const IPC::Message&));
     70 
     71   MOCK_METHOD3(OnDesktopSessionAgentAttached,
     72                bool(int, base::ProcessHandle, IPC::PlatformFileForTransit));
     73 
     74   MOCK_METHOD1(DoCreateDesktopSessionPtr, DesktopSession*(int));
     75   MOCK_METHOD1(DoCrashNetworkProcess, void(const tracked_objects::Location&));
     76   MOCK_METHOD0(LaunchNetworkProcess, void());
     77 
     78  private:
     79   DISALLOW_COPY_AND_ASSIGN(MockDaemonProcess);
     80 };
     81 
     82 FakeDesktopSession::FakeDesktopSession(DaemonProcess* daemon_process, int id)
     83     : DesktopSession(daemon_process, id) {
     84 }
     85 
     86 FakeDesktopSession::~FakeDesktopSession() {
     87 }
     88 
     89 MockDaemonProcess::MockDaemonProcess(
     90     scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
     91     scoped_refptr<AutoThreadTaskRunner> io_task_runner,
     92     const base::Closure& stopped_callback)
     93     : DaemonProcess(caller_task_runner, io_task_runner, stopped_callback) {
     94 }
     95 
     96 MockDaemonProcess::~MockDaemonProcess() {
     97 }
     98 
     99 scoped_ptr<DesktopSession> MockDaemonProcess::DoCreateDesktopSession(
    100     int terminal_id,
    101     const ScreenResolution& resolution,
    102     bool virtual_terminal) {
    103   return scoped_ptr<DesktopSession>(DoCreateDesktopSessionPtr(terminal_id));
    104 }
    105 
    106 bool MockDaemonProcess::OnMessageReceived(const IPC::Message& message) {
    107   // Notify the mock method.
    108   Received(message);
    109 
    110   // Call the actual handler.
    111   return DaemonProcess::OnMessageReceived(message);
    112 }
    113 
    114 void MockDaemonProcess::SendToNetwork(IPC::Message* message) {
    115   // Notify the mock method.
    116   Sent(*message);
    117   delete message;
    118 }
    119 
    120 }  // namespace
    121 
    122 class DaemonProcessTest : public testing::Test {
    123  public:
    124   DaemonProcessTest();
    125   virtual ~DaemonProcessTest();
    126 
    127   virtual void SetUp() OVERRIDE;
    128   virtual void TearDown() OVERRIDE;
    129 
    130   // DaemonProcess mocks
    131   DesktopSession* DoCreateDesktopSession(int terminal_id);
    132   void DoCrashNetworkProcess(const tracked_objects::Location& location);
    133   void LaunchNetworkProcess();
    134 
    135   // Deletes |daemon_process_|.
    136   void DeleteDaemonProcess();
    137 
    138   // Quits |message_loop_|.
    139   void QuitMessageLoop();
    140 
    141   void StartDaemonProcess();
    142 
    143   const DaemonProcess::DesktopSessionList& desktop_sessions() const {
    144     return daemon_process_->desktop_sessions();
    145   }
    146 
    147  protected:
    148   base::MessageLoop message_loop_;
    149 
    150   scoped_ptr<MockDaemonProcess> daemon_process_;
    151   int terminal_id_;
    152 };
    153 
    154 DaemonProcessTest::DaemonProcessTest()
    155     : message_loop_(base::MessageLoop::TYPE_IO), terminal_id_(0) {
    156 }
    157 
    158 DaemonProcessTest::~DaemonProcessTest() {
    159 }
    160 
    161 void DaemonProcessTest::SetUp() {
    162   scoped_refptr<AutoThreadTaskRunner> task_runner = new AutoThreadTaskRunner(
    163       message_loop_.message_loop_proxy(),
    164       base::Bind(&DaemonProcessTest::QuitMessageLoop,
    165                  base::Unretained(this)));
    166   daemon_process_.reset(
    167       new MockDaemonProcess(task_runner, task_runner,
    168                             base::Bind(&DaemonProcessTest::DeleteDaemonProcess,
    169                                        base::Unretained(this))));
    170 
    171   // Set up daemon process mocks.
    172   EXPECT_CALL(*daemon_process_, DoCreateDesktopSessionPtr(_))
    173       .Times(AnyNumber())
    174       .WillRepeatedly(Invoke(this, &DaemonProcessTest::DoCreateDesktopSession));
    175   EXPECT_CALL(*daemon_process_, DoCrashNetworkProcess(_))
    176       .Times(AnyNumber())
    177       .WillRepeatedly(Invoke(this, &DaemonProcessTest::DoCrashNetworkProcess));
    178   EXPECT_CALL(*daemon_process_, LaunchNetworkProcess())
    179       .Times(AnyNumber())
    180       .WillRepeatedly(Invoke(this, &DaemonProcessTest::LaunchNetworkProcess));
    181 }
    182 
    183 void DaemonProcessTest::TearDown() {
    184   daemon_process_->Stop();
    185   message_loop_.Run();
    186 }
    187 
    188 DesktopSession* DaemonProcessTest::DoCreateDesktopSession(int terminal_id) {
    189   return new FakeDesktopSession(daemon_process_.get(), terminal_id);
    190 }
    191 
    192 void DaemonProcessTest::DoCrashNetworkProcess(
    193     const tracked_objects::Location& location) {
    194   daemon_process_->SendToNetwork(
    195       new ChromotingDaemonMsg_Crash(location.function_name(),
    196                                     location.file_name(),
    197                                     location.line_number()));
    198 }
    199 
    200 void DaemonProcessTest::LaunchNetworkProcess() {
    201   terminal_id_ = 0;
    202   daemon_process_->OnChannelConnected(0);
    203 }
    204 
    205 void DaemonProcessTest::DeleteDaemonProcess() {
    206   daemon_process_.reset();
    207 }
    208 
    209 void DaemonProcessTest::QuitMessageLoop() {
    210   message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
    211 }
    212 
    213 void DaemonProcessTest::StartDaemonProcess() {
    214   // DaemonProcess::Initialize() sets up the config watcher that this test does
    215   // not support. Launch the process directly.
    216   daemon_process_->LaunchNetworkProcess();
    217 }
    218 
    219 MATCHER_P(Message, type, "") {
    220   return arg.type() == static_cast<uint32>(type);
    221 }
    222 
    223 TEST_F(DaemonProcessTest, OpenClose) {
    224   InSequence s;
    225   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration)));
    226   EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal)));
    227   EXPECT_CALL(*daemon_process_, Received(Message(kMessageDisconnectTerminal)));
    228   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageTerminalDisconnected)));
    229 
    230   StartDaemonProcess();
    231 
    232   int id = terminal_id_++;
    233   ScreenResolution resolution;
    234 
    235   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    236       ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false)));
    237   EXPECT_EQ(1u, desktop_sessions().size());
    238   EXPECT_EQ(id, desktop_sessions().front()->id());
    239 
    240   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    241       ChromotingNetworkHostMsg_DisconnectTerminal(id)));
    242   EXPECT_TRUE(desktop_sessions().empty());
    243 }
    244 
    245 TEST_F(DaemonProcessTest, CallCloseDesktopSession) {
    246   InSequence s;
    247   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration)));
    248   EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal)));
    249   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageTerminalDisconnected)));
    250 
    251   StartDaemonProcess();
    252 
    253   int id = terminal_id_++;
    254   ScreenResolution resolution;
    255 
    256   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    257       ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false)));
    258   EXPECT_EQ(1u, desktop_sessions().size());
    259   EXPECT_EQ(id, desktop_sessions().front()->id());
    260 
    261   daemon_process_->CloseDesktopSession(id);
    262   EXPECT_TRUE(desktop_sessions().empty());
    263 }
    264 
    265 // Sends two CloseDesktopSession messages and expects the second one to be
    266 // ignored.
    267 TEST_F(DaemonProcessTest, DoubleDisconnectTerminal) {
    268   InSequence s;
    269   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration)));
    270   EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal)));
    271   EXPECT_CALL(*daemon_process_, Received(Message(kMessageDisconnectTerminal)));
    272   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageTerminalDisconnected)));
    273   EXPECT_CALL(*daemon_process_, Received(Message(kMessageDisconnectTerminal)));
    274 
    275   StartDaemonProcess();
    276 
    277   int id = terminal_id_++;
    278   ScreenResolution resolution;
    279 
    280   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    281       ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false)));
    282   EXPECT_EQ(1u, desktop_sessions().size());
    283   EXPECT_EQ(id, desktop_sessions().front()->id());
    284 
    285   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    286       ChromotingNetworkHostMsg_DisconnectTerminal(id)));
    287   EXPECT_TRUE(desktop_sessions().empty());
    288 
    289   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    290       ChromotingNetworkHostMsg_DisconnectTerminal(id)));
    291   EXPECT_TRUE(desktop_sessions().empty());
    292 }
    293 
    294 // Tries to close an invalid terminal ID and expects the network process to be
    295 // restarted.
    296 TEST_F(DaemonProcessTest, InvalidDisconnectTerminal) {
    297   InSequence s;
    298   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration)));
    299   EXPECT_CALL(*daemon_process_, Received(Message(kMessageDisconnectTerminal)));
    300   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageCrash)))
    301       .WillOnce(InvokeWithoutArgs(this,
    302                                   &DaemonProcessTest::LaunchNetworkProcess));
    303   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration)));
    304 
    305   StartDaemonProcess();
    306 
    307   int id = terminal_id_++;
    308 
    309   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    310       ChromotingNetworkHostMsg_DisconnectTerminal(id)));
    311   EXPECT_TRUE(desktop_sessions().empty());
    312   EXPECT_EQ(0, terminal_id_);
    313 }
    314 
    315 // Tries to open an invalid terminal ID and expects the network process to be
    316 // restarted.
    317 TEST_F(DaemonProcessTest, InvalidConnectTerminal) {
    318   InSequence s;
    319   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration)));
    320   EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal)));
    321   EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal)));
    322   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageCrash)))
    323       .WillOnce(InvokeWithoutArgs(this,
    324                                   &DaemonProcessTest::LaunchNetworkProcess));
    325   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration)));
    326 
    327   StartDaemonProcess();
    328 
    329   int id = terminal_id_++;
    330   ScreenResolution resolution;
    331 
    332   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    333       ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false)));
    334   EXPECT_EQ(1u, desktop_sessions().size());
    335   EXPECT_EQ(id, desktop_sessions().front()->id());
    336 
    337   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    338       ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false)));
    339   EXPECT_TRUE(desktop_sessions().empty());
    340   EXPECT_EQ(0, terminal_id_);
    341 }
    342 
    343 }  // namespace remoting
    344