blob: 316c3970aaa725087c74f6e50c37c1ece19beffb [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 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.
initial.commit09911bf2008-07-26 23:55:294
5#include <windows.h>
6#include <stdio.h>
7#include <iostream>
8#include <string>
9
10#include "chrome/common/ipc_tests.h"
11
[email protected]ae6454712008-08-01 03:06:2512#include "base/at_exit.h"
initial.commit09911bf2008-07-26 23:55:2913#include "base/base_switches.h"
14#include "base/command_line.h"
15#include "base/debug_on_start.h"
16#include "base/perftimer.h"
17#include "base/process_util.h"
18#include "base/thread.h"
19#include "chrome/common/chrome_switches.h"
20#include "chrome/common/ipc_channel.h"
21#include "chrome/common/ipc_channel_proxy.h"
22#include "chrome/common/ipc_message_utils.h"
23#include "testing/gtest/include/gtest/gtest.h"
24
25// Define to enable IPC performance testing instead of the regular unit tests
26// #define PERFORMANCE_TEST
27
28const wchar_t kTestClientChannel[] = L"T1";
29const wchar_t kReflectorChannel[] = L"T2";
30const wchar_t kFuzzerChannel[] = L"F3";
31
32const wchar_t kChild[] = L"child";
33const wchar_t kReflector[] = L"reflector";
34const wchar_t kFuzzer[] = L"fuzzer";
35
36#ifndef PERFORMANCE_TEST
37
38TEST(IPCChannelTest, BasicMessageTest) {
39 int v1 = 10;
40 std::string v2("foobar");
41 std::wstring v3(L"hello world");
42
43 IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
44 EXPECT_TRUE(m.WriteInt(v1));
45 EXPECT_TRUE(m.WriteString(v2));
46 EXPECT_TRUE(m.WriteWString(v3));
47
48 void* iter = NULL;
49
50 int vi;
51 std::string vs;
52 std::wstring vw;
53
54 EXPECT_TRUE(m.ReadInt(&iter, &vi));
55 EXPECT_EQ(v1, vi);
56
57 EXPECT_TRUE(m.ReadString(&iter, &vs));
58 EXPECT_EQ(v2, vs);
59
60 EXPECT_TRUE(m.ReadWString(&iter, &vw));
61 EXPECT_EQ(v3, vw);
62
63 // should fail
64 EXPECT_FALSE(m.ReadInt(&iter, &vi));
65 EXPECT_FALSE(m.ReadString(&iter, &vs));
66 EXPECT_FALSE(m.ReadWString(&iter, &vw));
67}
68
69static void Send(IPC::Message::Sender* sender, const char* text) {
70 static int message_index = 0;
71
72 IPC::Message* message = new IPC::Message(0,
73 2,
74 IPC::Message::PRIORITY_NORMAL);
75 message->WriteInt(message_index++);
76 message->WriteString(std::string(text));
77
78 // make sure we can handle large messages
79 char junk[50000];
80 junk[sizeof(junk)-1] = 0;
81 message->WriteString(std::string(junk));
82
83 // DEBUG: printf("[%u] sending message [%s]\n", GetCurrentProcessId(), text);
84 sender->Send(message);
85}
86
87class MyChannelListener : public IPC::Channel::Listener {
88 public:
89 virtual void OnMessageReceived(const IPC::Message& message) {
90 IPC::MessageIterator iter(message);
91
92 int index = iter.NextInt();
93 const std::string data = iter.NextString();
94
95 if (--messages_left_ == 0) {
96 MessageLoop::current()->Quit();
97 } else {
98 Send(sender_, "Foo");
99 }
100 }
101
102 void Init(IPC::Message::Sender* s) {
103 sender_ = s;
104 messages_left_ = 50;
105 }
106
107 private:
108 IPC::Message::Sender* sender_;
109 int messages_left_;
110};
111static MyChannelListener channel_listener;
112
113TEST(IPCChannelTest, ChannelTest) {
114 // setup IPC channel
115 IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
116 &channel_listener);
117 chan.Connect();
118
119 channel_listener.Init(&chan);
120
121 HANDLE process_handle = SpawnChild(TEST_CLIENT);
122 ASSERT_TRUE(process_handle);
123
124 Send(&chan, "hello from parent");
125
126 // run message loop
127 MessageLoop::current()->Run();
128
129 // cleanup child process
130 WaitForSingleObject(process_handle, 5000);
131 CloseHandle(process_handle);
132}
133
134TEST(IPCChannelTest, ChannelProxyTest) {
135 // The thread needs to out-live the ChannelProxy.
136 Thread thread("ChannelProxyTestServer");
137 thread.Start();
138 {
139 // setup IPC channel proxy
140 IPC::ChannelProxy chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
141 &channel_listener, NULL, thread.message_loop());
142
143 channel_listener.Init(&chan);
144
145 HANDLE process_handle = SpawnChild(TEST_CLIENT);
146 ASSERT_TRUE(process_handle);
147
148 Send(&chan, "hello from parent");
149
150 // run message loop
151 MessageLoop::current()->Run();
152
153 // cleanup child process
154 WaitForSingleObject(process_handle, 5000);
155 CloseHandle(process_handle);
156 }
157 thread.Stop();
158}
159
160static bool RunTestClient() {
161 // setup IPC channel
162 IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT,
163 &channel_listener);
164 chan.Connect();
165 channel_listener.Init(&chan);
166 Send(&chan, "hello from child");
167 // run message loop
168 MessageLoop::current()->Run();
169 return true;
170}
171
172#endif // !PERFORMANCE_TEST
173
174#ifdef PERFORMANCE_TEST
175
176//-----------------------------------------------------------------------------
177// Manually performance test
178//
179// This test times the roundtrip IPC message cycle. It is enabled with a
180// special preprocessor define to enable it instead of the standard IPC
181// unit tests. This works around some funny termination conditions in the
182// regular unit tests.
183//
184// This test is not automated. To test, you will want to vary the message
185// count and message size in TEST to get the numbers you want.
186//
187// FIXME(brettw): Automate this test and have it run by default.
188
189// This channel listener just replies to all messages with the exact same
190// message. It assumes each message has one string parameter. When the string
191// "quit" is sent, it will exit.
192class ChannelReflectorListener : public IPC::Channel::Listener {
193 public:
194 ChannelReflectorListener(IPC::Channel *channel) :
195 channel_(channel),
196 count_messages_(0),
197 latency_messages_(0) {
198 std::cout << "Reflector up" << std::endl;
199 }
200
201 ~ChannelReflectorListener() {
202 std::cout << "Client Messages: " << count_messages_ << std::endl;
203 std::cout << "Client Latency: " << latency_messages_ << std::endl;
204 }
205
206 virtual void OnMessageReceived(const IPC::Message& message) {
207 count_messages_++;
208 IPC::MessageIterator iter(message);
209 int time = iter.NextInt();
210 int msgid = iter.NextInt();
211 std::string payload = iter.NextString();
212 latency_messages_ += GetTickCount() - time;
213
214 // cout << "reflector msg received: " << msgid << endl;
215 if (payload == "quit")
216 MessageLoop::current()->Quit();
217
218 IPC::Message* msg = new IPC::Message(0,
219 2,
220 IPC::Message::PRIORITY_NORMAL);
221 msg->WriteInt(GetTickCount());
222 msg->WriteInt(msgid);
223 msg->WriteString(payload);
224 channel_->Send(msg);
225 }
226 private:
227 IPC::Channel *channel_;
228 int count_messages_;
229 int latency_messages_;
230};
231
232class ChannelPerfListener : public IPC::Channel::Listener {
233 public:
234 ChannelPerfListener(IPC::Channel* channel, int msg_count, int msg_size) :
235 count_down_(msg_count),
236 channel_(channel),
237 count_messages_(0),
238 latency_messages_(0) {
239 payload_.resize(msg_size);
240 for (int i = 0; i < static_cast<int>(payload_.size()); i++)
241 payload_[i] = 'a';
242 std::cout << "perflistener up" << std::endl;
243 }
244
245 ~ChannelPerfListener() {
246 std::cout << "Server Messages: " << count_messages_ << std::endl;
247 std::cout << "Server Latency: " << latency_messages_ << std::endl;
248 }
249
250 virtual void OnMessageReceived(const IPC::Message& message) {
251 count_messages_++;
252 // decode the string so this gets counted in the total time
253 IPC::MessageIterator iter(message);
254 int time = iter.NextInt();
255 int msgid = iter.NextInt();
256 std::string cur = iter.NextString();
257 latency_messages_ += GetTickCount() - time;
258
259 // cout << "perflistener got message" << endl;
260
261 count_down_--;
262 if (count_down_ == 0) {
263 IPC::Message* msg = new IPC::Message(0,
264 2,
265 IPC::Message::PRIORITY_NORMAL);
266 msg->WriteInt(GetTickCount());
267 msg->WriteInt(count_down_);
268 msg->WriteString("quit");
269 channel_->Send(msg);
270 SetTimer(NULL, 1, 250, (TIMERPROC) PostQuitMessage);
271 return;
272 }
273
274 IPC::Message* msg = new IPC::Message(0,
275 2,
276 IPC::Message::PRIORITY_NORMAL);
277 msg->WriteInt(GetTickCount());
278 msg->WriteInt(count_down_);
279 msg->WriteString(payload_);
280 channel_->Send(msg);
281 }
282
283 private:
284 int count_down_;
285 std::string payload_;
286 IPC::Channel *channel_;
287 int count_messages_;
288 int latency_messages_;
289};
290
291TEST(IPCChannelTest, Performance) {
292 // setup IPC channel
293 IPC::Channel chan(kReflectorChannel, IPC::Channel::MODE_SERVER, NULL);
294 ChannelPerfListener perf_listener(&chan, 10000, 100000);
295 chan.set_listener(&perf_listener);
296 chan.Connect();
297
298 HANDLE process = SpawnChild(TEST_REFLECTOR);
299 ASSERT_TRUE(process);
300
301 Sleep(1000);
302
303 PerfTimeLogger logger("IPC_Perf");
304
305 // this initial message will kick-start the ping-pong of messages
306 IPC::Message* message = new IPC::Message(0,
307 2,
308 IPC::Message::PRIORITY_NORMAL);
309 message->WriteInt(GetTickCount());
310 message->WriteInt(-1);
311 message->WriteString("Hello");
312 chan.Send(message);
313
314 // run message loop
315 MessageLoop::current()->Run();
316
317 // cleanup child process
318 WaitForSingleObject(process, 5000);
319 CloseHandle(process);
320}
321
322// This message loop bounces all messages back to the sender
323static bool RunReflector() {
324 IPC::Channel chan(kReflectorChannel, IPC::Channel::MODE_CLIENT, NULL);
325 ChannelReflectorListener channel_reflector_listener(&chan);
326 chan.set_listener(&channel_reflector_listener);
327 chan.Connect();
328
329 MessageLoop::current()->Run();
330 return true;
331}
332
333#endif // PERFORMANCE_TEST
334
335// All fatal log messages (e.g. DCHECK failures) imply unit test failures
336static void IPCTestAssertHandler(const std::string& str) {
337 FAIL() << str;
338}
339
340// Disable crash dialogs so that it doesn't gum up the buildbot
341static void SuppressErrorDialogs() {
342 UINT new_flags = SEM_FAILCRITICALERRORS |
343 SEM_NOGPFAULTERRORBOX |
344 SEM_NOOPENFILEERRORBOX;
345
346 // Preserve existing error mode, as discussed at http://t/dmea
347 UINT existing_flags = SetErrorMode(new_flags);
348 SetErrorMode(existing_flags | new_flags);
349}
350
351HANDLE SpawnChild(ChildType child_type) {
352 // spawn child process
353 std::wstring cl(GetCommandLineW());
354 switch(child_type) {
355 case TEST_CLIENT:
356 CommandLine::AppendSwitch(&cl, kChild);
357 break;
358 case TEST_REFLECTOR:
359 CommandLine::AppendSwitch(&cl, kReflector);
360 break;
361 case FUZZER_SERVER:
362 CommandLine::AppendSwitch(&cl, kFuzzer);
363 break;
364 default:
365 return NULL;
366 }
367 // kDebugChildren support.
368 if (CommandLine().HasSwitch(switches::kDebugChildren)) {
369 CommandLine::AppendSwitch(&cl, switches::kDebugOnStart);
370 }
371 HANDLE process = NULL;
372 if (!process_util::LaunchApp(cl, false, true, &process))
373 return NULL;
374
375 return process;
376}
377
378int main(int argc, char** argv) {
[email protected]ae6454712008-08-01 03:06:25379 // Some tests may use base::Singleton<>, thus we need to instanciate
380 // the AtExitManager or else we will leak objects.
381 base::AtExitManager at_exit_manager;
382
initial.commit09911bf2008-07-26 23:55:29383 MessageLoop main_message_loop;
384
385 // suppress standard crash dialogs and such unless a debugger is present.
386 if (!IsDebuggerPresent()) {
387 SuppressErrorDialogs();
388 logging::SetLogAssertHandler(IPCTestAssertHandler);
389 }
390
391#ifndef PERFORMANCE_TEST
392 if (CommandLine().HasSwitch(kChild))
393 return RunTestClient();
394 if (CommandLine().HasSwitch(kFuzzer))
395 return RunFuzzServer();
396#else
397 if (CommandLine().HasSwitch(kReflector))
398 return RunReflector();
399
400 if (!InitPerfLog("ipc_perf_child.log"))
401 return 1;
402#endif
403
404 testing::InitGoogleTest(&argc, argv);
405 return RUN_ALL_TESTS();
406}
license.botbf09a502008-08-24 00:55:55407