summaryrefslogtreecommitdiffstats
path: root/ipc/ipc_tests.cc
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-22 23:57:21 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-22 23:57:21 +0000
commit946d1b2c806795351598aeb9faaed797284a8ee3 (patch)
treed8d2695f73a56ec33ab068f9070fe93cb7c0e4a3 /ipc/ipc_tests.cc
parent00fceac62015db950f3dde84f5aeeacb82f1b2c6 (diff)
downloadchromium_src-946d1b2c806795351598aeb9faaed797284a8ee3.zip
chromium_src-946d1b2c806795351598aeb9faaed797284a8ee3.tar.gz
chromium_src-946d1b2c806795351598aeb9faaed797284a8ee3.tar.bz2
Split the IPC code into ipc/
This splits the ipc code from the common project. The 'common' project pulls in all of webkit, the v8 bindings, skia, googleurl, and a number of other projects which makes it very difficult to deal with especially for external projects wanting just to use some of Chromium's infrastructure. This puts the ipc code into its top-level ipc/ directory with a dependency only on base. The common project depends on the new ipc/ipc.gyp:ipc target so that all projects currently pulling common in to get the IPC code still have it available. This mostly follows agl's pre-gyp attempt to do this which was r13062. Known issues: - Currently a number of projects depend on chrome/chrome.gyp:common in order to use the IPC infrastructure. Rather than fixing all of these dependencies I have made common depend on ipc/ipc.gyp:ipc and added "ipc" to the include_rules section of DEPS so that checkdeps.py doesn't complain. Over time projects that need IPC should depend on the IPC project themselves and dependencies on common removed, although I don't think many projects that need IPC will be able to get away without common currently. - ipc/ipc_message_macros.h still has #include "chrome/common/..." inside of a ipc/ should not refer to files in chrome/... now. I'm not sure how to resolve this since it's really an IDE bug - the named pipe name (windows+linux) and the logging event name (all) + env variable (posix) refer explicitly to 'Chrome' which somewhat hurts the illusion of ipc/ being an independent library. I think this should be examined in a subsequent, much smaller patch. - I've eliminated the IPC.SendMsgCount counter since it was implemented in a way to create a dependency from ipc/ to chrome/common/chrome_counters. This is the same approach that r13062 took. http://codereview.chromium.org/155905 (Patch from James Robinson) git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21342 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ipc/ipc_tests.cc')
-rw-r--r--ipc/ipc_tests.cc476
1 files changed, 476 insertions, 0 deletions
diff --git a/ipc/ipc_tests.cc b/ipc/ipc_tests.cc
new file mode 100644
index 0000000..172284e
--- /dev/null
+++ b/ipc/ipc_tests.cc
@@ -0,0 +1,476 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX)
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <iostream>
+#include <string>
+
+#include "ipc/ipc_tests.h"
+
+#include "base/at_exit.h"
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/debug_on_start.h"
+#if defined(OS_POSIX)
+#include "base/at_exit.h"
+#include "base/global_descriptors_posix.h"
+#endif
+#include "base/perftimer.h"
+#include "base/perf_test_suite.h"
+#include "base/test_suite.h"
+#include "base/thread.h"
+#include "ipc/ipc_descriptors.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_message_utils.h"
+#include "ipc/ipc_switches.h"
+#include "testing/multiprocess_func_list.h"
+
+// Define to enable IPC performance testing instead of the regular unit tests
+// #define PERFORMANCE_TEST
+
+const char kTestClientChannel[] = "T1";
+const char kReflectorChannel[] = "T2";
+const char kFuzzerChannel[] = "F3";
+
+const size_t kLongMessageStringNumBytes = 50000;
+
+#ifndef PERFORMANCE_TEST
+
+void IPCChannelTest::SetUp() {
+ MultiProcessTest::SetUp();
+
+ // Construct a fresh IO Message loop for the duration of each test.
+ message_loop_ = new MessageLoopForIO();
+}
+
+void IPCChannelTest::TearDown() {
+ delete message_loop_;
+ message_loop_ = NULL;
+
+ MultiProcessTest::TearDown();
+}
+
+#if defined(OS_WIN)
+base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type,
+ IPC::Channel *channel) {
+ // kDebugChildren support.
+ bool debug_on_start =
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren);
+
+ switch (child_type) {
+ case TEST_CLIENT:
+ return MultiProcessTest::SpawnChild(L"RunTestClient", debug_on_start);
+ break;
+ case TEST_REFLECTOR:
+ return MultiProcessTest::SpawnChild(L"RunReflector", debug_on_start);
+ break;
+ case FUZZER_SERVER:
+ return MultiProcessTest::SpawnChild(L"RunFuzzServer", debug_on_start);
+ break;
+ default:
+ return NULL;
+ break;
+ }
+}
+#elif defined(OS_POSIX)
+base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type,
+ IPC::Channel *channel) {
+ // kDebugChildren support.
+ bool debug_on_start =
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren);
+
+ base::file_handle_mapping_vector fds_to_map;
+ const int ipcfd = channel->GetClientFileDescriptor();
+ if (ipcfd > -1) {
+ fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3));
+ }
+
+ base::ProcessHandle ret = NULL;
+ switch (child_type) {
+ case TEST_CLIENT:
+ ret = MultiProcessTest::SpawnChild(L"RunTestClient",
+ fds_to_map,
+ debug_on_start);
+ break;
+ case TEST_DESCRIPTOR_CLIENT:
+ ret = MultiProcessTest::SpawnChild(L"RunTestDescriptorClient",
+ fds_to_map,
+ debug_on_start);
+ break;
+ case TEST_DESCRIPTOR_CLIENT_SANDBOXED:
+ ret = MultiProcessTest::SpawnChild(L"RunTestDescriptorClientSandboxed",
+ fds_to_map,
+ debug_on_start);
+ break;
+ case TEST_REFLECTOR:
+ ret = MultiProcessTest::SpawnChild(L"RunReflector",
+ fds_to_map,
+ debug_on_start);
+ break;
+ case FUZZER_SERVER:
+ ret = MultiProcessTest::SpawnChild(L"RunFuzzServer",
+ fds_to_map,
+ debug_on_start);
+ break;
+ default:
+ return NULL;
+ break;
+ }
+ return ret;
+}
+#endif // defined(OS_POSIX)
+
+TEST_F(IPCChannelTest, BasicMessageTest) {
+ int v1 = 10;
+ std::string v2("foobar");
+ std::wstring v3(L"hello world");
+
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(v1));
+ EXPECT_TRUE(m.WriteString(v2));
+ EXPECT_TRUE(m.WriteWString(v3));
+
+ void* iter = NULL;
+
+ int vi;
+ std::string vs;
+ std::wstring vw;
+
+ EXPECT_TRUE(m.ReadInt(&iter, &vi));
+ EXPECT_EQ(v1, vi);
+
+ EXPECT_TRUE(m.ReadString(&iter, &vs));
+ EXPECT_EQ(v2, vs);
+
+ EXPECT_TRUE(m.ReadWString(&iter, &vw));
+ EXPECT_EQ(v3, vw);
+
+ // should fail
+ EXPECT_FALSE(m.ReadInt(&iter, &vi));
+ EXPECT_FALSE(m.ReadString(&iter, &vs));
+ EXPECT_FALSE(m.ReadWString(&iter, &vw));
+}
+
+static void Send(IPC::Message::Sender* sender, const char* text) {
+ static int message_index = 0;
+
+ IPC::Message* message = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ message->WriteInt(message_index++);
+ message->WriteString(std::string(text));
+
+ // Make sure we can handle large messages.
+ char junk[kLongMessageStringNumBytes];
+ memset(junk, 'a', sizeof(junk)-1);
+ junk[sizeof(junk)-1] = 0;
+ message->WriteString(std::string(junk));
+
+ // DEBUG: printf("[%u] sending message [%s]\n", GetCurrentProcessId(), text);
+ sender->Send(message);
+}
+
+class MyChannelListener : public IPC::Channel::Listener {
+ public:
+ virtual void OnMessageReceived(const IPC::Message& message) {
+ IPC::MessageIterator iter(message);
+
+ iter.NextInt();
+ const std::string data = iter.NextString();
+ const std::string big_string = iter.NextString();
+ EXPECT_EQ(kLongMessageStringNumBytes - 1, big_string.length());
+
+
+ if (--messages_left_ == 0) {
+ MessageLoop::current()->Quit();
+ } else {
+ Send(sender_, "Foo");
+ }
+ }
+
+ virtual void OnChannelError() {
+ // There is a race when closing the channel so the last message may be lost.
+ EXPECT_LE(messages_left_, 1);
+ MessageLoop::current()->Quit();
+ }
+
+ void Init(IPC::Message::Sender* s) {
+ sender_ = s;
+ messages_left_ = 50;
+ }
+
+ private:
+ IPC::Message::Sender* sender_;
+ int messages_left_;
+};
+
+TEST_F(IPCChannelTest, ChannelTest) {
+ MyChannelListener channel_listener;
+ // Setup IPC channel.
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
+ &channel_listener);
+ chan.Connect();
+
+ channel_listener.Init(&chan);
+
+ base::ProcessHandle process_handle = SpawnChild(TEST_CLIENT, &chan);
+ ASSERT_TRUE(process_handle);
+
+ Send(&chan, "hello from parent");
+
+ // Run message loop.
+ MessageLoop::current()->Run();
+
+ // Close Channel so client gets its OnChannelError() callback fired.
+ chan.Close();
+
+ // Cleanup child process.
+ EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000));
+ base::CloseProcessHandle(process_handle);
+}
+
+TEST_F(IPCChannelTest, ChannelProxyTest) {
+ MyChannelListener channel_listener;
+
+ // The thread needs to out-live the ChannelProxy.
+ base::Thread thread("ChannelProxyTestServer");
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ thread.StartWithOptions(options);
+ {
+ // setup IPC channel proxy
+ IPC::ChannelProxy chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
+ &channel_listener, NULL, thread.message_loop());
+
+ channel_listener.Init(&chan);
+
+#if defined(OS_WIN)
+ base::ProcessHandle process_handle = SpawnChild(TEST_CLIENT, NULL);
+#elif defined(OS_POSIX)
+ bool debug_on_start = CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDebugChildren);
+ base::file_handle_mapping_vector fds_to_map;
+ const int ipcfd = chan.GetClientFileDescriptor();
+ if (ipcfd > -1) {
+ fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3));
+ }
+
+ base::ProcessHandle process_handle = MultiProcessTest::SpawnChild(
+ L"RunTestClient",
+ fds_to_map,
+ debug_on_start);
+#endif // defined(OS_POXIX)
+
+ ASSERT_TRUE(process_handle);
+
+ Send(&chan, "hello from parent");
+
+ // run message loop
+ MessageLoop::current()->Run();
+
+ // cleanup child process
+ EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000));
+ base::CloseProcessHandle(process_handle);
+ }
+ thread.Stop();
+}
+
+MULTIPROCESS_TEST_MAIN(RunTestClient) {
+ MessageLoopForIO main_message_loop;
+ MyChannelListener channel_listener;
+
+ // setup IPC channel
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT,
+ &channel_listener);
+ chan.Connect();
+ channel_listener.Init(&chan);
+ Send(&chan, "hello from child");
+ // run message loop
+ MessageLoop::current()->Run();
+ // return true;
+ return NULL;
+}
+
+#endif // !PERFORMANCE_TEST
+
+#ifdef PERFORMANCE_TEST
+
+//-----------------------------------------------------------------------------
+// Manually performance test
+//
+// This test times the roundtrip IPC message cycle. It is enabled with a
+// special preprocessor define to enable it instead of the standard IPC
+// unit tests. This works around some funny termination conditions in the
+// regular unit tests.
+//
+// This test is not automated. To test, you will want to vary the message
+// count and message size in TEST to get the numbers you want.
+//
+// FIXME(brettw): Automate this test and have it run by default.
+
+// This channel listener just replies to all messages with the exact same
+// message. It assumes each message has one string parameter. When the string
+// "quit" is sent, it will exit.
+class ChannelReflectorListener : public IPC::Channel::Listener {
+ public:
+ explicit ChannelReflectorListener(IPC::Channel *channel) :
+ channel_(channel),
+ count_messages_(0),
+ latency_messages_(0) {
+ std::cout << "Reflector up" << std::endl;
+ }
+
+ ~ChannelReflectorListener() {
+ std::cout << "Client Messages: " << count_messages_ << std::endl;
+ std::cout << "Client Latency: " << latency_messages_ << std::endl;
+ }
+
+ virtual void OnMessageReceived(const IPC::Message& message) {
+ count_messages_++;
+ IPC::MessageIterator iter(message);
+ int time = iter.NextInt();
+ int msgid = iter.NextInt();
+ std::string payload = iter.NextString();
+ latency_messages_ += GetTickCount() - time;
+
+ // cout << "reflector msg received: " << msgid << endl;
+ if (payload == "quit")
+ MessageLoop::current()->Quit();
+
+ IPC::Message* msg = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(GetTickCount());
+ msg->WriteInt(msgid);
+ msg->WriteString(payload);
+ channel_->Send(msg);
+ }
+ private:
+ IPC::Channel *channel_;
+ int count_messages_;
+ int latency_messages_;
+};
+
+class ChannelPerfListener : public IPC::Channel::Listener {
+ public:
+ ChannelPerfListener(IPC::Channel* channel, int msg_count, int msg_size) :
+ count_down_(msg_count),
+ channel_(channel),
+ count_messages_(0),
+ latency_messages_(0) {
+ payload_.resize(msg_size);
+ for (int i = 0; i < static_cast<int>(payload_.size()); i++)
+ payload_[i] = 'a';
+ std::cout << "perflistener up" << std::endl;
+ }
+
+ ~ChannelPerfListener() {
+ std::cout << "Server Messages: " << count_messages_ << std::endl;
+ std::cout << "Server Latency: " << latency_messages_ << std::endl;
+ }
+
+ virtual void OnMessageReceived(const IPC::Message& message) {
+ count_messages_++;
+ // decode the string so this gets counted in the total time
+ IPC::MessageIterator iter(message);
+ int time = iter.NextInt();
+ int msgid = iter.NextInt();
+ std::string cur = iter.NextString();
+ latency_messages_ += GetTickCount() - time;
+
+ // cout << "perflistener got message" << endl;
+
+ count_down_--;
+ if (count_down_ == 0) {
+ IPC::Message* msg = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(GetTickCount());
+ msg->WriteInt(count_down_);
+ msg->WriteString("quit");
+ channel_->Send(msg);
+ SetTimer(NULL, 1, 250, (TIMERPROC) PostQuitMessage);
+ return;
+ }
+
+ IPC::Message* msg = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(GetTickCount());
+ msg->WriteInt(count_down_);
+ msg->WriteString(payload_);
+ channel_->Send(msg);
+ }
+
+ private:
+ int count_down_;
+ std::string payload_;
+ IPC::Channel *channel_;
+ int count_messages_;
+ int latency_messages_;
+};
+
+TEST_F(IPCChannelTest, Performance) {
+ // setup IPC channel
+ IPC::Channel chan(kReflectorChannel, IPC::Channel::MODE_SERVER, NULL);
+ ChannelPerfListener perf_listener(&chan, 10000, 100000);
+ chan.set_listener(&perf_listener);
+ chan.Connect();
+
+ HANDLE process = SpawnChild(TEST_REFLECTOR, &chan);
+ ASSERT_TRUE(process);
+
+ PlatformThread::Sleep(1000);
+
+ PerfTimeLogger logger("IPC_Perf");
+
+ // this initial message will kick-start the ping-pong of messages
+ IPC::Message* message = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ message->WriteInt(GetTickCount());
+ message->WriteInt(-1);
+ message->WriteString("Hello");
+ chan.Send(message);
+
+ // run message loop
+ MessageLoop::current()->Run();
+
+ // cleanup child process
+ WaitForSingleObject(process, 5000);
+ CloseHandle(process);
+}
+
+// This message loop bounces all messages back to the sender
+MULTIPROCESS_TEST_MAIN(RunReflector) {
+ MessageLoopForIO main_message_loop;
+ IPC::Channel chan(kReflectorChannel, IPC::Channel::MODE_CLIENT, NULL);
+ ChannelReflectorListener channel_reflector_listener(&chan);
+ chan.set_listener(&channel_reflector_listener);
+ chan.Connect();
+
+ MessageLoop::current()->Run();
+ return true;
+}
+
+#endif // PERFORMANCE_TEST
+
+int main(int argc, char** argv) {
+#ifdef PERFORMANCE_TEST
+ int retval = PerfTestSuite(argc, argv).Run();
+#else
+ int retval = TestSuite(argc, argv).Run();
+#endif
+ return retval;
+}