Chromium Code Reviews| Index: chrome/browser/importer/firefox_importer_unittest_utils_mac.cc |
| diff --git a/chrome/browser/importer/firefox_importer_unittest_utils_mac.cc b/chrome/browser/importer/firefox_importer_unittest_utils_mac.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d91dfa7c1ae98a6e62230332b031da31ef3bcd8c |
| --- /dev/null |
| +++ b/chrome/browser/importer/firefox_importer_unittest_utils_mac.cc |
| @@ -0,0 +1,262 @@ |
| +// Copyright (c) 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 "chrome/browser/importer/firefox_importer_unittest_utils.h" |
| + |
| +#include "base/command_line.h" |
| +#include "base/debug_on_start.h" |
| +#include "base/file_path.h" |
| +#include "base/message_loop.h" |
| +#include "chrome/browser/importer/firefox_importer_utils.h" |
| +#include "ipc/ipc_channel.h" |
| +#include "ipc/ipc_descriptors.h" |
| +#include "ipc/ipc_message.h" |
| +#include "ipc/ipc_message_utils.h" |
| +#include "ipc/ipc_switches.h" |
| +#include "testing/multiprocess_func_list.h" |
| + |
| +// Definition of IPC Messages used for this test. |
| +#define MESSAGES_INTERNAL_FILE \ |
| + "chrome/browser/importer/firefox_importer_unittest_messages_internal.h" |
| +#include "ipc/ipc_message_macros.h" |
| + |
| +namespace { |
| + |
| +// Name of IPC Channel to use for Server<-> Child Communications. |
| +const char kTestChannelID[] = "T1"; |
| + |
| +// Launch the child process: |
| +// |nss_path| - path to the NSS directory holding the decryption libraries. |
| +// |channel| - IPC Channel to use for communication. |
| +// |handle| - On return, the process handle to use to communicate with the |
| +// child. |
| +bool LaunchNSSDecrypterChildProcess(std::wstring& nss_path, |
| + IPC::Channel& channel, base::ProcessHandle* handle) { |
|
kuchhal
2009/08/21 16:24:57
make nss_path const and may be channel too.
|
| + CommandLine cl(*CommandLine::ForCurrentProcess()); |
| + cl.AppendSwitchWithValue(L"client", L"NSSDecrypterChildProcess"); |
| + |
| + FilePath ff_dylib_dir = FilePath::FromWStringHack(nss_path); |
| + // Set env variable needed for FF encryption libs to load. |
| + base::environment_vector env; |
| + std::pair<const char*,const char*> dyld_override; |
| + dyld_override.first = "DYLD_FALLBACK_LIBRARY_PATH"; |
|
John Grabowski
2009/08/21 01:13:18
Add comment explaining use of DYLD_FALLBACK_LIBRAR
|
| + dyld_override.second = ff_dylib_dir.value().c_str(); |
| + env.push_back(dyld_override); |
| + |
| + 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)); |
| + } else { |
| + return false; |
| + } |
| + |
| + bool debug_on_start = CommandLine::ForCurrentProcess()->HasSwitch( |
| + switches::kDebugChildren); |
| + return base::LaunchApp(cl.argv(), env, fds_to_map, debug_on_start, |
| + handle); |
| +} |
| + |
| +} // namespace |
| + |
| +//----------------------- Server -------------------- |
| + |
| +// Class to communicate on the server side of the IPC Channel. |
| +// Method calls are sent over IPC and replies are read back into class |
| +// variables. |
| +// This class relies needs to be called on a single thread. |
|
John Grabowski
2009/08/21 01:13:18
remove "relies"
|
| +class FFDecryptorServerChannelListener : public IPC::Channel::Listener { |
| + public: |
| + FFDecryptorServerChannelListener() |
| + : got_result(false), sender_(NULL) {} |
| + |
| + void SetSender(IPC::Message::Sender* sender) { |
| + sender_ = sender; |
| + } |
| + |
| + void OnInitDecryptorResponse(bool result) { |
| + DCHECK(!got_result); |
| + result_bool = result; |
| + got_result = true; |
| + MessageLoop::current()->Quit(); |
| + } |
| + |
| + void OnDecryptedTextResonse(std::wstring decrypted_text) { |
| + DCHECK(!got_result); |
| + result_string = decrypted_text; |
| + got_result = true; |
| + MessageLoop::current()->Quit(); |
| + } |
| + |
| + void QuitClient() { |
| + if (sender_) |
| + sender_->Send(new Msg_Decryptor_Quit()); |
| + } |
| + |
| + virtual void OnMessageReceived(const IPC::Message& msg) { |
| + IPC_BEGIN_MESSAGE_MAP(FFDecryptorServerChannelListener, msg) |
| + IPC_MESSAGE_HANDLER(Msg_Decryptor_InitReturnCode, OnInitDecryptorResponse) |
| + IPC_MESSAGE_HANDLER(Msg_Decryptor_Response, OnDecryptedTextResonse) |
| + IPC_END_MESSAGE_MAP() |
| + } |
| + |
| + // If an error occured, just kill the message Loop. |
| + virtual void OnChannelError() { |
| + got_result = false; |
| + MessageLoop::current()->Quit(); |
| + } |
| + |
| + // Results of IPC calls. |
| + std::wstring result_string; |
| + bool result_bool; |
| + // True if IPC call succeeded and data in above variables is valid. |
| + bool got_result; |
| + |
| + private: |
| + IPC::Message::Sender* sender_; |
|
John Grabowski
2009/08/21 01:13:18
// weak
|
| +}; |
| + |
| +FFUnitTestDecryptorProxy::FFUnitTestDecryptorProxy(std::wstring &nss_path, |
| + bool* success) |
| + : child_process_(0) { |
| + // Create a new message loop and spawn the child process. |
| + message_loop_.reset(new MessageLoopForIO()); |
| + |
| + listener_.reset(new FFDecryptorServerChannelListener()); |
| + channel_.reset(new IPC::Channel(kTestChannelID, |
| + IPC::Channel::MODE_SERVER, |
| + listener_.get())); |
| + channel_->Connect(); |
| + listener_->SetSender(channel_.get()); |
| + |
| + // Spawn child and set up sync IPC connection. |
| + bool ret = LaunchNSSDecrypterChildProcess(nss_path, |
| + *(channel_.get()), |
| + &child_process_); |
| + *success = ret && (child_process_ != 0); |
| +} |
| + |
| +FFUnitTestDecryptorProxy::~FFUnitTestDecryptorProxy() { |
| + listener_->QuitClient(); |
| + channel_->Close(); |
| + |
| + if (child_process_) { |
| + base::WaitForSingleProcess(child_process_, 5000); |
| + base::CloseProcessHandle(child_process_); |
| + } |
| +} |
| + |
| +// A message_loop task that quits the message loop when invoked, setting cancel |
| +// causes the task to do nothing when invoked. |
| +class CancellableQuitMsgLoop : public base::RefCounted<CancellableQuitMsgLoop> { |
| + public: |
| + CancellableQuitMsgLoop() : cancelled_(false) {} |
| + void QuitNow() { |
| + if (!cancelled_) |
| + MessageLoop::current()->Quit(); |
| + } |
| + bool cancelled_; |
| +}; |
| + |
| +// Spin until either a client response arrives or a timeout occurs. |
| +bool FFUnitTestDecryptorProxy::WaitForClientResponse() { |
| + const int64 kLoopTimeoutMS = 3 * 1000; // 3 seconds. |
|
John Grabowski
2009/08/21 01:13:18
Jack up to help prevent flaky tests
|
| + |
| + // What we're trying to do here is to wait for an RPC message to go over the |
| + // wire and the client to reply. If the client does relpy by a given timeout |
|
kuchhal
2009/08/21 16:24:57
does *not reply*
|
| + // we kill the message loop. |
| + // The way we do this is to post a CancellableQuitMsgLoop for 3 seconds in |
| + // the future and cancel it if an RPC message comes back earlier. |
| + // This relies on the IPC listener class to quit the message loop itself when |
| + // a message comes in. |
| + scoped_refptr<CancellableQuitMsgLoop> quit_task = |
| + new CancellableQuitMsgLoop(); |
| + MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod( |
| + quit_task.get(), &CancellableQuitMsgLoop::QuitNow), kLoopTimeoutMS); |
| + |
| + message_loop_->Run(); |
| + bool ret = !quit_task->cancelled_; |
| + quit_task->cancelled_ = false; |
| + return ret; |
| +} |
| + |
| +bool FFUnitTestDecryptorProxy::Init(const std::wstring& dll_path, |
| + const std::wstring& db_path) { |
| + channel_->Send(new Msg_Decryptor_Init(dll_path, db_path)); |
| + bool ok = WaitForClientResponse(); |
| + if (ok && listener_->got_result) { |
| + listener_->got_result = false; |
| + return listener_->result_bool; |
| + } |
| + return false; |
| +} |
| + |
| +std::wstring FFUnitTestDecryptorProxy::Decrypt(const std::string& crypt) { |
| + channel_->Send(new Msg_Decrypt(crypt)); |
| + bool ok = WaitForClientResponse(); |
| + if (ok && listener_->got_result) { |
| + listener_->got_result = false; |
| + return listener_->result_string; |
| + } |
| + return L""; |
| +} |
| + |
| +//---------------------------- Child Process ----------------------- |
| + |
| +// Class to listen on the client side of the ipc channel, it calls through |
| +// to the NSSDecryptor and sends back a reply. |
| +class FFDecryptorClientChannelListener : public IPC::Channel::Listener { |
| + public: |
| + FFDecryptorClientChannelListener() |
| + : sender_(NULL) {} |
| + |
| + void SetSender(IPC::Message::Sender* sender) { |
| + sender_ = sender; |
| + } |
| + |
| + void OnDecryptor_Init(std::wstring dll_path, std::wstring db_path) { |
| + bool ret = decryptor_.Init(dll_path, db_path); |
| + sender_->Send(new Msg_Decryptor_InitReturnCode(ret)); |
| + } |
| + |
| + void OnDecrypt(std::string crypt) { |
| + std::wstring unencrypted_str = decryptor_.Decrypt(crypt); |
| + sender_->Send(new Msg_Decryptor_Response(unencrypted_str)); |
| + } |
| + |
| + void OnQuitRequest() { |
| + MessageLoop::current()->Quit(); |
| + } |
| + |
| + virtual void OnMessageReceived(const IPC::Message& msg) { |
| + IPC_BEGIN_MESSAGE_MAP(FFDecryptorClientChannelListener, msg) |
| + IPC_MESSAGE_HANDLER(Msg_Decryptor_Init, OnDecryptor_Init) |
| + IPC_MESSAGE_HANDLER(Msg_Decrypt, OnDecrypt) |
| + IPC_MESSAGE_HANDLER(Msg_Decryptor_Quit, OnQuitRequest) |
| + IPC_END_MESSAGE_MAP() |
| + } |
| + |
| + virtual void OnChannelError() { |
| + MessageLoop::current()->Quit(); |
| + } |
| + |
| + private: |
| + NSSDecryptor decryptor_; |
| + IPC::Message::Sender* sender_; |
| +}; |
| + |
| +// Entry function in child process. |
| +MULTIPROCESS_TEST_MAIN(NSSDecrypterChildProcess) { |
| + MessageLoopForIO main_message_loop; |
| + FFDecryptorClientChannelListener listener; |
| + |
| + IPC::Channel channel(kTestChannelID, IPC::Channel::MODE_CLIENT, &listener); |
| + channel.Connect(); |
| + listener.SetSender(&channel); |
| + |
| + // run message loop |
| + MessageLoop::current()->Run(); |
| + |
| + return 0; |
| +} |