OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2009 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 "build/build_config.h" | |
6 | |
7 #if defined(OS_WIN) | |
8 #include <windows.h> | |
9 #elif defined(OS_POSIX) | |
10 #include <sys/types.h> | |
11 #include <unistd.h> | |
12 #endif | |
13 | |
14 #include <stdio.h> | |
15 #include <iostream> | |
16 #include <string> | |
17 | |
18 #include "chrome/common/ipc_tests.h" | |
19 | |
20 #include "base/at_exit.h" | |
21 #include "base/base_switches.h" | |
22 #include "base/command_line.h" | |
23 #include "base/debug_on_start.h" | |
24 #if defined(OS_POSIX) | |
25 #include "base/at_exit.h" | |
26 #include "base/global_descriptors_posix.h" | |
27 #endif | |
28 #include "base/perftimer.h" | |
29 #include "base/perf_test_suite.h" | |
30 #include "base/test_suite.h" | |
31 #include "base/thread.h" | |
32 #include "chrome/common/chrome_switches.h" | |
33 #include "chrome/common/chrome_descriptors.h" | |
34 #include "chrome/common/ipc_channel.h" | |
35 #include "chrome/common/ipc_channel_proxy.h" | |
36 #include "chrome/common/ipc_message_utils.h" | |
37 #include "testing/multiprocess_func_list.h" | |
38 | |
39 // Define to enable IPC performance testing instead of the regular unit tests | |
40 // #define PERFORMANCE_TEST | |
41 | |
42 const char kTestClientChannel[] = "T1"; | |
43 const char kReflectorChannel[] = "T2"; | |
44 const char kFuzzerChannel[] = "F3"; | |
45 | |
46 const size_t kLongMessageStringNumBytes = 50000; | |
47 | |
48 #ifndef PERFORMANCE_TEST | |
49 | |
50 void IPCChannelTest::SetUp() { | |
51 MultiProcessTest::SetUp(); | |
52 | |
53 // Construct a fresh IO Message loop for the duration of each test. | |
54 message_loop_ = new MessageLoopForIO(); | |
55 } | |
56 | |
57 void IPCChannelTest::TearDown() { | |
58 delete message_loop_; | |
59 message_loop_ = NULL; | |
60 | |
61 MultiProcessTest::TearDown(); | |
62 } | |
63 | |
64 #if defined(OS_WIN) | |
65 base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type, | |
66 IPC::Channel *channel) { | |
67 // kDebugChildren support. | |
68 bool debug_on_start = | |
69 CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren); | |
70 | |
71 switch (child_type) { | |
72 case TEST_CLIENT: | |
73 return MultiProcessTest::SpawnChild(L"RunTestClient", debug_on_start); | |
74 break; | |
75 case TEST_REFLECTOR: | |
76 return MultiProcessTest::SpawnChild(L"RunReflector", debug_on_start); | |
77 break; | |
78 case FUZZER_SERVER: | |
79 return MultiProcessTest::SpawnChild(L"RunFuzzServer", debug_on_start); | |
80 break; | |
81 default: | |
82 return NULL; | |
83 break; | |
84 } | |
85 } | |
86 #elif defined(OS_POSIX) | |
87 base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type, | |
88 IPC::Channel *channel) { | |
89 // kDebugChildren support. | |
90 bool debug_on_start = | |
91 CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren); | |
92 | |
93 base::file_handle_mapping_vector fds_to_map; | |
94 const int ipcfd = channel->GetClientFileDescriptor(); | |
95 if (ipcfd > -1) { | |
96 fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3)); | |
97 } | |
98 | |
99 base::ProcessHandle ret = NULL; | |
100 switch (child_type) { | |
101 case TEST_CLIENT: | |
102 ret = MultiProcessTest::SpawnChild(L"RunTestClient", | |
103 fds_to_map, | |
104 debug_on_start); | |
105 break; | |
106 case TEST_DESCRIPTOR_CLIENT: | |
107 ret = MultiProcessTest::SpawnChild(L"RunTestDescriptorClient", | |
108 fds_to_map, | |
109 debug_on_start); | |
110 break; | |
111 case TEST_DESCRIPTOR_CLIENT_SANDBOXED: | |
112 ret = MultiProcessTest::SpawnChild(L"RunTestDescriptorClientSandboxed", | |
113 fds_to_map, | |
114 debug_on_start); | |
115 break; | |
116 case TEST_REFLECTOR: | |
117 ret = MultiProcessTest::SpawnChild(L"RunReflector", | |
118 fds_to_map, | |
119 debug_on_start); | |
120 break; | |
121 case FUZZER_SERVER: | |
122 ret = MultiProcessTest::SpawnChild(L"RunFuzzServer", | |
123 fds_to_map, | |
124 debug_on_start); | |
125 break; | |
126 default: | |
127 return NULL; | |
128 break; | |
129 } | |
130 return ret; | |
131 } | |
132 #endif // defined(OS_POSIX) | |
133 | |
134 TEST_F(IPCChannelTest, BasicMessageTest) { | |
135 int v1 = 10; | |
136 std::string v2("foobar"); | |
137 std::wstring v3(L"hello world"); | |
138 | |
139 IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); | |
140 EXPECT_TRUE(m.WriteInt(v1)); | |
141 EXPECT_TRUE(m.WriteString(v2)); | |
142 EXPECT_TRUE(m.WriteWString(v3)); | |
143 | |
144 void* iter = NULL; | |
145 | |
146 int vi; | |
147 std::string vs; | |
148 std::wstring vw; | |
149 | |
150 EXPECT_TRUE(m.ReadInt(&iter, &vi)); | |
151 EXPECT_EQ(v1, vi); | |
152 | |
153 EXPECT_TRUE(m.ReadString(&iter, &vs)); | |
154 EXPECT_EQ(v2, vs); | |
155 | |
156 EXPECT_TRUE(m.ReadWString(&iter, &vw)); | |
157 EXPECT_EQ(v3, vw); | |
158 | |
159 // should fail | |
160 EXPECT_FALSE(m.ReadInt(&iter, &vi)); | |
161 EXPECT_FALSE(m.ReadString(&iter, &vs)); | |
162 EXPECT_FALSE(m.ReadWString(&iter, &vw)); | |
163 } | |
164 | |
165 static void Send(IPC::Message::Sender* sender, const char* text) { | |
166 static int message_index = 0; | |
167 | |
168 IPC::Message* message = new IPC::Message(0, | |
169 2, | |
170 IPC::Message::PRIORITY_NORMAL); | |
171 message->WriteInt(message_index++); | |
172 message->WriteString(std::string(text)); | |
173 | |
174 // Make sure we can handle large messages. | |
175 char junk[kLongMessageStringNumBytes]; | |
176 memset(junk, 'a', sizeof(junk)-1); | |
177 junk[sizeof(junk)-1] = 0; | |
178 message->WriteString(std::string(junk)); | |
179 | |
180 // DEBUG: printf("[%u] sending message [%s]\n", GetCurrentProcessId(), text); | |
181 sender->Send(message); | |
182 } | |
183 | |
184 class MyChannelListener : public IPC::Channel::Listener { | |
185 public: | |
186 virtual void OnMessageReceived(const IPC::Message& message) { | |
187 IPC::MessageIterator iter(message); | |
188 | |
189 iter.NextInt(); | |
190 const std::string data = iter.NextString(); | |
191 const std::string big_string = iter.NextString(); | |
192 EXPECT_EQ(kLongMessageStringNumBytes - 1, big_string.length()); | |
193 | |
194 | |
195 if (--messages_left_ == 0) { | |
196 MessageLoop::current()->Quit(); | |
197 } else { | |
198 Send(sender_, "Foo"); | |
199 } | |
200 } | |
201 | |
202 virtual void OnChannelError() { | |
203 // There is a race when closing the channel so the last message may be lost. | |
204 EXPECT_LE(messages_left_, 1); | |
205 MessageLoop::current()->Quit(); | |
206 } | |
207 | |
208 void Init(IPC::Message::Sender* s) { | |
209 sender_ = s; | |
210 messages_left_ = 50; | |
211 } | |
212 | |
213 private: | |
214 IPC::Message::Sender* sender_; | |
215 int messages_left_; | |
216 }; | |
217 | |
218 TEST_F(IPCChannelTest, ChannelTest) { | |
219 MyChannelListener channel_listener; | |
220 // Setup IPC channel. | |
221 IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER, | |
222 &channel_listener); | |
223 chan.Connect(); | |
224 | |
225 channel_listener.Init(&chan); | |
226 | |
227 base::ProcessHandle process_handle = SpawnChild(TEST_CLIENT, &chan); | |
228 ASSERT_TRUE(process_handle); | |
229 | |
230 Send(&chan, "hello from parent"); | |
231 | |
232 // Run message loop. | |
233 MessageLoop::current()->Run(); | |
234 | |
235 // Close Channel so client gets its OnChannelError() callback fired. | |
236 chan.Close(); | |
237 | |
238 // Cleanup child process. | |
239 EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000)); | |
240 base::CloseProcessHandle(process_handle); | |
241 } | |
242 | |
243 TEST_F(IPCChannelTest, ChannelProxyTest) { | |
244 MyChannelListener channel_listener; | |
245 | |
246 // The thread needs to out-live the ChannelProxy. | |
247 base::Thread thread("ChannelProxyTestServer"); | |
248 base::Thread::Options options; | |
249 options.message_loop_type = MessageLoop::TYPE_IO; | |
250 thread.StartWithOptions(options); | |
251 { | |
252 // setup IPC channel proxy | |
253 IPC::ChannelProxy chan(kTestClientChannel, IPC::Channel::MODE_SERVER, | |
254 &channel_listener, NULL, thread.message_loop()); | |
255 | |
256 channel_listener.Init(&chan); | |
257 | |
258 #if defined(OS_WIN) | |
259 base::ProcessHandle process_handle = SpawnChild(TEST_CLIENT, NULL); | |
260 #elif defined(OS_POSIX) | |
261 bool debug_on_start = CommandLine::ForCurrentProcess()->HasSwitch( | |
262 switches::kDebugChildren); | |
263 base::file_handle_mapping_vector fds_to_map; | |
264 const int ipcfd = chan.GetClientFileDescriptor(); | |
265 if (ipcfd > -1) { | |
266 fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3)); | |
267 } | |
268 | |
269 base::ProcessHandle process_handle = MultiProcessTest::SpawnChild( | |
270 L"RunTestClient", | |
271 fds_to_map, | |
272 debug_on_start); | |
273 #endif // defined(OS_POXIX) | |
274 | |
275 ASSERT_TRUE(process_handle); | |
276 | |
277 Send(&chan, "hello from parent"); | |
278 | |
279 // run message loop | |
280 MessageLoop::current()->Run(); | |
281 | |
282 // cleanup child process | |
283 EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000)); | |
284 base::CloseProcessHandle(process_handle); | |
285 } | |
286 thread.Stop(); | |
287 } | |
288 | |
289 MULTIPROCESS_TEST_MAIN(RunTestClient) { | |
290 MessageLoopForIO main_message_loop; | |
291 MyChannelListener channel_listener; | |
292 | |
293 // setup IPC channel | |
294 IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT, | |
295 &channel_listener); | |
296 chan.Connect(); | |
297 channel_listener.Init(&chan); | |
298 Send(&chan, "hello from child"); | |
299 // run message loop | |
300 MessageLoop::current()->Run(); | |
301 // return true; | |
302 return NULL; | |
303 } | |
304 | |
305 #endif // !PERFORMANCE_TEST | |
306 | |
307 #ifdef PERFORMANCE_TEST | |
308 | |
309 //----------------------------------------------------------------------------- | |
310 // Manually performance test | |
311 // | |
312 // This test times the roundtrip IPC message cycle. It is enabled with a | |
313 // special preprocessor define to enable it instead of the standard IPC | |
314 // unit tests. This works around some funny termination conditions in the | |
315 // regular unit tests. | |
316 // | |
317 // This test is not automated. To test, you will want to vary the message | |
318 // count and message size in TEST to get the numbers you want. | |
319 // | |
320 // FIXME(brettw): Automate this test and have it run by default. | |
321 | |
322 // This channel listener just replies to all messages with the exact same | |
323 // message. It assumes each message has one string parameter. When the string | |
324 // "quit" is sent, it will exit. | |
325 class ChannelReflectorListener : public IPC::Channel::Listener { | |
326 public: | |
327 explicit ChannelReflectorListener(IPC::Channel *channel) : | |
328 channel_(channel), | |
329 count_messages_(0), | |
330 latency_messages_(0) { | |
331 std::cout << "Reflector up" << std::endl; | |
332 } | |
333 | |
334 ~ChannelReflectorListener() { | |
335 std::cout << "Client Messages: " << count_messages_ << std::endl; | |
336 std::cout << "Client Latency: " << latency_messages_ << std::endl; | |
337 } | |
338 | |
339 virtual void OnMessageReceived(const IPC::Message& message) { | |
340 count_messages_++; | |
341 IPC::MessageIterator iter(message); | |
342 int time = iter.NextInt(); | |
343 int msgid = iter.NextInt(); | |
344 std::string payload = iter.NextString(); | |
345 latency_messages_ += GetTickCount() - time; | |
346 | |
347 // cout << "reflector msg received: " << msgid << endl; | |
348 if (payload == "quit") | |
349 MessageLoop::current()->Quit(); | |
350 | |
351 IPC::Message* msg = new IPC::Message(0, | |
352 2, | |
353 IPC::Message::PRIORITY_NORMAL); | |
354 msg->WriteInt(GetTickCount()); | |
355 msg->WriteInt(msgid); | |
356 msg->WriteString(payload); | |
357 channel_->Send(msg); | |
358 } | |
359 private: | |
360 IPC::Channel *channel_; | |
361 int count_messages_; | |
362 int latency_messages_; | |
363 }; | |
364 | |
365 class ChannelPerfListener : public IPC::Channel::Listener { | |
366 public: | |
367 ChannelPerfListener(IPC::Channel* channel, int msg_count, int msg_size) : | |
368 count_down_(msg_count), | |
369 channel_(channel), | |
370 count_messages_(0), | |
371 latency_messages_(0) { | |
372 payload_.resize(msg_size); | |
373 for (int i = 0; i < static_cast<int>(payload_.size()); i++) | |
374 payload_[i] = 'a'; | |
375 std::cout << "perflistener up" << std::endl; | |
376 } | |
377 | |
378 ~ChannelPerfListener() { | |
379 std::cout << "Server Messages: " << count_messages_ << std::endl; | |
380 std::cout << "Server Latency: " << latency_messages_ << std::endl; | |
381 } | |
382 | |
383 virtual void OnMessageReceived(const IPC::Message& message) { | |
384 count_messages_++; | |
385 // decode the string so this gets counted in the total time | |
386 IPC::MessageIterator iter(message); | |
387 int time = iter.NextInt(); | |
388 int msgid = iter.NextInt(); | |
389 std::string cur = iter.NextString(); | |
390 latency_messages_ += GetTickCount() - time; | |
391 | |
392 // cout << "perflistener got message" << endl; | |
393 | |
394 count_down_--; | |
395 if (count_down_ == 0) { | |
396 IPC::Message* msg = new IPC::Message(0, | |
397 2, | |
398 IPC::Message::PRIORITY_NORMAL); | |
399 msg->WriteInt(GetTickCount()); | |
400 msg->WriteInt(count_down_); | |
401 msg->WriteString("quit"); | |
402 channel_->Send(msg); | |
403 SetTimer(NULL, 1, 250, (TIMERPROC) PostQuitMessage); | |
404 return; | |
405 } | |
406 | |
407 IPC::Message* msg = new IPC::Message(0, | |
408 2, | |
409 IPC::Message::PRIORITY_NORMAL); | |
410 msg->WriteInt(GetTickCount()); | |
411 msg->WriteInt(count_down_); | |
412 msg->WriteString(payload_); | |
413 channel_->Send(msg); | |
414 } | |
415 | |
416 private: | |
417 int count_down_; | |
418 std::string payload_; | |
419 IPC::Channel *channel_; | |
420 int count_messages_; | |
421 int latency_messages_; | |
422 }; | |
423 | |
424 TEST_F(IPCChannelTest, Performance) { | |
425 // setup IPC channel | |
426 IPC::Channel chan(kReflectorChannel, IPC::Channel::MODE_SERVER, NULL); | |
427 ChannelPerfListener perf_listener(&chan, 10000, 100000); | |
428 chan.set_listener(&perf_listener); | |
429 chan.Connect(); | |
430 | |
431 HANDLE process = SpawnChild(TEST_REFLECTOR, &chan); | |
432 ASSERT_TRUE(process); | |
433 | |
434 PlatformThread::Sleep(1000); | |
435 | |
436 PerfTimeLogger logger("IPC_Perf"); | |
437 | |
438 // this initial message will kick-start the ping-pong of messages | |
439 IPC::Message* message = new IPC::Message(0, | |
440 2, | |
441 IPC::Message::PRIORITY_NORMAL); | |
442 message->WriteInt(GetTickCount()); | |
443 message->WriteInt(-1); | |
444 message->WriteString("Hello"); | |
445 chan.Send(message); | |
446 | |
447 // run message loop | |
448 MessageLoop::current()->Run(); | |
449 | |
450 // cleanup child process | |
451 WaitForSingleObject(process, 5000); | |
452 CloseHandle(process); | |
453 } | |
454 | |
455 // This message loop bounces all messages back to the sender | |
456 MULTIPROCESS_TEST_MAIN(RunReflector) { | |
457 MessageLoopForIO main_message_loop; | |
458 IPC::Channel chan(kReflectorChannel, IPC::Channel::MODE_CLIENT, NULL); | |
459 ChannelReflectorListener channel_reflector_listener(&chan); | |
460 chan.set_listener(&channel_reflector_listener); | |
461 chan.Connect(); | |
462 | |
463 MessageLoop::current()->Run(); | |
464 return true; | |
465 } | |
466 | |
467 #endif // PERFORMANCE_TEST | |
468 | |
469 int main(int argc, char** argv) { | |
470 #ifdef PERFORMANCE_TEST | |
471 int retval = PerfTestSuite(argc, argv).Run(); | |
472 #else | |
473 int retval = TestSuite(argc, argv).Run(); | |
474 #endif | |
475 return retval; | |
476 } | |
OLD | NEW |