• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SetScreenResolution(const ScreenResolution & resolution)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 
FakeDesktopSession(DaemonProcess * daemon_process,int id)82 FakeDesktopSession::FakeDesktopSession(DaemonProcess* daemon_process, int id)
83     : DesktopSession(daemon_process, id) {
84 }
85 
~FakeDesktopSession()86 FakeDesktopSession::~FakeDesktopSession() {
87 }
88 
MockDaemonProcess(scoped_refptr<AutoThreadTaskRunner> caller_task_runner,scoped_refptr<AutoThreadTaskRunner> io_task_runner,const base::Closure & stopped_callback)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 
~MockDaemonProcess()96 MockDaemonProcess::~MockDaemonProcess() {
97 }
98 
DoCreateDesktopSession(int terminal_id,const ScreenResolution & resolution,bool virtual_terminal)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 
OnMessageReceived(const IPC::Message & message)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 
SendToNetwork(IPC::Message * message)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 
desktop_sessions() const143   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 
DaemonProcessTest()154 DaemonProcessTest::DaemonProcessTest() : terminal_id_(0) {
155 }
156 
~DaemonProcessTest()157 DaemonProcessTest::~DaemonProcessTest() {
158 }
159 
SetUp()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 
TearDown()182 void DaemonProcessTest::TearDown() {
183   daemon_process_->Stop();
184   message_loop_.Run();
185 }
186 
DoCreateDesktopSession(int terminal_id)187 DesktopSession* DaemonProcessTest::DoCreateDesktopSession(int terminal_id) {
188   return new FakeDesktopSession(daemon_process_.get(), terminal_id);
189 }
190 
DoCrashNetworkProcess(const tracked_objects::Location & location)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 
LaunchNetworkProcess()199 void DaemonProcessTest::LaunchNetworkProcess() {
200   terminal_id_ = 0;
201   daemon_process_->OnChannelConnected(0);
202 }
203 
DeleteDaemonProcess()204 void DaemonProcessTest::DeleteDaemonProcess() {
205   daemon_process_.reset();
206 }
207 
QuitMessageLoop()208 void DaemonProcessTest::QuitMessageLoop() {
209   message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
210 }
211 
StartDaemonProcess()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 
TEST_F(DaemonProcessTest,OpenClose)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 
TEST_F(DaemonProcessTest,CallCloseDesktopSession)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.
TEST_F(DaemonProcessTest,DoubleDisconnectTerminal)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.
TEST_F(DaemonProcessTest,InvalidDisconnectTerminal)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.
TEST_F(DaemonProcessTest,InvalidConnectTerminal)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