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.
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"
22 using testing::AnyNumber
;
23 using testing::InSequence
;
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
38 // Provides a public constructor allowing the test to create instances of
39 // DesktopSession directly.
40 class FakeDesktopSession
: public DesktopSession
{
42 FakeDesktopSession(DaemonProcess
* daemon_process
, int id
);
43 ~FakeDesktopSession() override
;
45 void SetScreenResolution(const ScreenResolution
& resolution
) override
{}
48 DISALLOW_COPY_AND_ASSIGN(FakeDesktopSession
);
51 class MockDaemonProcess
: public DaemonProcess
{
54 scoped_refptr
<AutoThreadTaskRunner
> caller_task_runner
,
55 scoped_refptr
<AutoThreadTaskRunner
> io_task_runner
,
56 const base::Closure
& stopped_callback
);
57 virtual ~MockDaemonProcess();
59 virtual scoped_ptr
<DesktopSession
> DoCreateDesktopSession(
61 const ScreenResolution
& resolution
,
62 bool virtual_terminal
) override
;
64 virtual bool OnMessageReceived(const IPC::Message
& message
) override
;
65 virtual void SendToNetwork(IPC::Message
* message
) override
;
67 MOCK_METHOD1(Received
, void(const IPC::Message
&));
68 MOCK_METHOD1(Sent
, void(const IPC::Message
&));
70 MOCK_METHOD3(OnDesktopSessionAgentAttached
,
71 bool(int, base::ProcessHandle
, IPC::PlatformFileForTransit
));
73 MOCK_METHOD1(DoCreateDesktopSessionPtr
, DesktopSession
*(int));
74 MOCK_METHOD1(DoCrashNetworkProcess
, void(const tracked_objects::Location
&));
75 MOCK_METHOD0(LaunchNetworkProcess
, void());
78 DISALLOW_COPY_AND_ASSIGN(MockDaemonProcess
);
81 FakeDesktopSession::FakeDesktopSession(DaemonProcess
* daemon_process
, int id
)
82 : DesktopSession(daemon_process
, id
) {
85 FakeDesktopSession::~FakeDesktopSession() {
88 MockDaemonProcess::MockDaemonProcess(
89 scoped_refptr
<AutoThreadTaskRunner
> caller_task_runner
,
90 scoped_refptr
<AutoThreadTaskRunner
> io_task_runner
,
91 const base::Closure
& stopped_callback
)
92 : DaemonProcess(caller_task_runner
, io_task_runner
, stopped_callback
) {
95 MockDaemonProcess::~MockDaemonProcess() {
98 scoped_ptr
<DesktopSession
> MockDaemonProcess::DoCreateDesktopSession(
100 const ScreenResolution
& resolution
,
101 bool virtual_terminal
) {
102 return make_scoped_ptr(DoCreateDesktopSessionPtr(terminal_id
));
105 bool MockDaemonProcess::OnMessageReceived(const IPC::Message
& message
) {
106 // Notify the mock method.
109 // Call the actual handler.
110 return DaemonProcess::OnMessageReceived(message
);
113 void MockDaemonProcess::SendToNetwork(IPC::Message
* message
) {
114 // Notify the mock method.
121 class DaemonProcessTest
: public testing::Test
{
124 ~DaemonProcessTest() override
;
126 void SetUp() override
;
127 void TearDown() override
;
129 // DaemonProcess mocks
130 DesktopSession
* DoCreateDesktopSession(int terminal_id
);
131 void DoCrashNetworkProcess(const tracked_objects::Location
& location
);
132 void LaunchNetworkProcess();
134 // Deletes |daemon_process_|.
135 void DeleteDaemonProcess();
137 // Quits |message_loop_|.
138 void QuitMessageLoop();
140 void StartDaemonProcess();
142 const DaemonProcess::DesktopSessionList
& desktop_sessions() const {
143 return daemon_process_
->desktop_sessions();
147 base::MessageLoopForIO message_loop_
;
149 scoped_ptr
<MockDaemonProcess
> daemon_process_
;
153 DaemonProcessTest::DaemonProcessTest() : terminal_id_(0) {
156 DaemonProcessTest::~DaemonProcessTest() {
159 void DaemonProcessTest::SetUp() {
160 scoped_refptr
<AutoThreadTaskRunner
> task_runner
= new AutoThreadTaskRunner(
161 message_loop_
.message_loop_proxy(),
162 base::Bind(&DaemonProcessTest::QuitMessageLoop
,
163 base::Unretained(this)));
164 daemon_process_
.reset(
165 new MockDaemonProcess(task_runner
, task_runner
,
166 base::Bind(&DaemonProcessTest::DeleteDaemonProcess
,
167 base::Unretained(this))));
169 // Set up daemon process mocks.
170 EXPECT_CALL(*daemon_process_
, DoCreateDesktopSessionPtr(_
))
172 .WillRepeatedly(Invoke(this, &DaemonProcessTest::DoCreateDesktopSession
));
173 EXPECT_CALL(*daemon_process_
, DoCrashNetworkProcess(_
))
175 .WillRepeatedly(Invoke(this, &DaemonProcessTest::DoCrashNetworkProcess
));
176 EXPECT_CALL(*daemon_process_
, LaunchNetworkProcess())
178 .WillRepeatedly(Invoke(this, &DaemonProcessTest::LaunchNetworkProcess
));
181 void DaemonProcessTest::TearDown() {
182 daemon_process_
->Stop();
186 DesktopSession
* DaemonProcessTest::DoCreateDesktopSession(int terminal_id
) {
187 return new FakeDesktopSession(daemon_process_
.get(), terminal_id
);
190 void DaemonProcessTest::DoCrashNetworkProcess(
191 const tracked_objects::Location
& location
) {
192 daemon_process_
->SendToNetwork(
193 new ChromotingDaemonMsg_Crash(location
.function_name(),
194 location
.file_name(),
195 location
.line_number()));
198 void DaemonProcessTest::LaunchNetworkProcess() {
200 daemon_process_
->OnChannelConnected(0);
203 void DaemonProcessTest::DeleteDaemonProcess() {
204 daemon_process_
.reset();
207 void DaemonProcessTest::QuitMessageLoop() {
208 message_loop_
.PostTask(FROM_HERE
, base::MessageLoop::QuitClosure());
211 void DaemonProcessTest::StartDaemonProcess() {
212 // DaemonProcess::Initialize() sets up the config watcher that this test does
213 // not support. Launch the process directly.
214 daemon_process_
->LaunchNetworkProcess();
217 MATCHER_P(Message
, type
, "") {
218 return arg
.type() == static_cast<uint32
>(type
);
221 TEST_F(DaemonProcessTest
, OpenClose
) {
223 EXPECT_CALL(*daemon_process_
, Sent(Message(kMessageConfiguration
)));
224 EXPECT_CALL(*daemon_process_
, Received(Message(kMessageConnectTerminal
)));
225 EXPECT_CALL(*daemon_process_
, Received(Message(kMessageDisconnectTerminal
)));
226 EXPECT_CALL(*daemon_process_
, Sent(Message(kMessageTerminalDisconnected
)));
228 StartDaemonProcess();
230 int id
= terminal_id_
++;
231 ScreenResolution resolution
;
233 EXPECT_TRUE(daemon_process_
->OnMessageReceived(
234 ChromotingNetworkHostMsg_ConnectTerminal(id
, resolution
, false)));
235 EXPECT_EQ(1u, desktop_sessions().size());
236 EXPECT_EQ(id
, desktop_sessions().front()->id());
238 EXPECT_TRUE(daemon_process_
->OnMessageReceived(
239 ChromotingNetworkHostMsg_DisconnectTerminal(id
)));
240 EXPECT_TRUE(desktop_sessions().empty());
243 TEST_F(DaemonProcessTest
, CallCloseDesktopSession
) {
245 EXPECT_CALL(*daemon_process_
, Sent(Message(kMessageConfiguration
)));
246 EXPECT_CALL(*daemon_process_
, Received(Message(kMessageConnectTerminal
)));
247 EXPECT_CALL(*daemon_process_
, Sent(Message(kMessageTerminalDisconnected
)));
249 StartDaemonProcess();
251 int id
= terminal_id_
++;
252 ScreenResolution resolution
;
254 EXPECT_TRUE(daemon_process_
->OnMessageReceived(
255 ChromotingNetworkHostMsg_ConnectTerminal(id
, resolution
, false)));
256 EXPECT_EQ(1u, desktop_sessions().size());
257 EXPECT_EQ(id
, desktop_sessions().front()->id());
259 daemon_process_
->CloseDesktopSession(id
);
260 EXPECT_TRUE(desktop_sessions().empty());
263 // Sends two CloseDesktopSession messages and expects the second one to be
265 TEST_F(DaemonProcessTest
, DoubleDisconnectTerminal
) {
267 EXPECT_CALL(*daemon_process_
, Sent(Message(kMessageConfiguration
)));
268 EXPECT_CALL(*daemon_process_
, Received(Message(kMessageConnectTerminal
)));
269 EXPECT_CALL(*daemon_process_
, Received(Message(kMessageDisconnectTerminal
)));
270 EXPECT_CALL(*daemon_process_
, Sent(Message(kMessageTerminalDisconnected
)));
271 EXPECT_CALL(*daemon_process_
, Received(Message(kMessageDisconnectTerminal
)));
273 StartDaemonProcess();
275 int id
= terminal_id_
++;
276 ScreenResolution resolution
;
278 EXPECT_TRUE(daemon_process_
->OnMessageReceived(
279 ChromotingNetworkHostMsg_ConnectTerminal(id
, resolution
, false)));
280 EXPECT_EQ(1u, desktop_sessions().size());
281 EXPECT_EQ(id
, desktop_sessions().front()->id());
283 EXPECT_TRUE(daemon_process_
->OnMessageReceived(
284 ChromotingNetworkHostMsg_DisconnectTerminal(id
)));
285 EXPECT_TRUE(desktop_sessions().empty());
287 EXPECT_TRUE(daemon_process_
->OnMessageReceived(
288 ChromotingNetworkHostMsg_DisconnectTerminal(id
)));
289 EXPECT_TRUE(desktop_sessions().empty());
292 // Tries to close an invalid terminal ID and expects the network process to be
294 TEST_F(DaemonProcessTest
, InvalidDisconnectTerminal
) {
296 EXPECT_CALL(*daemon_process_
, Sent(Message(kMessageConfiguration
)));
297 EXPECT_CALL(*daemon_process_
, Received(Message(kMessageDisconnectTerminal
)));
298 EXPECT_CALL(*daemon_process_
, Sent(Message(kMessageCrash
)))
299 .WillOnce(InvokeWithoutArgs(this,
300 &DaemonProcessTest::LaunchNetworkProcess
));
301 EXPECT_CALL(*daemon_process_
, Sent(Message(kMessageConfiguration
)));
303 StartDaemonProcess();
305 int id
= terminal_id_
++;
307 EXPECT_TRUE(daemon_process_
->OnMessageReceived(
308 ChromotingNetworkHostMsg_DisconnectTerminal(id
)));
309 EXPECT_TRUE(desktop_sessions().empty());
310 EXPECT_EQ(0, terminal_id_
);
313 // Tries to open an invalid terminal ID and expects the network process to be
315 TEST_F(DaemonProcessTest
, InvalidConnectTerminal
) {
317 EXPECT_CALL(*daemon_process_
, Sent(Message(kMessageConfiguration
)));
318 EXPECT_CALL(*daemon_process_
, Received(Message(kMessageConnectTerminal
)));
319 EXPECT_CALL(*daemon_process_
, Received(Message(kMessageConnectTerminal
)));
320 EXPECT_CALL(*daemon_process_
, Sent(Message(kMessageCrash
)))
321 .WillOnce(InvokeWithoutArgs(this,
322 &DaemonProcessTest::LaunchNetworkProcess
));
323 EXPECT_CALL(*daemon_process_
, Sent(Message(kMessageConfiguration
)));
325 StartDaemonProcess();
327 int id
= terminal_id_
++;
328 ScreenResolution resolution
;
330 EXPECT_TRUE(daemon_process_
->OnMessageReceived(
331 ChromotingNetworkHostMsg_ConnectTerminal(id
, resolution
, false)));
332 EXPECT_EQ(1u, desktop_sessions().size());
333 EXPECT_EQ(id
, desktop_sessions().front()->id());
335 EXPECT_TRUE(daemon_process_
->OnMessageReceived(
336 ChromotingNetworkHostMsg_ConnectTerminal(id
, resolution
, false)));
337 EXPECT_TRUE(desktop_sessions().empty());
338 EXPECT_EQ(0, terminal_id_
);
341 } // namespace remoting