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 "remoting/host/desktop_process.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/location.h"
     10 #include "base/memory/ref_counted.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/run_loop.h"
     13 #include "base/single_thread_task_runner.h"
     14 #include "ipc/ipc_channel.h"
     15 #include "ipc/ipc_channel_proxy.h"
     16 #include "ipc/ipc_listener.h"
     17 #include "ipc/ipc_message.h"
     18 #include "remoting/base/auto_thread.h"
     19 #include "remoting/base/auto_thread_task_runner.h"
     20 #include "remoting/host/chromoting_messages.h"
     21 #include "remoting/host/desktop_process.h"
     22 #include "remoting/host/host_exit_codes.h"
     23 #include "remoting/host/host_mock_objects.h"
     24 #include "remoting/host/screen_capturer_fake.h"
     25 #include "remoting/host/screen_resolution.h"
     26 #include "remoting/protocol/protocol_mock_objects.h"
     27 #include "testing/gmock/include/gmock/gmock.h"
     28 #include "testing/gmock_mutant.h"
     29 #include "testing/gtest/include/gtest/gtest.h"
     30 
     31 using testing::_;
     32 using testing::AnyNumber;
     33 using testing::AtMost;
     34 using testing::InSequence;
     35 using testing::Return;
     36 
     37 namespace remoting {
     38 
     39 namespace {
     40 
     41 class MockDaemonListener : public IPC::Listener {
     42  public:
     43   MockDaemonListener() {}
     44   virtual ~MockDaemonListener() {}
     45 
     46   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
     47 
     48   MOCK_METHOD1(OnDesktopAttached, void(IPC::PlatformFileForTransit));
     49   MOCK_METHOD1(OnChannelConnected, void(int32));
     50   MOCK_METHOD0(OnChannelError, void());
     51 
     52  private:
     53   DISALLOW_COPY_AND_ASSIGN(MockDaemonListener);
     54 };
     55 
     56 class MockNetworkListener : public IPC::Listener {
     57  public:
     58   MockNetworkListener() {}
     59   virtual ~MockNetworkListener() {}
     60 
     61   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
     62 
     63   MOCK_METHOD1(OnChannelConnected, void(int32));
     64   MOCK_METHOD0(OnChannelError, void());
     65 
     66   MOCK_METHOD0(OnDesktopEnvironmentCreated, void());
     67 
     68  private:
     69   DISALLOW_COPY_AND_ASSIGN(MockNetworkListener);
     70 };
     71 
     72 bool MockDaemonListener::OnMessageReceived(const IPC::Message& message) {
     73   bool handled = true;
     74   IPC_BEGIN_MESSAGE_MAP(MockDaemonListener, message)
     75     IPC_MESSAGE_HANDLER(ChromotingDesktopDaemonMsg_DesktopAttached,
     76                         OnDesktopAttached)
     77     IPC_MESSAGE_UNHANDLED(handled = false)
     78   IPC_END_MESSAGE_MAP()
     79 
     80   EXPECT_TRUE(handled);
     81   return handled;
     82 }
     83 
     84 bool MockNetworkListener::OnMessageReceived(const IPC::Message& message) {
     85   bool handled = true;
     86 
     87   // TODO(alexeypa): handle received messages here.
     88 
     89   EXPECT_TRUE(handled);
     90   return handled;
     91 }
     92 
     93 }  // namespace
     94 
     95 class DesktopProcessTest : public testing::Test {
     96  public:
     97   DesktopProcessTest();
     98   virtual ~DesktopProcessTest();
     99 
    100   // testing::Test overrides
    101   virtual void SetUp() OVERRIDE;
    102   virtual void TearDown() OVERRIDE;
    103 
    104   // MockDaemonListener mocks
    105   void ConnectNetworkChannel(IPC::PlatformFileForTransit desktop_process);
    106   void OnDesktopAttached(IPC::PlatformFileForTransit desktop_process);
    107 
    108   // Creates a DesktopEnvironment with a fake webrtc::ScreenCapturer, to mock
    109   // DesktopEnvironmentFactory::Create().
    110   DesktopEnvironment* CreateDesktopEnvironment();
    111 
    112   // Creates a dummy InputInjector, to mock
    113   // DesktopEnvironment::CreateInputInjector().
    114   InputInjector* CreateInputInjector();
    115 
    116   // Creates a fake webrtc::ScreenCapturer, to mock
    117   // DesktopEnvironment::CreateVideoCapturer().
    118   webrtc::ScreenCapturer* CreateVideoCapturer();
    119 
    120   // Disconnects the daemon-to-desktop channel causing the desktop process to
    121   // exit.
    122   void DisconnectChannels();
    123 
    124   // Posts DisconnectChannels() to |message_loop_|.
    125   void PostDisconnectChannels();
    126 
    127   // Runs the desktop process code in a separate thread.
    128   void RunDesktopProcess();
    129 
    130   // Creates the desktop process and sends a crash request to it.
    131   void RunDeathTest();
    132 
    133   // Sends a crash request to the desktop process.
    134   void SendCrashRequest();
    135 
    136   // Requests the desktop process to start the desktop session agent.
    137   void SendStartSessionAgent();
    138 
    139  protected:
    140   // The daemon's end of the daemon-to-desktop channel.
    141   scoped_ptr<IPC::ChannelProxy> daemon_channel_;
    142 
    143   // Delegate that is passed to |daemon_channel_|.
    144   MockDaemonListener daemon_listener_;
    145 
    146   // Runs the daemon's end of the channel.
    147   base::MessageLoop message_loop_;
    148 
    149   scoped_refptr<AutoThreadTaskRunner> io_task_runner_;
    150 
    151   // The network's end of the network-to-desktop channel.
    152   scoped_ptr<IPC::ChannelProxy> network_channel_;
    153 
    154   // Delegate that is passed to |network_channel_|.
    155   MockNetworkListener network_listener_;
    156 };
    157 
    158 DesktopProcessTest::DesktopProcessTest()
    159     : message_loop_(base::MessageLoop::TYPE_UI) {}
    160 
    161 DesktopProcessTest::~DesktopProcessTest() {
    162 }
    163 
    164 void DesktopProcessTest::SetUp() {
    165 }
    166 
    167 void DesktopProcessTest::TearDown() {
    168 }
    169 
    170 void DesktopProcessTest::ConnectNetworkChannel(
    171     IPC::PlatformFileForTransit desktop_process) {
    172 
    173 #if defined(OS_POSIX)
    174   IPC::ChannelHandle channel_handle(std::string(), desktop_process);
    175 #elif defined(OS_WIN)
    176   IPC::ChannelHandle channel_handle(desktop_process);
    177 #endif  // defined(OS_WIN)
    178 
    179   network_channel_.reset(new IPC::ChannelProxy(channel_handle,
    180                                                IPC::Channel::MODE_CLIENT,
    181                                                &network_listener_,
    182                                                io_task_runner_.get()));
    183 }
    184 
    185 void DesktopProcessTest::OnDesktopAttached(
    186     IPC::PlatformFileForTransit desktop_process) {
    187 #if defined(OS_POSIX)
    188     DCHECK(desktop_process.auto_close);
    189 
    190     base::ClosePlatformFile(desktop_process.fd);
    191 #endif  // defined(OS_POSIX)
    192 }
    193 
    194 DesktopEnvironment* DesktopProcessTest::CreateDesktopEnvironment() {
    195   MockDesktopEnvironment* desktop_environment = new MockDesktopEnvironment();
    196   EXPECT_CALL(*desktop_environment, CreateAudioCapturerPtr())
    197       .Times(0);
    198   EXPECT_CALL(*desktop_environment, CreateInputInjectorPtr())
    199       .Times(AtMost(1))
    200       .WillOnce(Invoke(this, &DesktopProcessTest::CreateInputInjector));
    201   EXPECT_CALL(*desktop_environment, CreateScreenControlsPtr())
    202       .Times(AtMost(1));
    203   EXPECT_CALL(*desktop_environment, CreateVideoCapturerPtr())
    204       .Times(AtMost(1))
    205       .WillOnce(Invoke(this, &DesktopProcessTest::CreateVideoCapturer));
    206   EXPECT_CALL(*desktop_environment, GetCapabilities())
    207       .Times(AtMost(1));
    208   EXPECT_CALL(*desktop_environment, SetCapabilities(_))
    209       .Times(AtMost(1));
    210 
    211   // Notify the test that the desktop environment has been created.
    212   network_listener_.OnDesktopEnvironmentCreated();
    213   return desktop_environment;
    214 }
    215 
    216 InputInjector* DesktopProcessTest::CreateInputInjector() {
    217   MockInputInjector* input_injector = new MockInputInjector();
    218   EXPECT_CALL(*input_injector, StartPtr(_));
    219   return input_injector;
    220 }
    221 
    222 webrtc::ScreenCapturer* DesktopProcessTest::CreateVideoCapturer() {
    223   return new ScreenCapturerFake();
    224 }
    225 
    226 void DesktopProcessTest::DisconnectChannels() {
    227   daemon_channel_.reset();
    228   network_channel_.reset();
    229   io_task_runner_ = NULL;
    230 }
    231 
    232 void DesktopProcessTest::PostDisconnectChannels() {
    233   message_loop_.PostTask(FROM_HERE, base::Bind(
    234       &DesktopProcessTest::DisconnectChannels, base::Unretained(this)));
    235 }
    236 
    237 void DesktopProcessTest::RunDesktopProcess() {
    238   base::RunLoop run_loop;
    239   base::Closure quit_ui_task_runner = base::Bind(
    240       base::IgnoreResult(&base::SingleThreadTaskRunner::PostTask),
    241       message_loop_.message_loop_proxy(),
    242       FROM_HERE, run_loop.QuitClosure());
    243   scoped_refptr<AutoThreadTaskRunner> ui_task_runner = new AutoThreadTaskRunner(
    244       message_loop_.message_loop_proxy(), quit_ui_task_runner);
    245 
    246   io_task_runner_ = AutoThread::CreateWithType(
    247       "IPC thread", ui_task_runner, base::MessageLoop::TYPE_IO);
    248 
    249   std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID();
    250   daemon_channel_.reset(new IPC::ChannelProxy(IPC::ChannelHandle(channel_name),
    251                                               IPC::Channel::MODE_SERVER,
    252                                               &daemon_listener_,
    253                                               io_task_runner_.get()));
    254 
    255   scoped_ptr<MockDesktopEnvironmentFactory> desktop_environment_factory(
    256       new MockDesktopEnvironmentFactory());
    257   EXPECT_CALL(*desktop_environment_factory, CreatePtr())
    258       .Times(AnyNumber())
    259       .WillRepeatedly(Invoke(this,
    260                              &DesktopProcessTest::CreateDesktopEnvironment));
    261   EXPECT_CALL(*desktop_environment_factory, SupportsAudioCapture())
    262       .Times(AnyNumber())
    263       .WillRepeatedly(Return(false));
    264 
    265   DesktopProcess desktop_process(ui_task_runner, io_task_runner_, channel_name);
    266   EXPECT_TRUE(desktop_process.Start(
    267       desktop_environment_factory.PassAs<DesktopEnvironmentFactory>()));
    268 
    269   ui_task_runner = NULL;
    270   run_loop.Run();
    271 }
    272 
    273 void DesktopProcessTest::RunDeathTest() {
    274   InSequence s;
    275   EXPECT_CALL(daemon_listener_, OnChannelConnected(_));
    276   EXPECT_CALL(daemon_listener_, OnDesktopAttached(_))
    277       .WillOnce(DoAll(
    278           Invoke(this, &DesktopProcessTest::OnDesktopAttached),
    279           InvokeWithoutArgs(this, &DesktopProcessTest::SendCrashRequest)));
    280 
    281   RunDesktopProcess();
    282 }
    283 
    284 void DesktopProcessTest::SendCrashRequest() {
    285   tracked_objects::Location location = FROM_HERE;
    286   daemon_channel_->Send(new ChromotingDaemonMsg_Crash(
    287       location.function_name(), location.file_name(), location.line_number()));
    288 }
    289 
    290 void DesktopProcessTest::SendStartSessionAgent() {
    291   network_channel_->Send(new ChromotingNetworkDesktopMsg_StartSessionAgent(
    292       "user@domain/rest-of-jid", ScreenResolution(), false));
    293 }
    294 
    295 // Launches the desktop process and waits when it connects back.
    296 TEST_F(DesktopProcessTest, Basic) {
    297   InSequence s;
    298   EXPECT_CALL(daemon_listener_, OnChannelConnected(_));
    299   EXPECT_CALL(daemon_listener_, OnDesktopAttached(_))
    300       .WillOnce(DoAll(
    301           Invoke(this, &DesktopProcessTest::OnDesktopAttached),
    302           InvokeWithoutArgs(this, &DesktopProcessTest::DisconnectChannels)));
    303 
    304   RunDesktopProcess();
    305 }
    306 
    307 // Launches the desktop process and waits when it connects back.
    308 TEST_F(DesktopProcessTest, ConnectNetworkChannel) {
    309   InSequence s;
    310   EXPECT_CALL(daemon_listener_, OnChannelConnected(_));
    311   EXPECT_CALL(daemon_listener_, OnDesktopAttached(_))
    312       .WillOnce(Invoke(this, &DesktopProcessTest::ConnectNetworkChannel));
    313   EXPECT_CALL(network_listener_, OnChannelConnected(_))
    314       .WillOnce(InvokeWithoutArgs(
    315           this, &DesktopProcessTest::DisconnectChannels));
    316 
    317   RunDesktopProcess();
    318 }
    319 
    320 // Launches the desktop process, waits when it connects back and starts
    321 // the desktop session agent.
    322 TEST_F(DesktopProcessTest, StartSessionAgent) {
    323   {
    324     InSequence s;
    325     EXPECT_CALL(daemon_listener_, OnChannelConnected(_));
    326     EXPECT_CALL(daemon_listener_, OnDesktopAttached(_))
    327         .WillOnce(Invoke(this, &DesktopProcessTest::ConnectNetworkChannel));
    328     EXPECT_CALL(network_listener_, OnChannelConnected(_))
    329         .WillOnce(InvokeWithoutArgs(
    330             this, &DesktopProcessTest::SendStartSessionAgent));
    331   }
    332 
    333   EXPECT_CALL(network_listener_, OnDesktopEnvironmentCreated())
    334       .WillOnce(InvokeWithoutArgs(
    335           this, &DesktopProcessTest::PostDisconnectChannels));
    336 
    337   RunDesktopProcess();
    338 }
    339 
    340 // Run the desktop process and ask it to crash.
    341 TEST_F(DesktopProcessTest, DeathTest) {
    342   testing::GTEST_FLAG(death_test_style) = "threadsafe";
    343 
    344   EXPECT_DEATH(RunDeathTest(), "");
    345 }
    346 
    347 }  // namespace remoting
    348