blob: 61c21d36c5166ab68282a5ee0ed756c471a9fc03 [file] [log] [blame]
levin@chromium.org5c528682011-03-28 10:54:15 +09001// Copyright (c) 2011 The Chromium Authors. All rights reserved.
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// These tests are POSIX only.
6
7#include "ipc/ipc_channel_posix.h"
8
9#include <fcntl.h>
10#include <sys/socket.h>
11#include <sys/un.h>
12#include <unistd.h>
13
14#include "base/basictypes.h"
15#include "base/eintr_wrapper.h"
16#include "base/file_path.h"
17#include "base/file_util.h"
levin@chromium.org5c528682011-03-28 10:54:15 +090018#include "base/memory/scoped_ptr.h"
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +090019#include "base/message_loop.h"
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +090020#include "base/test/multiprocess_test.h"
21#include "base/test/test_timeouts.h"
22#include "testing/multiprocess_func_list.h"
23
24namespace {
25
pkasting@chromium.orgc4f2de22011-09-01 09:46:33 +090026static const uint32 kQuitMessage = 47;
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +090027
28class IPCChannelPosixTestListener : public IPC::Channel::Listener {
29 public:
30 enum STATUS {
31 DISCONNECTED,
32 MESSAGE_RECEIVED,
33 CHANNEL_ERROR,
34 CONNECTED,
35 DENIED,
36 LISTEN_ERROR
37 };
38
39 IPCChannelPosixTestListener(bool quit_only_on_message)
40 : status_(DISCONNECTED), quit_only_on_message_(quit_only_on_message) {}
41
42 virtual ~IPCChannelPosixTestListener() {}
43
evan@chromium.orgecc3f072011-08-17 06:09:25 +090044 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
pkasting@chromium.orgc4f2de22011-09-01 09:46:33 +090045 EXPECT_EQ(message.type(), kQuitMessage);
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +090046 status_ = MESSAGE_RECEIVED;
47 QuitRunLoop();
jam@chromium.org8a2c7842010-12-24 15:19:28 +090048 return true;
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +090049 }
50
evan@chromium.orgecc3f072011-08-17 06:09:25 +090051 virtual void OnChannelConnected(int32 peer_pid) OVERRIDE {
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +090052 status_ = CONNECTED;
53 if (!quit_only_on_message_) {
54 QuitRunLoop();
55 }
56 }
57
evan@chromium.orgecc3f072011-08-17 06:09:25 +090058 virtual void OnChannelError() OVERRIDE {
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +090059 status_ = CHANNEL_ERROR;
60 if (!quit_only_on_message_) {
61 QuitRunLoop();
62 }
63 }
64
evan@chromium.orgecc3f072011-08-17 06:09:25 +090065 virtual void OnChannelDenied() OVERRIDE {
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +090066 status_ = DENIED;
67 if (!quit_only_on_message_) {
68 QuitRunLoop();
69 }
70 }
71
evan@chromium.orgecc3f072011-08-17 06:09:25 +090072 virtual void OnChannelListenError() OVERRIDE {
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +090073 status_ = LISTEN_ERROR;
74 if (!quit_only_on_message_) {
75 QuitRunLoop();
76 }
77 }
78
79 STATUS status() { return status_; }
80
81 void QuitRunLoop() {
82 MessageLoopForIO::current()->QuitNow();
83 }
84
85 private:
86 // The current status of the listener.
87 STATUS status_;
88 // If |quit_only_on_message_| then the listener will only break out of
pkasting@chromium.orgc4f2de22011-09-01 09:46:33 +090089 // the run loop when kQuitMessage is received.
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +090090 bool quit_only_on_message_;
91};
92
93} // namespace
94
95class IPCChannelPosixTest : public base::MultiProcessTest {
96 public:
97 static const char kConnectionSocketTestName[];
98 static void SetUpSocket(IPC::ChannelHandle *handle,
99 IPC::Channel::Mode mode);
100 static void SpinRunLoop(int milliseconds);
101
102 protected:
103 virtual void SetUp();
104 virtual void TearDown();
105
106private:
107 scoped_ptr<MessageLoopForIO> message_loop_;
108};
109
jrg@chromium.org2eb192e2011-11-04 09:14:16 +0900110#if defined(OS_ANDROID)
111const char IPCChannelPosixTest::kConnectionSocketTestName[] =
112 "/data/local/chrome_IPCChannelPosixTest__ConnectionSocket";
113#else
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +0900114const char IPCChannelPosixTest::kConnectionSocketTestName[] =
115 "/var/tmp/chrome_IPCChannelPosixTest__ConnectionSocket";
jrg@chromium.org2eb192e2011-11-04 09:14:16 +0900116#endif
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +0900117
118void IPCChannelPosixTest::SetUp() {
119 MultiProcessTest::SetUp();
120 // Construct a fresh IO Message loop for the duration of each test.
121 message_loop_.reset(new MessageLoopForIO());
122}
123
124void IPCChannelPosixTest::TearDown() {
125 message_loop_.reset(NULL);
126 MultiProcessTest::TearDown();
127}
128
129// Create up a socket and bind and listen to it, or connect it
130// depending on the |mode|.
131void IPCChannelPosixTest::SetUpSocket(IPC::ChannelHandle *handle,
132 IPC::Channel::Mode mode) {
133 const std::string& name = handle->name;
134
135 int socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
136 ASSERT_GE(socket_fd, 0) << name;
137 ASSERT_GE(fcntl(socket_fd, F_SETFL, O_NONBLOCK), 0);
138 struct sockaddr_un server_address = { 0 };
139 memset(&server_address, 0, sizeof(server_address));
140 server_address.sun_family = AF_UNIX;
141 int path_len = snprintf(server_address.sun_path, IPC::kMaxPipeNameLength,
142 "%s", name.c_str());
143 DCHECK_EQ(static_cast<int>(name.length()), path_len);
144 size_t server_address_len = offsetof(struct sockaddr_un,
145 sun_path) + path_len + 1;
146
147 if (mode == IPC::Channel::MODE_NAMED_SERVER) {
148 // Only one server at a time. Cleanup garbage if it exists.
149 unlink(name.c_str());
pkasting@chromium.orgc4f2de22011-09-01 09:46:33 +0900150 // Make sure the path we need exists.
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +0900151 FilePath path(name);
152 FilePath dir_path = path.DirName();
153 ASSERT_TRUE(file_util::CreateDirectory(dir_path));
154 ASSERT_GE(bind(socket_fd,
155 reinterpret_cast<struct sockaddr *>(&server_address),
156 server_address_len), 0) << server_address.sun_path
157 << ": " << strerror(errno)
158 << "(" << errno << ")";
159 ASSERT_GE(listen(socket_fd, SOMAXCONN), 0) << server_address.sun_path
160 << ": " << strerror(errno)
161 << "(" << errno << ")";
162 } else if (mode == IPC::Channel::MODE_NAMED_CLIENT) {
163 ASSERT_GE(connect(socket_fd,
164 reinterpret_cast<struct sockaddr *>(&server_address),
165 server_address_len), 0) << server_address.sun_path
166 << ": " << strerror(errno)
167 << "(" << errno << ")";
168 } else {
169 FAIL() << "Unknown mode " << mode;
170 }
171 handle->socket.fd = socket_fd;
172}
173
174void IPCChannelPosixTest::SpinRunLoop(int milliseconds) {
175 MessageLoopForIO *loop = MessageLoopForIO::current();
176 // Post a quit task so that this loop eventually ends and we don't hang
177 // in the case of a bad test. Usually, the run loop will quit sooner than
178 // that because all tests use a IPCChannelPosixTestListener which quits the
179 // current run loop on any channel activity.
180 loop->PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask(), milliseconds);
181 loop->Run();
182}
183
184TEST_F(IPCChannelPosixTest, BasicListen) {
jrg@chromium.org2eb192e2011-11-04 09:14:16 +0900185
186#if defined(OS_ANDROID)
187 const char* kChannelName = "/data/local/IPCChannelPosixTest_BasicListen";
188#else
189 const char* kChannelName = "/var/tmp/IPCChannelPosixTest_BasicListen";
190#endif
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +0900191 // Test creating a socket that is listening.
jrg@chromium.org2eb192e2011-11-04 09:14:16 +0900192 IPC::ChannelHandle handle(kChannelName);
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +0900193 SetUpSocket(&handle, IPC::Channel::MODE_NAMED_SERVER);
194 unlink(handle.name.c_str());
195 IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_SERVER, NULL);
196 ASSERT_TRUE(channel.Connect());
197 ASSERT_TRUE(channel.AcceptsConnections());
198 ASSERT_FALSE(channel.HasAcceptedConnection());
199 channel.ResetToAcceptingConnectionState();
200 ASSERT_FALSE(channel.HasAcceptedConnection());
201}
202
203TEST_F(IPCChannelPosixTest, BasicConnected) {
204 // Test creating a socket that is connected.
205 int pipe_fds[2];
206 ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds));
207 std::string socket_name("/var/tmp/IPCChannelPosixTest_BasicConnected");
208 ASSERT_GE(fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK), 0);
209
210 base::FileDescriptor fd(pipe_fds[0], false);
211 IPC::ChannelHandle handle(socket_name, fd);
212 IPC::Channel channel(handle, IPC::Channel::MODE_SERVER, NULL);
213 ASSERT_TRUE(channel.Connect());
214 ASSERT_FALSE(channel.AcceptsConnections());
215 channel.Close();
216 ASSERT_TRUE(HANDLE_EINTR(close(pipe_fds[1])) == 0);
217
218 // Make sure that we can use the socket that is created for us by
219 // a standard channel.
220 IPC::Channel channel2(socket_name, IPC::Channel::MODE_SERVER, NULL);
221 ASSERT_TRUE(channel2.Connect());
222 ASSERT_FALSE(channel2.AcceptsConnections());
223}
224
225TEST_F(IPCChannelPosixTest, AdvancedConnected) {
226 // Test creating a connection to an external process.
227 IPCChannelPosixTestListener listener(false);
228 IPC::ChannelHandle chan_handle(kConnectionSocketTestName);
229 SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER);
230 IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
231 ASSERT_TRUE(channel.Connect());
232 ASSERT_TRUE(channel.AcceptsConnections());
233 ASSERT_FALSE(channel.HasAcceptedConnection());
234
235 base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc",
236 false);
237 ASSERT_TRUE(handle);
238 SpinRunLoop(TestTimeouts::action_max_timeout_ms());
239 ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
240 ASSERT_TRUE(channel.HasAcceptedConnection());
pkasting@chromium.orgc4f2de22011-09-01 09:46:33 +0900241 IPC::Message* message = new IPC::Message(0, // routing_id
242 kQuitMessage, // message type
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +0900243 IPC::Message::PRIORITY_NORMAL);
244 channel.Send(message);
245 SpinRunLoop(TestTimeouts::action_timeout_ms());
246 int exit_code = 0;
247 EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
248 EXPECT_EQ(0, exit_code);
249 ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
250 ASSERT_FALSE(channel.HasAcceptedConnection());
251}
252
253TEST_F(IPCChannelPosixTest, ResetState) {
254 // Test creating a connection to an external process. Close the connection,
255 // but continue to listen and make sure another external process can connect
256 // to us.
257 IPCChannelPosixTestListener listener(false);
258 IPC::ChannelHandle chan_handle(kConnectionSocketTestName);
259 SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER);
260 IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
261 ASSERT_TRUE(channel.Connect());
262 ASSERT_TRUE(channel.AcceptsConnections());
263 ASSERT_FALSE(channel.HasAcceptedConnection());
264
265 base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc",
266 false);
267 ASSERT_TRUE(handle);
268 SpinRunLoop(TestTimeouts::action_max_timeout_ms());
269 ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
270 ASSERT_TRUE(channel.HasAcceptedConnection());
271 channel.ResetToAcceptingConnectionState();
272 ASSERT_FALSE(channel.HasAcceptedConnection());
273
274 base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixTestConnectionProc",
275 false);
276 ASSERT_TRUE(handle2);
277 SpinRunLoop(TestTimeouts::action_max_timeout_ms());
278 ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
279 ASSERT_TRUE(channel.HasAcceptedConnection());
pkasting@chromium.orgc4f2de22011-09-01 09:46:33 +0900280 IPC::Message* message = new IPC::Message(0, // routing_id
281 kQuitMessage, // message type
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +0900282 IPC::Message::PRIORITY_NORMAL);
283 channel.Send(message);
284 SpinRunLoop(TestTimeouts::action_timeout_ms());
285 EXPECT_TRUE(base::KillProcess(handle, 0, false));
286 int exit_code = 0;
287 EXPECT_TRUE(base::WaitForExitCode(handle2, &exit_code));
288 EXPECT_EQ(0, exit_code);
289 ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
290 ASSERT_FALSE(channel.HasAcceptedConnection());
291}
292
dmaclach@chromium.org344a3d52011-08-25 23:14:05 +0900293TEST_F(IPCChannelPosixTest, BadChannelName) {
294 // Test empty name
295 IPC::ChannelHandle handle("");
296 IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_SERVER, NULL);
297 ASSERT_FALSE(channel.Connect());
298
299 // Test name that is too long.
300 const char *kTooLongName = "This_is_a_very_long_name_to_proactively_implement"
301 "client-centered_synergy_through_top-line"
302 "platforms_Phosfluorescently_disintermediate_"
303 "clicks-and-mortar_best_practices_without_"
304 "future-proof_growth_strategies_Continually"
305 "pontificate_proactive_potentialities_before"
306 "leading-edge_processes";
307 EXPECT_GE(strlen(kTooLongName), IPC::kMaxPipeNameLength);
308 IPC::ChannelHandle handle2(kTooLongName);
309 IPC::Channel channel2(handle2, IPC::Channel::MODE_NAMED_SERVER, NULL);
310 EXPECT_FALSE(channel2.Connect());
311}
312
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +0900313TEST_F(IPCChannelPosixTest, MultiConnection) {
314 // Test setting up a connection to an external process, and then have
315 // another external process attempt to connect to us.
316 IPCChannelPosixTestListener listener(false);
317 IPC::ChannelHandle chan_handle(kConnectionSocketTestName);
318 SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER);
319 IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
320 ASSERT_TRUE(channel.Connect());
321 ASSERT_TRUE(channel.AcceptsConnections());
322 ASSERT_FALSE(channel.HasAcceptedConnection());
323
324 base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc",
325 false);
326 ASSERT_TRUE(handle);
327 SpinRunLoop(TestTimeouts::action_max_timeout_ms());
328 ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
329 ASSERT_TRUE(channel.HasAcceptedConnection());
330 base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixFailConnectionProc",
331 false);
332 ASSERT_TRUE(handle2);
333 SpinRunLoop(TestTimeouts::action_max_timeout_ms());
334 int exit_code = 0;
335 EXPECT_TRUE(base::WaitForExitCode(handle2, &exit_code));
336 EXPECT_EQ(exit_code, 0);
337 ASSERT_EQ(IPCChannelPosixTestListener::DENIED, listener.status());
338 ASSERT_TRUE(channel.HasAcceptedConnection());
pkasting@chromium.orgc4f2de22011-09-01 09:46:33 +0900339 IPC::Message* message = new IPC::Message(0, // routing_id
340 kQuitMessage, // message type
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +0900341 IPC::Message::PRIORITY_NORMAL);
342 channel.Send(message);
343 SpinRunLoop(TestTimeouts::action_timeout_ms());
344 EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
345 EXPECT_EQ(exit_code, 0);
346 ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
347 ASSERT_FALSE(channel.HasAcceptedConnection());
348}
349
dmaclach@chromium.org2812db62011-03-03 07:27:14 +0900350TEST_F(IPCChannelPosixTest, DoubleServer) {
351 // Test setting up two servers with the same name.
352 IPCChannelPosixTestListener listener(false);
353 IPCChannelPosixTestListener listener2(false);
354 IPC::ChannelHandle chan_handle(kConnectionSocketTestName);
355 IPC::Channel channel(chan_handle, IPC::Channel::MODE_SERVER, &listener);
356 IPC::Channel channel2(chan_handle, IPC::Channel::MODE_SERVER, &listener2);
357 ASSERT_TRUE(channel.Connect());
358 ASSERT_FALSE(channel2.Connect());
359}
360
361TEST_F(IPCChannelPosixTest, BadMode) {
362 // Test setting up two servers with a bad mode.
363 IPCChannelPosixTestListener listener(false);
364 IPC::ChannelHandle chan_handle(kConnectionSocketTestName);
365 IPC::Channel channel(chan_handle, IPC::Channel::MODE_NONE, &listener);
366 ASSERT_FALSE(channel.Connect());
367}
368
kkania@chromium.orgf37b4e52011-08-09 15:46:06 +0900369TEST_F(IPCChannelPosixTest, IsNamedServerInitialized) {
370 IPCChannelPosixTestListener listener(false);
371 IPC::ChannelHandle chan_handle(kConnectionSocketTestName);
372 ASSERT_TRUE(file_util::Delete(FilePath(kConnectionSocketTestName), false));
373 ASSERT_FALSE(IPC::Channel::IsNamedServerInitialized(
374 kConnectionSocketTestName));
375 IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
376 ASSERT_TRUE(IPC::Channel::IsNamedServerInitialized(
377 kConnectionSocketTestName));
378 channel.Close();
379 ASSERT_FALSE(IPC::Channel::IsNamedServerInitialized(
380 kConnectionSocketTestName));
381}
382
dmaclach@chromium.orgc1d3d422010-12-20 15:59:23 +0900383// A long running process that connects to us
384MULTIPROCESS_TEST_MAIN(IPCChannelPosixTestConnectionProc) {
385 MessageLoopForIO message_loop;
386 IPCChannelPosixTestListener listener(true);
387 IPC::ChannelHandle handle(IPCChannelPosixTest::kConnectionSocketTestName);
388 IPCChannelPosixTest::SetUpSocket(&handle, IPC::Channel::MODE_NAMED_CLIENT);
389 IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_CLIENT, &listener);
390 EXPECT_TRUE(channel.Connect());
391 IPCChannelPosixTest::SpinRunLoop(TestTimeouts::action_max_timeout_ms());
392 EXPECT_EQ(IPCChannelPosixTestListener::MESSAGE_RECEIVED, listener.status());
393 return 0;
394}
395
396// Simple external process that shouldn't be able to connect to us.
397MULTIPROCESS_TEST_MAIN(IPCChannelPosixFailConnectionProc) {
398 MessageLoopForIO message_loop;
399 IPCChannelPosixTestListener listener(false);
400 IPC::ChannelHandle handle(IPCChannelPosixTest::kConnectionSocketTestName);
401 IPCChannelPosixTest::SetUpSocket(&handle, IPC::Channel::MODE_NAMED_CLIENT);
402 IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_CLIENT, &listener);
403
404 // In this case connect may succeed or fail depending on if the packet
405 // actually gets sent at sendmsg. Since we never delay on send, we may not
406 // see the error. However even if connect succeeds, eventually we will get an
407 // error back since the channel will be closed when we attempt to read from
408 // it.
409 bool connected = channel.Connect();
410 if (connected) {
411 IPCChannelPosixTest::SpinRunLoop(TestTimeouts::action_max_timeout_ms());
412 EXPECT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
413 } else {
414 EXPECT_EQ(IPCChannelPosixTestListener::DISCONNECTED, listener.status());
415 }
416 return 0;
417}