// Copyright (c) 2012 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 #elif defined(OS_POSIX) #include #include #endif #include #include #include #include "base/command_line.h" #include "base/pickle.h" #include "base/threading/thread.h" #include "base/time.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_multiprocess_test.h" #include "ipc/ipc_sender.h" #include "ipc/ipc_switches.h" #include "ipc/ipc_test_base.h" #include "testing/multiprocess_func_list.h" namespace { const size_t kLongMessageStringNumBytes = 50000; class IPCChannelTest : public IPCTestBase { }; 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)); PickleIterator iter(m); 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::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::Listener { public: virtual bool OnMessageReceived(const IPC::Message& message) { PickleIterator iter(message); int ignored; EXPECT_TRUE(iter.ReadInt(&ignored)); std::string data; EXPECT_TRUE(iter.ReadString(&data)); std::string big_string; EXPECT_TRUE(iter.ReadString(&big_string)); EXPECT_EQ(kLongMessageStringNumBytes - 1, big_string.length()); if (--messages_left_ == 0) { MessageLoop::current()->Quit(); } else { Send(sender_, "Foo"); } return true; } 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::Sender* s) { sender_ = s; messages_left_ = 50; } private: IPC::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); ASSERT_TRUE(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, base::TimeDelta::FromSeconds(5))); base::CloseProcessHandle(process_handle); } #if defined(OS_WIN) TEST_F(IPCChannelTest, ChannelTestExistingPipe) { MyChannelListener channel_listener; // Setup IPC channel with existing pipe. Specify name in Chrome format. std::string name("\\\\.\\pipe\\chrome."); name.append(kTestClientChannel); const DWORD open_mode = PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE; HANDLE pipe = CreateNamedPipeA(name.c_str(), open_mode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 4096, 4096, 5000, NULL); IPC::Channel chan(IPC::ChannelHandle(pipe), IPC::Channel::MODE_SERVER, &channel_listener); // Channel will duplicate the handle. CloseHandle(pipe); ASSERT_TRUE(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, base::TimeDelta::FromSeconds(5))); base::CloseProcessHandle(process_handle); } #endif // defined (OS_WIN) 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, thread.message_loop_proxy()); 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::FileHandleMappingVector fds_to_map; const int ipcfd = chan.GetClientFileDescriptor(); if (ipcfd > -1) { fds_to_map.push_back(std::pair(ipcfd, kPrimaryIPCChannel + 3)); } base::ProcessHandle process_handle = MultiProcessTest::SpawnChild( "RunTestClient", fds_to_map, debug_on_start); #endif // defined(OS_POSIX) ASSERT_TRUE(process_handle); Send(&chan, "hello from parent"); // run message loop MessageLoop::current()->Run(); // cleanup child process EXPECT_TRUE(base::WaitForSingleProcess( process_handle, base::TimeDelta::FromSeconds(5))); base::CloseProcessHandle(process_handle); } thread.Stop(); } class ChannelListenerWithOnConnectedSend : public IPC::Listener { public: virtual void OnChannelConnected(int32 peer_pid) OVERRIDE { SendNextMessage(); } virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { PickleIterator iter(message); int ignored; EXPECT_TRUE(iter.ReadInt(&ignored)); std::string data; EXPECT_TRUE(iter.ReadString(&data)); std::string big_string; EXPECT_TRUE(iter.ReadString(&big_string)); EXPECT_EQ(kLongMessageStringNumBytes - 1, big_string.length()); SendNextMessage(); return true; } virtual void OnChannelError() OVERRIDE { // 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::Sender* s) { sender_ = s; messages_left_ = 50; } private: void SendNextMessage() { if (--messages_left_ == 0) { MessageLoop::current()->Quit(); } else { Send(sender_, "Foo"); } } IPC::Sender* sender_; int messages_left_; }; #if defined(OS_WIN) // Acting flakey in Windows. http://crbug.com/129595 #define MAYBE_SendMessageInChannelConnected DISABLED_SendMessageInChannelConnected #else #define MAYBE_SendMessageInChannelConnected SendMessageInChannelConnected #endif TEST_F(IPCChannelTest, MAYBE_SendMessageInChannelConnected) { // This tests the case of a listener sending back an event in it's // OnChannelConnected handler. ChannelListenerWithOnConnectedSend channel_listener; // Setup IPC channel. IPC::Channel channel(kTestClientChannel, IPC::Channel::MODE_SERVER, &channel_listener); channel_listener.Init(&channel); ASSERT_TRUE(channel.Connect()); base::ProcessHandle process_handle = SpawnChild(TEST_CLIENT, &channel); ASSERT_TRUE(process_handle); Send(&channel, "hello from parent"); // Run message loop. MessageLoop::current()->Run(); // Close Channel so client gets its OnChannelError() callback fired. channel.Close(); // Cleanup child process. EXPECT_TRUE(base::WaitForSingleProcess( process_handle, base::TimeDelta::FromSeconds(5))); base::CloseProcessHandle(process_handle); } MULTIPROCESS_IPC_TEST_MAIN(RunTestClient) { MessageLoopForIO main_message_loop; MyChannelListener channel_listener; // setup IPC channel IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT, &channel_listener); CHECK(chan.Connect()); channel_listener.Init(&chan); Send(&chan, "hello from child"); // run message loop MessageLoop::current()->Run(); return 0; } } // namespace