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