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