Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / utility / importer / firefox_importer_unittest_utils_mac.cc
1 // Copyright (c) 2012 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.
4
5 #include "chrome/utility/importer/firefox_importer_unittest_utils.h"
6
7 #include "base/base_switches.h"
8 #include "base/bind.h"
9 #include "base/command_line.h"
10 #include "base/file_util.h"
11 #include "base/files/file_path.h"
12 #include "base/files/scoped_file.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/posix/global_descriptors.h"
15 #include "base/process/kill.h"
16 #include "base/process/launch.h"
17 #include "base/test/test_timeouts.h"
18 #include "chrome/common/importer/firefox_importer_utils.h"
19 #include "ipc/ipc_channel.h"
20 #include "ipc/ipc_descriptors.h"
21 #include "ipc/ipc_listener.h"
22 #include "ipc/ipc_message.h"
23 #include "ipc/ipc_multiprocess_test.h"
24 #include "testing/multiprocess_func_list.h"
25
26 #define IPC_MESSAGE_IMPL
27 #include "chrome/utility/importer/firefox_importer_unittest_messages_internal.h"
28
29 namespace {
30
31 // Name of IPC Channel to use for Server<-> Child Communications.
32 const char kTestChannelID[] = "T1";
33
34 // Launch the child process:
35 // |nss_path| - path to the NSS directory holding the decryption libraries.
36 // |channel| - IPC Channel to use for communication.
37 // |handle| - On return, the process handle to use to communicate with the
38 // child.
39 bool LaunchNSSDecrypterChildProcess(const base::FilePath& nss_path,
40     IPC::Channel* channel, base::ProcessHandle* handle) {
41   CommandLine cl(*CommandLine::ForCurrentProcess());
42   cl.AppendSwitchASCII(switches::kTestChildProcess, "NSSDecrypterChildProcess");
43
44   // Set env variable needed for FF encryption libs to load.
45   // See "chrome/utility/importer/nss_decryptor_mac.mm" for an explanation of
46   // why we need this.
47   base::LaunchOptions options;
48   options.environ["DYLD_FALLBACK_LIBRARY_PATH"] = nss_path.value();
49
50   base::ScopedFD ipcfd(channel->TakeClientFileDescriptor());
51   if (!ipcfd.is_valid())
52     return false;
53
54   base::FileHandleMappingVector fds_to_map;
55   fds_to_map.push_back(std::pair<int,int>(ipcfd.get(),
56       kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor));
57
58   options.fds_to_remap = &fds_to_map;
59   return base::LaunchProcess(cl.argv(), options, handle);
60 }
61
62 }  // namespace
63
64 //----------------------- Server --------------------
65
66 // Class to communicate on the server side of the IPC Channel.
67 // Method calls are sent over IPC and replies are read back into class
68 // variables.
69 // This class needs to be called on a single thread.
70 class FFDecryptorServerChannelListener : public IPC::Listener {
71  public:
72   FFDecryptorServerChannelListener()
73       : got_result(false), sender_(NULL) {}
74
75   void SetSender(IPC::Sender* sender) {
76     sender_ = sender;
77   }
78
79   void OnInitDecryptorResponse(bool result) {
80     DCHECK(!got_result);
81     result_bool = result;
82     got_result = true;
83     base::MessageLoop::current()->Quit();
84   }
85
86   void OnDecryptedTextResonse(const base::string16& decrypted_text) {
87     DCHECK(!got_result);
88     result_string = decrypted_text;
89     got_result = true;
90     base::MessageLoop::current()->Quit();
91   }
92
93   void QuitClient() {
94     if (sender_)
95       sender_->Send(new Msg_Decryptor_Quit());
96   }
97
98   virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE {
99     bool handled = true;
100     IPC_BEGIN_MESSAGE_MAP(FFDecryptorServerChannelListener, msg)
101       IPC_MESSAGE_HANDLER(Msg_Decryptor_InitReturnCode, OnInitDecryptorResponse)
102       IPC_MESSAGE_HANDLER(Msg_Decryptor_Response, OnDecryptedTextResonse)
103       IPC_MESSAGE_UNHANDLED(handled = false)
104     IPC_END_MESSAGE_MAP()
105     return handled;
106   }
107
108   // If an error occured, just kill the message Loop.
109   virtual void OnChannelError() OVERRIDE {
110     got_result = false;
111     base::MessageLoop::current()->Quit();
112   }
113
114   // Results of IPC calls.
115   base::string16 result_string;
116   bool result_bool;
117   // True if IPC call succeeded and data in above variables is valid.
118   bool got_result;
119
120  private:
121   IPC::Sender* sender_;  // weak
122 };
123
124 FFUnitTestDecryptorProxy::FFUnitTestDecryptorProxy()
125     : child_process_(0) {
126 }
127
128 bool FFUnitTestDecryptorProxy::Setup(const base::FilePath& nss_path) {
129   // Create a new message loop and spawn the child process.
130   message_loop_.reset(new base::MessageLoopForIO());
131
132   listener_.reset(new FFDecryptorServerChannelListener());
133   channel_.reset(new IPC::Channel(kTestChannelID,
134                                   IPC::Channel::MODE_SERVER,
135                                   listener_.get()));
136   CHECK(channel_->Connect());
137   listener_->SetSender(channel_.get());
138
139   // Spawn child and set up sync IPC connection.
140   bool ret = LaunchNSSDecrypterChildProcess(nss_path,
141                                             channel_.get(),
142                                             &child_process_);
143   return ret && (child_process_ != 0);
144 }
145
146 FFUnitTestDecryptorProxy::~FFUnitTestDecryptorProxy() {
147   listener_->QuitClient();
148   channel_->Close();
149
150   if (child_process_) {
151     base::WaitForSingleProcess(child_process_, base::TimeDelta::FromSeconds(5));
152     base::CloseProcessHandle(child_process_);
153   }
154 }
155
156 // A message_loop task that quits the message loop when invoked, setting cancel
157 // causes the task to do nothing when invoked.
158 class CancellableQuitMsgLoop : public base::RefCounted<CancellableQuitMsgLoop> {
159  public:
160   CancellableQuitMsgLoop() : cancelled_(false) {}
161   void QuitNow() {
162     if (!cancelled_)
163       base::MessageLoop::current()->Quit();
164   }
165   bool cancelled_;
166
167  private:
168   friend class base::RefCounted<CancellableQuitMsgLoop>;
169   ~CancellableQuitMsgLoop() {}
170 };
171
172 // Spin until either a client response arrives or a timeout occurs.
173 bool FFUnitTestDecryptorProxy::WaitForClientResponse() {
174   // What we're trying to do here is to wait for an RPC message to go over the
175   // wire and the client to reply.  If the client does not reply by a given
176   // timeout we kill the message loop.
177   // The way we do this is to post a CancellableQuitMsgLoop for 3 seconds in
178   // the future and cancel it if an RPC message comes back earlier.
179   // This relies on the IPC listener class to quit the message loop itself when
180   // a message comes in.
181   scoped_refptr<CancellableQuitMsgLoop> quit_task(
182       new CancellableQuitMsgLoop());
183   base::MessageLoop::current()->PostDelayedTask(
184       FROM_HERE,
185       base::Bind(&CancellableQuitMsgLoop::QuitNow, quit_task.get()),
186       TestTimeouts::action_max_timeout());
187
188   message_loop_->Run();
189   bool ret = !quit_task->cancelled_;
190   quit_task->cancelled_ = false;
191   return ret;
192 }
193
194 bool FFUnitTestDecryptorProxy::DecryptorInit(const base::FilePath& dll_path,
195                                              const base::FilePath& db_path) {
196   channel_->Send(new Msg_Decryptor_Init(dll_path, db_path));
197   bool ok = WaitForClientResponse();
198   if (ok && listener_->got_result) {
199     listener_->got_result = false;
200     return listener_->result_bool;
201   }
202   return false;
203 }
204
205 base::string16 FFUnitTestDecryptorProxy::Decrypt(const std::string& crypt) {
206   channel_->Send(new Msg_Decrypt(crypt));
207   bool ok = WaitForClientResponse();
208   if (ok && listener_->got_result) {
209     listener_->got_result = false;
210     return listener_->result_string;
211   }
212   return base::string16();
213 }
214
215 //---------------------------- Child Process -----------------------
216
217 // Class to listen on the client side of the ipc channel, it calls through
218 // to the NSSDecryptor and sends back a reply.
219 class FFDecryptorClientChannelListener : public IPC::Listener {
220  public:
221   FFDecryptorClientChannelListener()
222       : sender_(NULL) {}
223
224   void SetSender(IPC::Sender* sender) {
225     sender_ = sender;
226   }
227
228   void OnDecryptor_Init(base::FilePath dll_path, base::FilePath db_path) {
229     bool ret = decryptor_.Init(dll_path, db_path);
230     sender_->Send(new Msg_Decryptor_InitReturnCode(ret));
231   }
232
233   void OnDecrypt(std::string crypt) {
234     base::string16 unencrypted_str = decryptor_.Decrypt(crypt);
235     sender_->Send(new Msg_Decryptor_Response(unencrypted_str));
236   }
237
238   void OnQuitRequest() {
239     base::MessageLoop::current()->Quit();
240   }
241
242   virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE {
243     bool handled = true;
244     IPC_BEGIN_MESSAGE_MAP(FFDecryptorClientChannelListener, msg)
245       IPC_MESSAGE_HANDLER(Msg_Decryptor_Init, OnDecryptor_Init)
246       IPC_MESSAGE_HANDLER(Msg_Decrypt, OnDecrypt)
247       IPC_MESSAGE_HANDLER(Msg_Decryptor_Quit, OnQuitRequest)
248       IPC_MESSAGE_UNHANDLED(handled = false)
249     IPC_END_MESSAGE_MAP()
250     return handled;
251   }
252
253   virtual void OnChannelError() OVERRIDE {
254     base::MessageLoop::current()->Quit();
255   }
256
257  private:
258   NSSDecryptor decryptor_;
259   IPC::Sender* sender_;
260 };
261
262 // Entry function in child process.
263 MULTIPROCESS_IPC_TEST_MAIN(NSSDecrypterChildProcess) {
264   base::MessageLoopForIO main_message_loop;
265   FFDecryptorClientChannelListener listener;
266
267   IPC::Channel channel(kTestChannelID, IPC::Channel::MODE_CLIENT, &listener);
268   CHECK(channel.Connect());
269   listener.SetSender(&channel);
270
271   // run message loop
272   base::MessageLoop::current()->Run();
273
274   return 0;
275 }