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/fake_desktop_capturer.h"
23#include "remoting/host/host_exit_codes.h"
24#include "remoting/host/host_mock_objects.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
31using testing::_;
32using testing::AnyNumber;
33using testing::AtMost;
34using testing::InSequence;
35using testing::Return;
36
37namespace remoting {
38
39namespace {
40
41class 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
56class 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
72bool 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
84bool 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
95class 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::DesktopCapturer, 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::DesktopCapturer, to mock
117  // DesktopEnvironment::CreateVideoCapturer().
118  webrtc::DesktopCapturer* 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::MessageLoopForUI 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
158DesktopProcessTest::DesktopProcessTest() {}
159
160DesktopProcessTest::~DesktopProcessTest() {
161}
162
163void DesktopProcessTest::SetUp() {
164}
165
166void DesktopProcessTest::TearDown() {
167}
168
169void DesktopProcessTest::ConnectNetworkChannel(
170    IPC::PlatformFileForTransit desktop_process) {
171
172#if defined(OS_POSIX)
173  IPC::ChannelHandle channel_handle(std::string(), desktop_process);
174#elif defined(OS_WIN)
175  IPC::ChannelHandle channel_handle(desktop_process);
176#endif  // defined(OS_WIN)
177
178  network_channel_ = IPC::ChannelProxy::Create(channel_handle,
179                                               IPC::Channel::MODE_CLIENT,
180                                               &network_listener_,
181                                               io_task_runner_.get());
182}
183
184void DesktopProcessTest::OnDesktopAttached(
185    IPC::PlatformFileForTransit desktop_process) {
186#if defined(OS_POSIX)
187    DCHECK(desktop_process.auto_close);
188
189    base::File closer(IPC::PlatformFileForTransitToFile(desktop_process));
190#endif  // defined(OS_POSIX)
191}
192
193DesktopEnvironment* DesktopProcessTest::CreateDesktopEnvironment() {
194  MockDesktopEnvironment* desktop_environment = new MockDesktopEnvironment();
195  EXPECT_CALL(*desktop_environment, CreateAudioCapturerPtr())
196      .Times(0);
197  EXPECT_CALL(*desktop_environment, CreateInputInjectorPtr())
198      .Times(AtMost(1))
199      .WillOnce(Invoke(this, &DesktopProcessTest::CreateInputInjector));
200  EXPECT_CALL(*desktop_environment, CreateScreenControlsPtr())
201      .Times(AtMost(1));
202  EXPECT_CALL(*desktop_environment, CreateVideoCapturerPtr())
203      .Times(AtMost(1))
204      .WillOnce(Invoke(this, &DesktopProcessTest::CreateVideoCapturer));
205  EXPECT_CALL(*desktop_environment, GetCapabilities())
206      .Times(AtMost(1));
207  EXPECT_CALL(*desktop_environment, SetCapabilities(_))
208      .Times(AtMost(1));
209
210  // Notify the test that the desktop environment has been created.
211  network_listener_.OnDesktopEnvironmentCreated();
212  return desktop_environment;
213}
214
215InputInjector* DesktopProcessTest::CreateInputInjector() {
216  MockInputInjector* input_injector = new MockInputInjector();
217  EXPECT_CALL(*input_injector, StartPtr(_));
218  return input_injector;
219}
220
221webrtc::DesktopCapturer* DesktopProcessTest::CreateVideoCapturer() {
222  return new FakeDesktopCapturer();
223}
224
225void DesktopProcessTest::DisconnectChannels() {
226  daemon_channel_.reset();
227  network_channel_.reset();
228  io_task_runner_ = NULL;
229}
230
231void DesktopProcessTest::PostDisconnectChannels() {
232  message_loop_.PostTask(FROM_HERE, base::Bind(
233      &DesktopProcessTest::DisconnectChannels, base::Unretained(this)));
234}
235
236void DesktopProcessTest::RunDesktopProcess() {
237  base::RunLoop run_loop;
238  base::Closure quit_ui_task_runner = base::Bind(
239      base::IgnoreResult(&base::SingleThreadTaskRunner::PostTask),
240      message_loop_.message_loop_proxy(),
241      FROM_HERE, run_loop.QuitClosure());
242  scoped_refptr<AutoThreadTaskRunner> ui_task_runner = new AutoThreadTaskRunner(
243      message_loop_.message_loop_proxy(), quit_ui_task_runner);
244
245  io_task_runner_ = AutoThread::CreateWithType(
246      "IPC thread", ui_task_runner, base::MessageLoop::TYPE_IO);
247
248  std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID();
249  daemon_channel_ = IPC::ChannelProxy::Create(IPC::ChannelHandle(channel_name),
250                                              IPC::Channel::MODE_SERVER,
251                                              &daemon_listener_,
252                                              io_task_runner_.get());
253
254  scoped_ptr<MockDesktopEnvironmentFactory> desktop_environment_factory(
255      new MockDesktopEnvironmentFactory());
256  EXPECT_CALL(*desktop_environment_factory, CreatePtr())
257      .Times(AnyNumber())
258      .WillRepeatedly(Invoke(this,
259                             &DesktopProcessTest::CreateDesktopEnvironment));
260  EXPECT_CALL(*desktop_environment_factory, SupportsAudioCapture())
261      .Times(AnyNumber())
262      .WillRepeatedly(Return(false));
263
264  DesktopProcess desktop_process(ui_task_runner, io_task_runner_, channel_name);
265  EXPECT_TRUE(desktop_process.Start(
266      desktop_environment_factory.PassAs<DesktopEnvironmentFactory>()));
267
268  ui_task_runner = NULL;
269  run_loop.Run();
270}
271
272void DesktopProcessTest::RunDeathTest() {
273  InSequence s;
274  EXPECT_CALL(daemon_listener_, OnChannelConnected(_));
275  EXPECT_CALL(daemon_listener_, OnDesktopAttached(_))
276      .WillOnce(DoAll(
277          Invoke(this, &DesktopProcessTest::OnDesktopAttached),
278          InvokeWithoutArgs(this, &DesktopProcessTest::SendCrashRequest)));
279
280  RunDesktopProcess();
281}
282
283void DesktopProcessTest::SendCrashRequest() {
284  tracked_objects::Location location = FROM_HERE;
285  daemon_channel_->Send(new ChromotingDaemonMsg_Crash(
286      location.function_name(), location.file_name(), location.line_number()));
287}
288
289void DesktopProcessTest::SendStartSessionAgent() {
290  network_channel_->Send(new ChromotingNetworkDesktopMsg_StartSessionAgent(
291      "user@domain/rest-of-jid", ScreenResolution(), false));
292}
293
294// Launches the desktop process and waits when it connects back.
295TEST_F(DesktopProcessTest, Basic) {
296  InSequence s;
297  EXPECT_CALL(daemon_listener_, OnChannelConnected(_));
298  EXPECT_CALL(daemon_listener_, OnDesktopAttached(_))
299      .WillOnce(DoAll(
300          Invoke(this, &DesktopProcessTest::OnDesktopAttached),
301          InvokeWithoutArgs(this, &DesktopProcessTest::DisconnectChannels)));
302
303  RunDesktopProcess();
304}
305
306// Launches the desktop process and waits when it connects back.
307TEST_F(DesktopProcessTest, ConnectNetworkChannel) {
308  InSequence s;
309  EXPECT_CALL(daemon_listener_, OnChannelConnected(_));
310  EXPECT_CALL(daemon_listener_, OnDesktopAttached(_))
311      .WillOnce(Invoke(this, &DesktopProcessTest::ConnectNetworkChannel));
312  EXPECT_CALL(network_listener_, OnChannelConnected(_))
313      .WillOnce(InvokeWithoutArgs(
314          this, &DesktopProcessTest::DisconnectChannels));
315
316  RunDesktopProcess();
317}
318
319// Launches the desktop process, waits when it connects back and starts
320// the desktop session agent.
321TEST_F(DesktopProcessTest, StartSessionAgent) {
322  {
323    InSequence s;
324    EXPECT_CALL(daemon_listener_, OnChannelConnected(_));
325    EXPECT_CALL(daemon_listener_, OnDesktopAttached(_))
326        .WillOnce(Invoke(this, &DesktopProcessTest::ConnectNetworkChannel));
327    EXPECT_CALL(network_listener_, OnChannelConnected(_))
328        .WillOnce(InvokeWithoutArgs(
329            this, &DesktopProcessTest::SendStartSessionAgent));
330  }
331
332  EXPECT_CALL(network_listener_, OnDesktopEnvironmentCreated())
333      .WillOnce(InvokeWithoutArgs(
334          this, &DesktopProcessTest::PostDisconnectChannels));
335
336  RunDesktopProcess();
337}
338
339// Run the desktop process and ask it to crash.
340TEST_F(DesktopProcessTest, DeathTest) {
341  testing::GTEST_FLAG(death_test_style) = "threadsafe";
342
343  EXPECT_DEATH(RunDeathTest(), "");
344}
345
346}  // namespace remoting
347