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::MessageLoopForIO message_loop_;
    149 
    150   scoped_ptr<MockDaemonProcess> daemon_process_;
    151   int terminal_id_;
    152 };
    153 
    154 DaemonProcessTest::DaemonProcessTest() : terminal_id_(0) {
    155 }
    156 
    157 DaemonProcessTest::~DaemonProcessTest() {
    158 }
    159 
    160 void DaemonProcessTest::SetUp() {
    161   scoped_refptr<AutoThreadTaskRunner> task_runner = new AutoThreadTaskRunner(
    162       message_loop_.message_loop_proxy(),
    163       base::Bind(&DaemonProcessTest::QuitMessageLoop,
    164                  base::Unretained(this)));
    165   daemon_process_.reset(
    166       new MockDaemonProcess(task_runner, task_runner,
    167                             base::Bind(&DaemonProcessTest::DeleteDaemonProcess,
    168                                        base::Unretained(this))));
    169 
    170   // Set up daemon process mocks.
    171   EXPECT_CALL(*daemon_process_, DoCreateDesktopSessionPtr(_))
    172       .Times(AnyNumber())
    173       .WillRepeatedly(Invoke(this, &DaemonProcessTest::DoCreateDesktopSession));
    174   EXPECT_CALL(*daemon_process_, DoCrashNetworkProcess(_))
    175       .Times(AnyNumber())
    176       .WillRepeatedly(Invoke(this, &DaemonProcessTest::DoCrashNetworkProcess));
    177   EXPECT_CALL(*daemon_process_, LaunchNetworkProcess())
    178       .Times(AnyNumber())
    179       .WillRepeatedly(Invoke(this, &DaemonProcessTest::LaunchNetworkProcess));
    180 }
    181 
    182 void DaemonProcessTest::TearDown() {
    183   daemon_process_->Stop();
    184   message_loop_.Run();
    185 }
    186 
    187 DesktopSession* DaemonProcessTest::DoCreateDesktopSession(int terminal_id) {
    188   return new FakeDesktopSession(daemon_process_.get(), terminal_id);
    189 }
    190 
    191 void DaemonProcessTest::DoCrashNetworkProcess(
    192     const tracked_objects::Location& location) {
    193   daemon_process_->SendToNetwork(
    194       new ChromotingDaemonMsg_Crash(location.function_name(),
    195                                     location.file_name(),
    196                                     location.line_number()));
    197 }
    198 
    199 void DaemonProcessTest::LaunchNetworkProcess() {
    200   terminal_id_ = 0;
    201   daemon_process_->OnChannelConnected(0);
    202 }
    203 
    204 void DaemonProcessTest::DeleteDaemonProcess() {
    205   daemon_process_.reset();
    206 }
    207 
    208 void DaemonProcessTest::QuitMessageLoop() {
    209   message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
    210 }
    211 
    212 void DaemonProcessTest::StartDaemonProcess() {
    213   // DaemonProcess::Initialize() sets up the config watcher that this test does
    214   // not support. Launch the process directly.
    215   daemon_process_->LaunchNetworkProcess();
    216 }
    217 
    218 MATCHER_P(Message, type, "") {
    219   return arg.type() == static_cast<uint32>(type);
    220 }
    221 
    222 TEST_F(DaemonProcessTest, OpenClose) {
    223   InSequence s;
    224   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration)));
    225   EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal)));
    226   EXPECT_CALL(*daemon_process_, Received(Message(kMessageDisconnectTerminal)));
    227   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageTerminalDisconnected)));
    228 
    229   StartDaemonProcess();
    230 
    231   int id = terminal_id_++;
    232   ScreenResolution resolution;
    233 
    234   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    235       ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false)));
    236   EXPECT_EQ(1u, desktop_sessions().size());
    237   EXPECT_EQ(id, desktop_sessions().front()->id());
    238 
    239   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    240       ChromotingNetworkHostMsg_DisconnectTerminal(id)));
    241   EXPECT_TRUE(desktop_sessions().empty());
    242 }
    243 
    244 TEST_F(DaemonProcessTest, CallCloseDesktopSession) {
    245   InSequence s;
    246   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration)));
    247   EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal)));
    248   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageTerminalDisconnected)));
    249 
    250   StartDaemonProcess();
    251 
    252   int id = terminal_id_++;
    253   ScreenResolution resolution;
    254 
    255   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    256       ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false)));
    257   EXPECT_EQ(1u, desktop_sessions().size());
    258   EXPECT_EQ(id, desktop_sessions().front()->id());
    259 
    260   daemon_process_->CloseDesktopSession(id);
    261   EXPECT_TRUE(desktop_sessions().empty());
    262 }
    263 
    264 // Sends two CloseDesktopSession messages and expects the second one to be
    265 // ignored.
    266 TEST_F(DaemonProcessTest, DoubleDisconnectTerminal) {
    267   InSequence s;
    268   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration)));
    269   EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal)));
    270   EXPECT_CALL(*daemon_process_, Received(Message(kMessageDisconnectTerminal)));
    271   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageTerminalDisconnected)));
    272   EXPECT_CALL(*daemon_process_, Received(Message(kMessageDisconnectTerminal)));
    273 
    274   StartDaemonProcess();
    275 
    276   int id = terminal_id_++;
    277   ScreenResolution resolution;
    278 
    279   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    280       ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false)));
    281   EXPECT_EQ(1u, desktop_sessions().size());
    282   EXPECT_EQ(id, desktop_sessions().front()->id());
    283 
    284   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    285       ChromotingNetworkHostMsg_DisconnectTerminal(id)));
    286   EXPECT_TRUE(desktop_sessions().empty());
    287 
    288   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    289       ChromotingNetworkHostMsg_DisconnectTerminal(id)));
    290   EXPECT_TRUE(desktop_sessions().empty());
    291 }
    292 
    293 // Tries to close an invalid terminal ID and expects the network process to be
    294 // restarted.
    295 TEST_F(DaemonProcessTest, InvalidDisconnectTerminal) {
    296   InSequence s;
    297   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration)));
    298   EXPECT_CALL(*daemon_process_, Received(Message(kMessageDisconnectTerminal)));
    299   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageCrash)))
    300       .WillOnce(InvokeWithoutArgs(this,
    301                                   &DaemonProcessTest::LaunchNetworkProcess));
    302   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration)));
    303 
    304   StartDaemonProcess();
    305 
    306   int id = terminal_id_++;
    307 
    308   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    309       ChromotingNetworkHostMsg_DisconnectTerminal(id)));
    310   EXPECT_TRUE(desktop_sessions().empty());
    311   EXPECT_EQ(0, terminal_id_);
    312 }
    313 
    314 // Tries to open an invalid terminal ID and expects the network process to be
    315 // restarted.
    316 TEST_F(DaemonProcessTest, InvalidConnectTerminal) {
    317   InSequence s;
    318   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration)));
    319   EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal)));
    320   EXPECT_CALL(*daemon_process_, Received(Message(kMessageConnectTerminal)));
    321   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageCrash)))
    322       .WillOnce(InvokeWithoutArgs(this,
    323                                   &DaemonProcessTest::LaunchNetworkProcess));
    324   EXPECT_CALL(*daemon_process_, Sent(Message(kMessageConfiguration)));
    325 
    326   StartDaemonProcess();
    327 
    328   int id = terminal_id_++;
    329   ScreenResolution resolution;
    330 
    331   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    332       ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false)));
    333   EXPECT_EQ(1u, desktop_sessions().size());
    334   EXPECT_EQ(id, desktop_sessions().front()->id());
    335 
    336   EXPECT_TRUE(daemon_process_->OnMessageReceived(
    337       ChromotingNetworkHostMsg_ConnectTerminal(id, resolution, false)));
    338   EXPECT_TRUE(desktop_sessions().empty());
    339   EXPECT_EQ(0, terminal_id_);
    340 }
    341 
    342 }  // namespace remoting
    343