- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / process_singleton_linux_unittest.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/browser/process_singleton.h"
6
7 #include <signal.h>
8 #include <sys/types.h>
9 #include <sys/wait.h>
10 #include <unistd.h>
11
12 #include <string>
13 #include <vector>
14
15 #include "base/bind.h"
16 #include "base/command_line.h"
17 #include "base/files/file_path.h"
18 #include "base/files/scoped_temp_dir.h"
19 #include "base/message_loop/message_loop.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/synchronization/waitable_event.h"
22 #include "base/test/test_timeouts.h"
23 #include "base/test/thread_test_helper.h"
24 #include "base/threading/thread.h"
25 #include "chrome/common/chrome_constants.h"
26 #include "content/public/test/test_browser_thread.h"
27 #include "net/base/net_util.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29
30 using content::BrowserThread;
31
32 namespace {
33
34 bool NotificationCallback(const CommandLine& command_line,
35                           const base::FilePath& current_directory) {
36   return true;
37 }
38
39 class ProcessSingletonLinuxTest : public testing::Test {
40  public:
41   // A ProcessSingleton exposing some protected methods for testing.
42   class TestableProcessSingleton : public ProcessSingleton {
43    public:
44     explicit TestableProcessSingleton(const base::FilePath& user_data_dir)
45         : ProcessSingleton(
46             user_data_dir,
47             base::Bind(&TestableProcessSingleton::NotificationCallback,
48                        base::Unretained(this))) {}
49
50
51     std::vector<CommandLine::StringVector> callback_command_lines_;
52
53     using ProcessSingleton::NotifyOtherProcessWithTimeout;
54     using ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate;
55     using ProcessSingleton::OverrideCurrentPidForTesting;
56     using ProcessSingleton::OverrideKillCallbackForTesting;
57
58    private:
59     bool NotificationCallback(const CommandLine& command_line,
60                               const base::FilePath& current_directory) {
61       callback_command_lines_.push_back(command_line.argv());
62       return true;
63     }
64   };
65
66   ProcessSingletonLinuxTest()
67       : kill_callbacks_(0),
68         io_thread_(BrowserThread::IO),
69         wait_event_(true, false),
70         signal_event_(true, false),
71         process_singleton_on_thread_(NULL) {
72     io_thread_.StartIOThread();
73   }
74
75   virtual void SetUp() {
76     testing::Test::SetUp();
77
78     ProcessSingleton::DisablePromptForTesting();
79     // Put the lock in a temporary directory.  Doesn't need to be a
80     // full profile to test this code.
81     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
82     lock_path_ = temp_dir_.path().Append(chrome::kSingletonLockFilename);
83     socket_path_ = temp_dir_.path().Append(chrome::kSingletonSocketFilename);
84     cookie_path_ = temp_dir_.path().Append(chrome::kSingletonCookieFilename);
85   }
86
87   virtual void TearDown() {
88     scoped_refptr<base::ThreadTestHelper> io_helper(new base::ThreadTestHelper(
89         BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get()));
90     ASSERT_TRUE(io_helper->Run());
91
92     // Destruct the ProcessSingleton object before the IO thread so that its
93     // internals are destructed properly.
94     if (process_singleton_on_thread_) {
95       worker_thread_->message_loop()->PostTask(
96           FROM_HERE,
97           base::Bind(&ProcessSingletonLinuxTest::DestructProcessSingleton,
98                      base::Unretained(this)));
99
100       scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
101           worker_thread_->message_loop_proxy().get()));
102       ASSERT_TRUE(helper->Run());
103     }
104
105     io_thread_.Stop();
106     testing::Test::TearDown();
107   }
108
109   void CreateProcessSingletonOnThread() {
110     ASSERT_EQ(NULL, worker_thread_.get());
111     worker_thread_.reset(new base::Thread("BlockingThread"));
112     worker_thread_->Start();
113
114     worker_thread_->message_loop()->PostTask(
115        FROM_HERE,
116        base::Bind(&ProcessSingletonLinuxTest::
117                       CreateProcessSingletonInternal,
118                   base::Unretained(this)));
119
120     scoped_refptr<base::ThreadTestHelper> helper(
121         new base::ThreadTestHelper(worker_thread_->message_loop_proxy().get()));
122     ASSERT_TRUE(helper->Run());
123   }
124
125   TestableProcessSingleton* CreateProcessSingleton() {
126     return new TestableProcessSingleton(temp_dir_.path());
127   }
128
129   ProcessSingleton::NotifyResult NotifyOtherProcess(
130       bool override_kill,
131       base::TimeDelta timeout) {
132     scoped_ptr<TestableProcessSingleton> process_singleton(
133         CreateProcessSingleton());
134     CommandLine command_line(CommandLine::ForCurrentProcess()->GetProgram());
135     command_line.AppendArg("about:blank");
136     if (override_kill) {
137       process_singleton->OverrideCurrentPidForTesting(
138           base::GetCurrentProcId() + 1);
139       process_singleton->OverrideKillCallbackForTesting(
140           base::Bind(&ProcessSingletonLinuxTest::KillCallback,
141                      base::Unretained(this)));
142     }
143
144     return process_singleton->NotifyOtherProcessWithTimeout(
145         command_line, timeout.InSeconds(), true);
146   }
147
148   // A helper method to call ProcessSingleton::NotifyOtherProcessOrCreate().
149   ProcessSingleton::NotifyResult NotifyOtherProcessOrCreate(
150       const std::string& url,
151       base::TimeDelta timeout) {
152     scoped_ptr<TestableProcessSingleton> process_singleton(
153         CreateProcessSingleton());
154     CommandLine command_line(CommandLine::ForCurrentProcess()->GetProgram());
155     command_line.AppendArg(url);
156     return process_singleton->NotifyOtherProcessWithTimeoutOrCreate(
157         command_line, timeout.InSeconds());
158   }
159
160   void CheckNotified() {
161     ASSERT_TRUE(process_singleton_on_thread_ != NULL);
162     ASSERT_EQ(1u, process_singleton_on_thread_->callback_command_lines_.size());
163     bool found = false;
164     for (size_t i = 0;
165          i < process_singleton_on_thread_->callback_command_lines_[0].size();
166          ++i) {
167       if (process_singleton_on_thread_->callback_command_lines_[0][i] ==
168           "about:blank") {
169         found = true;
170         break;
171       }
172     }
173     ASSERT_TRUE(found);
174     ASSERT_EQ(0, kill_callbacks_);
175   }
176
177   void BlockWorkerThread() {
178     worker_thread_->message_loop()->PostTask(
179         FROM_HERE,
180         base::Bind(&ProcessSingletonLinuxTest::BlockThread,
181                    base::Unretained(this)));
182   }
183
184   void UnblockWorkerThread() {
185     wait_event_.Signal();  // Unblock the worker thread for shutdown.
186     signal_event_.Wait();  // Ensure thread unblocks before continuing.
187   }
188
189   void BlockThread() {
190     wait_event_.Wait();
191     signal_event_.Signal();
192   }
193
194   base::FilePath lock_path_;
195   base::FilePath socket_path_;
196   base::FilePath cookie_path_;
197   int kill_callbacks_;
198
199  private:
200   void CreateProcessSingletonInternal() {
201     ASSERT_TRUE(!process_singleton_on_thread_);
202     process_singleton_on_thread_ = CreateProcessSingleton();
203     ASSERT_EQ(ProcessSingleton::PROCESS_NONE,
204               process_singleton_on_thread_->NotifyOtherProcessOrCreate());
205   }
206
207   void DestructProcessSingleton() {
208     ASSERT_TRUE(process_singleton_on_thread_);
209     delete process_singleton_on_thread_;
210   }
211
212   void KillCallback(int pid) {
213     kill_callbacks_++;
214   }
215
216   content::TestBrowserThread io_thread_;
217   base::ScopedTempDir temp_dir_;
218   base::WaitableEvent wait_event_;
219   base::WaitableEvent signal_event_;
220
221   scoped_ptr<base::Thread> worker_thread_;
222   TestableProcessSingleton* process_singleton_on_thread_;
223 };
224
225 }  // namespace
226
227 // Test if the socket file and symbol link created by ProcessSingletonLinux
228 // are valid.
229 // If this test flakes, use http://crbug.com/74554.
230 TEST_F(ProcessSingletonLinuxTest, CheckSocketFile) {
231   CreateProcessSingletonOnThread();
232   struct stat statbuf;
233   ASSERT_EQ(0, lstat(lock_path_.value().c_str(), &statbuf));
234   ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
235   char buf[PATH_MAX];
236   ssize_t len = readlink(lock_path_.value().c_str(), buf, PATH_MAX);
237   ASSERT_GT(len, 0);
238
239   ASSERT_EQ(0, lstat(socket_path_.value().c_str(), &statbuf));
240   ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
241
242   len = readlink(socket_path_.value().c_str(), buf, PATH_MAX);
243   ASSERT_GT(len, 0);
244   base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
245
246   ASSERT_EQ(0, lstat(socket_target_path.value().c_str(), &statbuf));
247   ASSERT_TRUE(S_ISSOCK(statbuf.st_mode));
248
249   len = readlink(cookie_path_.value().c_str(), buf, PATH_MAX);
250   ASSERT_GT(len, 0);
251   std::string cookie(buf, len);
252
253   base::FilePath remote_cookie_path = socket_target_path.DirName().
254       Append(chrome::kSingletonCookieFilename);
255   len = readlink(remote_cookie_path.value().c_str(), buf, PATH_MAX);
256   ASSERT_GT(len, 0);
257   EXPECT_EQ(cookie, std::string(buf, len));
258 }
259
260 // TODO(james.su@gmail.com): port following tests to Windows.
261 // Test success case of NotifyOtherProcess().
262 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessSuccess) {
263   CreateProcessSingletonOnThread();
264   EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED,
265             NotifyOtherProcess(true, TestTimeouts::action_timeout()));
266   CheckNotified();
267 }
268
269 // Test failure case of NotifyOtherProcess().
270 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessFailure) {
271   CreateProcessSingletonOnThread();
272
273   BlockWorkerThread();
274   EXPECT_EQ(ProcessSingleton::PROCESS_NONE,
275             NotifyOtherProcess(true, TestTimeouts::action_timeout()));
276
277   ASSERT_EQ(1, kill_callbacks_);
278   UnblockWorkerThread();
279 }
280
281 // Test that we don't kill ourselves by accident if a lockfile with the same pid
282 // happens to exist.
283 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessNoSuicide) {
284   CreateProcessSingletonOnThread();
285   // Replace lockfile with one containing our own pid.
286   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
287   std::string symlink_content = base::StringPrintf(
288       "%s%c%u",
289       net::GetHostName().c_str(),
290       '-',
291       base::GetCurrentProcId());
292   EXPECT_EQ(0, symlink(symlink_content.c_str(), lock_path_.value().c_str()));
293
294   // Remove socket so that we will not be able to notify the existing browser.
295   EXPECT_EQ(0, unlink(socket_path_.value().c_str()));
296
297   EXPECT_EQ(ProcessSingleton::PROCESS_NONE,
298             NotifyOtherProcess(false, TestTimeouts::action_timeout()));
299   // If we've gotten to this point without killing ourself, the test succeeded.
300 }
301
302 // Test that we can still notify a process on the same host even after the
303 // hostname changed.
304 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessHostChanged) {
305   CreateProcessSingletonOnThread();
306   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
307   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
308
309   EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED,
310             NotifyOtherProcess(false, TestTimeouts::action_timeout()));
311   CheckNotified();
312 }
313
314 // Test that we fail when lock says process is on another host and we can't
315 // notify it over the socket.
316 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessDifferingHost) {
317   CreateProcessSingletonOnThread();
318
319   BlockWorkerThread();
320
321   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
322   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
323
324   EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
325             NotifyOtherProcess(false, TestTimeouts::action_timeout()));
326
327   ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
328
329   UnblockWorkerThread();
330 }
331
332 // Test that we fail when lock says process is on another host and we can't
333 // notify it over the socket.
334 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessOrCreate_DifferingHost) {
335   CreateProcessSingletonOnThread();
336
337   BlockWorkerThread();
338
339   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
340   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
341
342   std::string url("about:blank");
343   EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
344             NotifyOtherProcessOrCreate(url, TestTimeouts::action_timeout()));
345
346   ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
347
348   UnblockWorkerThread();
349 }
350
351 // Test that Create fails when another browser is using the profile directory.
352 TEST_F(ProcessSingletonLinuxTest, CreateFailsWithExistingBrowser) {
353   CreateProcessSingletonOnThread();
354
355   scoped_ptr<TestableProcessSingleton> process_singleton(
356       CreateProcessSingleton());
357   process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
358   EXPECT_FALSE(process_singleton->Create());
359 }
360
361 // Test that Create fails when another browser is using the profile directory
362 // but with the old socket location.
363 TEST_F(ProcessSingletonLinuxTest, CreateChecksCompatibilitySocket) {
364   CreateProcessSingletonOnThread();
365   scoped_ptr<TestableProcessSingleton> process_singleton(
366       CreateProcessSingleton());
367   process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
368
369   // Do some surgery so as to look like the old configuration.
370   char buf[PATH_MAX];
371   ssize_t len = readlink(socket_path_.value().c_str(), buf, sizeof(buf));
372   ASSERT_GT(len, 0);
373   base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
374   ASSERT_EQ(0, unlink(socket_path_.value().c_str()));
375   ASSERT_EQ(0, rename(socket_target_path.value().c_str(),
376                       socket_path_.value().c_str()));
377   ASSERT_EQ(0, unlink(cookie_path_.value().c_str()));
378
379   EXPECT_FALSE(process_singleton->Create());
380 }
381
382 // Test that we fail when lock says process is on another host and we can't
383 // notify it over the socket before of a bad cookie.
384 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessOrCreate_BadCookie) {
385   CreateProcessSingletonOnThread();
386   // Change the cookie.
387   EXPECT_EQ(0, unlink(cookie_path_.value().c_str()));
388   EXPECT_EQ(0, symlink("INCORRECTCOOKIE", cookie_path_.value().c_str()));
389
390   // Also change the hostname, so the remote does not retry.
391   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
392   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
393
394   std::string url("about:blank");
395   EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
396             NotifyOtherProcessOrCreate(url, TestTimeouts::action_timeout()));
397 }
398