diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/common/ipc_sync_channel_unittest.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/ipc_sync_channel_unittest.cc')
-rw-r--r-- | chrome/common/ipc_sync_channel_unittest.cc | 625 |
1 files changed, 625 insertions, 0 deletions
diff --git a/chrome/common/ipc_sync_channel_unittest.cc b/chrome/common/ipc_sync_channel_unittest.cc new file mode 100644 index 0000000..74f046a --- /dev/null +++ b/chrome/common/ipc_sync_channel_unittest.cc @@ -0,0 +1,625 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Unit test for SyncChannel. + +#include <windows.h> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "base/thread.h" +#include "chrome/common/child_process.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/ipc_sync_channel.h" +#include "chrome/common/stl_util-inl.h" +#include "testing/gtest/include/gtest/gtest.h" + +#define IPC_MESSAGE_MACROS_ENUMS +#include "chrome/common/ipc_sync_channel_unittest.h" + +// define the classes +#define IPC_MESSAGE_MACROS_CLASSES +#include "chrome/common/ipc_sync_channel_unittest.h" + +using namespace IPC; + +// SyncChannel should only be used in child processes as we don't want to hang +// the browser. So in the unit test we need to have a ChildProcess object. +class TestProcess : public ChildProcess { + public: + explicit TestProcess(const std::wstring& channel_name) {} + static void GlobalInit() { + ChildProcessFactory<TestProcess> factory; + ChildProcess::GlobalInit(L"blah", &factory); + } +}; + +// Wrapper around an event handle. +class Event { + public: + Event() : handle_(CreateEvent(NULL, FALSE, FALSE, NULL)) { } + ~Event() { CloseHandle(handle_); } + void Set() { SetEvent(handle_); } + void Wait() { WaitForSingleObject(handle_, INFINITE); } + HANDLE handle() { return handle_; } + + private: + HANDLE handle_; + + DISALLOW_EVIL_CONSTRUCTORS(Event); +}; + +// Base class for a "process" with listener and IPC threads. +class Worker : public Channel::Listener, public Message::Sender { + public: + // Will create a channel without a name. + Worker(Channel::Mode mode, const std::string& thread_name) + : channel_name_(), + mode_(mode), + ipc_thread_((thread_name + "_ipc").c_str()), + listener_thread_((thread_name + "_listener").c_str()), + overrided_thread_(NULL) { } + + // Will create a named channel and use this name for the threads' name. + Worker(const std::wstring& channel_name, Channel::Mode mode) + : channel_name_(channel_name), + mode_(mode), + ipc_thread_((WideToUTF8(channel_name) + "_ipc").c_str()), + listener_thread_((WideToUTF8(channel_name) + "_listener").c_str()), + overrided_thread_(NULL) { } + + // The IPC thread needs to outlive SyncChannel, so force the correct order of + // destruction. + virtual ~Worker() { + CloseChannel(); + // We must stop the threads and release the channel here. The IPC thread + // must die before the listener thread, otherwise if its in the process of + // sending a message, it will get an error, it will use channel_, which + // references listener_. There are many ways of crashing, depending on + // timing. + // This is a race condition so you may not see it all the time even if you + // reverse the Stop() calls. You may see this bug with AppVerifier only. + ipc_thread_.Stop(); + listener_thread_.Stop(); + channel_.reset(); + } + void AddRef() { } + void Release() { } + bool Send(Message* msg) { return channel_->Send(msg); } + void WaitForChannelCreation() { channel_created_.Wait(); } + void CloseChannel() { channel_.reset(); } + void Start() { + listener_thread_.Start(); + Thread* thread = overrided_thread_ ? overrided_thread_ : &listener_thread_; + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &Worker::OnStart)); + } + void OverrideThread(Thread* overrided_thread) { + DCHECK(overrided_thread_ == NULL); + overrided_thread_ = overrided_thread; + } + Channel::Mode mode() { return mode_; } + HANDLE done_event() { return done_.handle(); } + + protected: + // Derived classes need to call this when they've completed their part of + // the test. + void Done() { done_.Set(); } + + // Functions for dervied classes to implement if they wish. + virtual void Run() { } + virtual void OnDouble(int in, int* out) { NOTREACHED(); } + virtual void OnAnswer(int* answer) { NOTREACHED(); } + virtual void OnAnswerDelay(Message* reply_msg) { + // The message handler map below can only take one entry for + // SyncChannelTestMsg_AnswerToLife, so since some classes want + // the normal version while other want the delayed reply, we + // call the normal version if the derived class didn't override + // this function. + int answer; + OnAnswer(&answer); + SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, answer); + Send(reply_msg); + } + + private: + // Called on the listener thread to create the sync channel. + void OnStart() { + ipc_thread_.Start(); + // Link ipc_thread_, listener_thread_ and channel_ altogether. + channel_.reset(new SyncChannel( + channel_name_, mode_, this, ipc_thread_.message_loop(), true)); + channel_created_.Set(); + Run(); + } + + void OnMessageReceived(const Message& message) { + IPC_BEGIN_MESSAGE_MAP(Worker, message) + IPC_MESSAGE_HANDLER(SyncChannelTestMsg_Double, OnDouble) + IPC_MESSAGE_HANDLER_DELAY_REPLY(SyncChannelTestMsg_AnswerToLife, + OnAnswerDelay) + IPC_END_MESSAGE_MAP() + } + + Event done_; + Event channel_created_; + std::wstring channel_name_; + Channel::Mode mode_; + scoped_ptr<SyncChannel> channel_; + Thread ipc_thread_; + Thread listener_thread_; + Thread* overrided_thread_; + + DISALLOW_EVIL_CONSTRUCTORS(Worker); +}; + + +// Starts the test with the given workers. This function deletes the workers +// when it's done. +void RunTest(std::vector<Worker*> workers) { + TestProcess::GlobalInit(); + + // First we create the workers that are channel servers, or else the other + // workers' channel initialization might fail because the pipe isn't created.. + for (size_t i = 0; i < workers.size(); ++i) { + if (workers[i]->mode() == Channel::MODE_SERVER) { + workers[i]->Start(); + workers[i]->WaitForChannelCreation(); + } + } + + // now create the clients + for (size_t i = 0; i < workers.size(); ++i) { + if (workers[i]->mode() == Channel::MODE_CLIENT) + workers[i]->Start(); + } + + // wait for all the workers to finish + std::vector<HANDLE> done_handles; + for (size_t i = 0; i < workers.size(); ++i) + done_handles.push_back(workers[i]->done_event()); + + int count = static_cast<int>(done_handles.size()); + WaitForMultipleObjects(count, &done_handles.front(), TRUE, INFINITE); + STLDeleteContainerPointers(workers.begin(), workers.end()); +} + + +//----------------------------------------------------------------------------- +class SimpleServer : public Worker { + public: + SimpleServer() : Worker(Channel::MODE_SERVER, "simpler_server") { } + void Run() { + int answer = 0; + bool result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(result); + DCHECK(answer == 42); + Done(); + } +}; + +class SimpleClient : public Worker { + public: + SimpleClient() : Worker(Channel::MODE_CLIENT, "simple_client") { } + + void OnAnswer(int* answer) { + *answer = 42; + Done(); + } +}; + +// Tests basic synchronous call +TEST(IPCSyncChannelTest, Simple) { + std::vector<Worker*> workers; + workers.push_back(new SimpleServer()); + workers.push_back(new SimpleClient()); + RunTest(workers); +} + + +//----------------------------------------------------------------------------- +class DelayClient : public Worker { + public: + DelayClient() : Worker(Channel::MODE_CLIENT, "delay_client") { } + + void OnAnswerDelay(Message* reply_msg) { + SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42); + Send(reply_msg); + Done(); + } +}; + +// Tests that asynchronous replies work +TEST(IPCSyncChannelTest, DelayReply) { + std::vector<Worker*> workers; + workers.push_back(new SimpleServer()); + workers.push_back(new DelayClient()); + RunTest(workers); +} + + +//----------------------------------------------------------------------------- +class NoHangServer : public Worker { + public: + explicit NoHangServer(Event* got_first_reply) + : Worker(Channel::MODE_SERVER, "no_hang_server"), + got_first_reply_(got_first_reply) { } + void Run() { + int answer = 0; + bool result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(result); + DCHECK(answer == 42); + got_first_reply_->Set(); + + result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(!result); + Done(); + } + + Event* got_first_reply_; +}; + +class NoHangClient : public Worker { + public: + explicit NoHangClient(Event* got_first_reply) + : Worker(Channel::MODE_CLIENT, "no_hang_client"), + got_first_reply_(got_first_reply) { } + + virtual void OnAnswerDelay(Message* reply_msg) { + // Use the DELAY_REPLY macro so that we can force the reply to be sent + // before this function returns (when the channel will be reset). + SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42); + Send(reply_msg); + got_first_reply_->Wait(); + CloseChannel(); + Done(); + } + + Event* got_first_reply_; +}; + +// Tests that caller doesn't hang if receiver dies +TEST(IPCSyncChannelTest, NoHang) { + Event got_first_reply; + + std::vector<Worker*> workers; + workers.push_back(new NoHangServer(&got_first_reply)); + workers.push_back(new NoHangClient(&got_first_reply)); + RunTest(workers); +} + + +//----------------------------------------------------------------------------- +class RecursiveServer : public Worker { + public: + RecursiveServer() : Worker(Channel::MODE_SERVER, "recursive_server") { } + void Run() { + int answer = 0; + bool result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(result); + DCHECK(answer == 42); + Done(); + } + + void OnDouble(int in, int* out) { + *out = in * 2; + } +}; + +class RecursiveClient : public Worker { + public: + RecursiveClient() : Worker(Channel::MODE_CLIENT, "recursive_client") { } + + void OnAnswer(int* answer) { + BOOL result = Send(new SyncChannelTestMsg_Double(21, answer)); + DCHECK(result); + Done(); + } +}; + +// Tests that the caller unblocks to answer a sync message from the receiver. +TEST(IPCSyncChannelTest, Recursive) { + std::vector<Worker*> workers; + workers.push_back(new RecursiveServer()); + workers.push_back(new RecursiveClient()); + RunTest(workers); +} + + +//----------------------------------------------------------------------------- +class MultipleServer1 : public Worker { + public: + MultipleServer1() : Worker(L"test_channel1", Channel::MODE_SERVER) { } + void Run() { + int answer = 0; + bool result = Send(new SyncChannelTestMsg_Double(5, &answer)); + DCHECK(result); + DCHECK(answer == 10); + Done(); + } +}; + +class MultipleClient1 : public Worker { + public: + MultipleClient1(Event* client1_msg_received, Event* client1_can_reply) : + Worker(L"test_channel1", Channel::MODE_CLIENT), + client1_msg_received_(client1_msg_received), + client1_can_reply_(client1_can_reply) { } + + void OnDouble(int in, int* out) { + client1_msg_received_->Set(); + *out = in * 2; + client1_can_reply_->Wait(); + Done(); + } + + private: + Event *client1_msg_received_, *client1_can_reply_; +}; + +class MultipleServer2 : public Worker { + public: + MultipleServer2() : Worker(L"test_channel2", Channel::MODE_SERVER) { } + + void OnAnswer(int* result) { + *result = 42; + Done(); + } +}; + +class MultipleClient2 : public Worker { + public: + MultipleClient2(Event* client1_msg_received, Event* client1_can_reply) : + Worker(L"test_channel2", Channel::MODE_CLIENT), + client1_msg_received_(client1_msg_received), + client1_can_reply_(client1_can_reply) { } + + void Run() { + int answer = 0; + client1_msg_received_->Wait(); + bool result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(result); + DCHECK(answer == 42); + client1_can_reply_->Set(); + Done(); + } + + private: + Event *client1_msg_received_, *client1_can_reply_; +}; + +// Tests that multiple SyncObjects on the same listener thread can unblock each +// other. +TEST(IPCSyncChannelTest, Multiple) { + std::vector<Worker*> workers; + + // A shared worker thread so that server1 and server2 run on one thread. + Thread worker_thread("Multiple"); + worker_thread.Start(); + + // Server1 sends a sync msg to client1, which blocks the reply until + // server2 (which runs on the same worker thread as server1) responds + // to a sync msg from client2. + Event client1_msg_received, client1_can_reply; + + Worker* worker; + + worker = new MultipleServer2(); + worker->OverrideThread(&worker_thread); + workers.push_back(worker); + + worker = new MultipleClient2( + &client1_msg_received, &client1_can_reply); + workers.push_back(worker); + + worker = new MultipleServer1(); + worker->OverrideThread(&worker_thread); + workers.push_back(worker); + + worker = new MultipleClient1( + &client1_msg_received, &client1_can_reply); + workers.push_back(worker); + + RunTest(workers); +} + + +//----------------------------------------------------------------------------- +class QueuedReplyServer1 : public Worker { + public: + QueuedReplyServer1() : Worker(L"test_channel1", Channel::MODE_SERVER) { } + void Run() { + int answer = 0; + bool result = Send(new SyncChannelTestMsg_Double(5, &answer)); + DCHECK(result); + DCHECK(answer == 10); + Done(); + } +}; + +class QueuedReplyClient1 : public Worker { + public: + QueuedReplyClient1(Event* client1_msg_received, Event* server2_can_reply) : + Worker(L"test_channel1", Channel::MODE_CLIENT), + client1_msg_received_(client1_msg_received), + server2_can_reply_(server2_can_reply) { } + + void OnDouble(int in, int* out) { + client1_msg_received_->Set(); + *out = in * 2; + server2_can_reply_->Wait(); + Done(); + } + + private: + Event *client1_msg_received_, *server2_can_reply_; +}; + +class QueuedReplyServer2 : public Worker { + public: + explicit QueuedReplyServer2(Event* server2_can_reply) : + Worker(L"test_channel2", Channel::MODE_SERVER), + server2_can_reply_(server2_can_reply) { } + + void OnAnswer(int* result) { + server2_can_reply_->Set(); + + // give client1's reply time to reach the server listener thread + Sleep(200); + + *result = 42; + Done(); + } + + Event *server2_can_reply_; +}; + +class QueuedReplyClient2 : public Worker { + public: + explicit QueuedReplyClient2(Event* client1_msg_received) : + Worker(L"test_channel2", Channel::MODE_CLIENT), + client1_msg_received_(client1_msg_received) { } + + void Run() { + int answer = 0; + client1_msg_received_->Wait(); + bool result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(result); + DCHECK(answer == 42); + Done(); + } + + private: + Event *client1_msg_received_; +}; + +// While a blocking send is in progress, the listener thread might answer other +// synchronous messages. This tests that if during the response to another +// message the reply to the original messages comes, it is queued up correctly +// and the original Send is unblocked later. +TEST(IPCSyncChannelTest, QueuedReply) { + std::vector<Worker*> workers; + + // A shared worker thread so that server1 and server2 run on one thread. + Thread worker_thread("QueuedReply"); + worker_thread.Start(); + + Event client1_msg_received, server2_can_reply; + + Worker* worker; + + worker = new QueuedReplyServer2(&server2_can_reply); + worker->OverrideThread(&worker_thread); + workers.push_back(worker); + + worker = new QueuedReplyClient2(&client1_msg_received); + workers.push_back(worker); + + worker = new QueuedReplyServer1(); + worker->OverrideThread(&worker_thread); + workers.push_back(worker); + + worker = new QueuedReplyClient1( + &client1_msg_received, &server2_can_reply); + workers.push_back(worker); + + RunTest(workers); +} + + +//----------------------------------------------------------------------------- +class BadServer : public Worker { + public: + BadServer() : Worker(Channel::MODE_SERVER, "simpler_server") { } + void Run() { + int answer = 0; + + Message* msg = new SyncMessage(MSG_ROUTING_CONTROL, + SyncChannelTestMsg_Double::ID, + Message::PRIORITY_NORMAL, + NULL); + // Temporarily set the minimum logging very high so that the assertion + // in ipc_message_utils doesn't fire. + int log_level = logging::GetMinLogLevel(); + logging::SetMinLogLevel(kint32max); + bool result = Send(msg); + logging::SetMinLogLevel(log_level); + DCHECK(!result); + + // Need to send another message to get the client to call Done(). + result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(result); + DCHECK(answer == 42); + + Done(); + } +}; + +// Tests that if a message is not serialized correctly, the Send() will fail. +TEST(IPCSyncChannelTest, BadMessage) { + std::vector<Worker*> workers; + workers.push_back(new BadServer()); + workers.push_back(new SimpleClient()); + RunTest(workers); +} + + +//----------------------------------------------------------------------------- +class ChattyRecursiveClient : public Worker { + public: + ChattyRecursiveClient() : + Worker(Channel::MODE_CLIENT, "chatty_recursive_client") { } + + void OnAnswer(int* answer) { + // The PostMessage limit is 10k. Send 20% more than that. + const int kMessageLimit = 10000; + const int kMessagesToSend = kMessageLimit * 120 / 100; + for (int i = 0; i < kMessagesToSend; ++i) { + bool result = Send(new SyncChannelTestMsg_Double(21, answer)); + DCHECK(result); + if (!result) + break; + } + Done(); + } +}; + +// Tests http://b/issue?id=1093251 - that sending lots of sync messages while +// the receiver is waiting for a sync reply does not overflow the PostMessage +// queue. +TEST(IPCSyncChannelTest, ChattyServer) { + std::vector<Worker*> workers; + workers.push_back(new RecursiveServer()); + workers.push_back(new ChattyRecursiveClient()); + RunTest(workers); +} |