daemon_process_unittest.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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.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
21using testing::_;
22using testing::AnyNumber;
23using testing::InSequence;
24
25namespace remoting {
26
27namespace {
28
29enum 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.
40class 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
52class 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
82FakeDesktopSession::FakeDesktopSession(DaemonProcess* daemon_process, int id)
83    : DesktopSession(daemon_process, id) {
84}
85
86FakeDesktopSession::~FakeDesktopSession() {
87}
88
89MockDaemonProcess::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
96MockDaemonProcess::~MockDaemonProcess() {
97}
98
99scoped_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
106bool 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
114void MockDaemonProcess::SendToNetwork(IPC::Message* message) {
115  // Notify the mock method.
116  Sent(*message);
117  delete message;
118}
119
120}  // namespace
121
122class 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
154DaemonProcessTest::DaemonProcessTest()
155    : message_loop_(base::MessageLoop::TYPE_IO), terminal_id_(0) {
156}
157
158DaemonProcessTest::~DaemonProcessTest() {
159}
160
161void 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
183void DaemonProcessTest::TearDown() {
184  daemon_process_->Stop();
185  message_loop_.Run();
186}
187
188DesktopSession* DaemonProcessTest::DoCreateDesktopSession(int terminal_id) {
189  return new FakeDesktopSession(daemon_process_.get(), terminal_id);
190}
191
192void 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
200void DaemonProcessTest::LaunchNetworkProcess() {
201  terminal_id_ = 0;
202  daemon_process_->OnChannelConnected(0);
203}
204
205void DaemonProcessTest::DeleteDaemonProcess() {
206  daemon_process_.reset();
207}
208
209void DaemonProcessTest::QuitMessageLoop() {
210  message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
211}
212
213void 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
219MATCHER_P(Message, type, "") {
220  return arg.type() == static_cast<uint32>(type);
221}
222
223TEST_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
245TEST_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.
267TEST_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.
296TEST_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.
317TEST_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