Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / chrome / browser / process_singleton_posix_unittest.cc
blob0558d0a21f7d96edd877fb728c3d3302651d587d
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.
5 #include "chrome/browser/process_singleton.h"
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>
14 #include <string>
15 #include <vector>
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"
34 using content::BrowserThread;
36 namespace {
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))) {}
50 std::vector<CommandLine::StringVector> callback_command_lines_;
52 using ProcessSingleton::NotifyOtherProcessWithTimeout;
53 using ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate;
54 using ProcessSingleton::OverrideCurrentPidForTesting;
55 using ProcessSingleton::OverrideKillCallbackForTesting;
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;
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();
74 virtual void SetUp() {
75 testing::Test::SetUp();
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_));
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);
92 virtual void TearDown() {
93 scoped_refptr<base::ThreadTestHelper> io_helper(new base::ThreadTestHelper(
94 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get()));
95 ASSERT_TRUE(io_helper->Run());
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)));
105 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
106 worker_thread_->message_loop_proxy().get()));
107 ASSERT_TRUE(helper->Run());
110 io_thread_.Stop();
111 testing::Test::TearDown();
114 void CreateProcessSingletonOnThread() {
115 ASSERT_EQ(NULL, worker_thread_.get());
116 worker_thread_.reset(new base::Thread("BlockingThread"));
117 worker_thread_->Start();
119 worker_thread_->message_loop()->PostTask(
120 FROM_HERE,
121 base::Bind(&ProcessSingletonPosixTest::
122 CreateProcessSingletonInternal,
123 base::Unretained(this)));
125 scoped_refptr<base::ThreadTestHelper> helper(
126 new base::ThreadTestHelper(worker_thread_->message_loop_proxy().get()));
127 ASSERT_TRUE(helper->Run());
130 TestableProcessSingleton* CreateProcessSingleton() {
131 return new TestableProcessSingleton(user_data_path_);
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);
142 ASSERT_EQ(0, lstat(socket_path_.value().c_str(), &statbuf));
143 ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
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));
149 ASSERT_EQ(0, lstat(socket_target_path.value().c_str(), &statbuf));
150 ASSERT_TRUE(S_ISSOCK(statbuf.st_mode));
152 len = readlink(cookie_path_.value().c_str(), buf, PATH_MAX);
153 ASSERT_GT(len, 0);
154 std::string cookie(buf, len);
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));
163 ProcessSingleton::NotifyResult NotifyOtherProcess(
164 bool override_kill,
165 base::TimeDelta timeout) {
166 scoped_ptr<TestableProcessSingleton> process_singleton(
167 CreateProcessSingleton());
168 CommandLine command_line(CommandLine::ForCurrentProcess()->GetProgram());
169 command_line.AppendArg("about:blank");
170 if (override_kill) {
171 process_singleton->OverrideCurrentPidForTesting(
172 base::GetCurrentProcId() + 1);
173 process_singleton->OverrideKillCallbackForTesting(
174 base::Bind(&ProcessSingletonPosixTest::KillCallback,
175 base::Unretained(this)));
178 return process_singleton->NotifyOtherProcessWithTimeout(
179 command_line, timeout.InSeconds(), true);
182 // A helper method to call ProcessSingleton::NotifyOtherProcessOrCreate().
183 ProcessSingleton::NotifyResult NotifyOtherProcessOrCreate(
184 const std::string& url,
185 base::TimeDelta timeout) {
186 scoped_ptr<TestableProcessSingleton> process_singleton(
187 CreateProcessSingleton());
188 CommandLine command_line(CommandLine::ForCurrentProcess()->GetProgram());
189 command_line.AppendArg(url);
190 return process_singleton->NotifyOtherProcessWithTimeoutOrCreate(
191 command_line, timeout.InSeconds());
194 void CheckNotified() {
195 ASSERT_TRUE(process_singleton_on_thread_ != NULL);
196 ASSERT_EQ(1u, process_singleton_on_thread_->callback_command_lines_.size());
197 bool found = false;
198 for (size_t i = 0;
199 i < process_singleton_on_thread_->callback_command_lines_[0].size();
200 ++i) {
201 if (process_singleton_on_thread_->callback_command_lines_[0][i] ==
202 "about:blank") {
203 found = true;
204 break;
207 ASSERT_TRUE(found);
208 ASSERT_EQ(0, kill_callbacks_);
211 void BlockWorkerThread() {
212 worker_thread_->message_loop()->PostTask(
213 FROM_HERE,
214 base::Bind(&ProcessSingletonPosixTest::BlockThread,
215 base::Unretained(this)));
218 void UnblockWorkerThread() {
219 wait_event_.Signal(); // Unblock the worker thread for shutdown.
220 signal_event_.Wait(); // Ensure thread unblocks before continuing.
223 void BlockThread() {
224 wait_event_.Wait();
225 signal_event_.Signal();
228 base::FilePath user_data_path_;
229 base::FilePath lock_path_;
230 base::FilePath socket_path_;
231 base::FilePath cookie_path_;
232 int kill_callbacks_;
234 private:
235 void CreateProcessSingletonInternal() {
236 ASSERT_TRUE(!process_singleton_on_thread_);
237 process_singleton_on_thread_ = CreateProcessSingleton();
238 ASSERT_EQ(ProcessSingleton::PROCESS_NONE,
239 process_singleton_on_thread_->NotifyOtherProcessOrCreate());
242 void DestructProcessSingleton() {
243 ASSERT_TRUE(process_singleton_on_thread_);
244 delete process_singleton_on_thread_;
247 void KillCallback(int pid) {
248 kill_callbacks_++;
251 content::TestBrowserThread io_thread_;
252 base::ScopedTempDir temp_dir_;
253 base::WaitableEvent wait_event_;
254 base::WaitableEvent signal_event_;
256 scoped_ptr<base::Thread> worker_thread_;
257 TestableProcessSingleton* process_singleton_on_thread_;
260 } // namespace
262 // Test if the socket file and symbol link created by ProcessSingletonPosix
263 // are valid.
264 // If this test flakes, use http://crbug.com/74554.
265 TEST_F(ProcessSingletonPosixTest, CheckSocketFile) {
266 CreateProcessSingletonOnThread();
267 VerifyFiles();
270 // TODO(james.su@gmail.com): port following tests to Windows.
271 // Test success case of NotifyOtherProcess().
272 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessSuccess) {
273 CreateProcessSingletonOnThread();
274 EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED,
275 NotifyOtherProcess(true, TestTimeouts::action_timeout()));
276 CheckNotified();
279 // Test failure case of NotifyOtherProcess().
280 // Disabled, http://crbug.com/407065 .
281 TEST_F(ProcessSingletonPosixTest, DISABLED_NotifyOtherProcessFailure) {
282 CreateProcessSingletonOnThread();
284 BlockWorkerThread();
285 EXPECT_EQ(ProcessSingleton::PROCESS_NONE,
286 NotifyOtherProcess(true, TestTimeouts::action_timeout()));
288 ASSERT_EQ(1, kill_callbacks_);
289 UnblockWorkerThread();
292 // Test that we don't kill ourselves by accident if a lockfile with the same pid
293 // happens to exist.
294 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessNoSuicide) {
295 CreateProcessSingletonOnThread();
296 // Replace lockfile with one containing our own pid.
297 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
298 std::string symlink_content = base::StringPrintf(
299 "%s%c%u",
300 net::GetHostName().c_str(),
301 '-',
302 base::GetCurrentProcId());
303 EXPECT_EQ(0, symlink(symlink_content.c_str(), lock_path_.value().c_str()));
305 // Remove socket so that we will not be able to notify the existing browser.
306 EXPECT_EQ(0, unlink(socket_path_.value().c_str()));
308 EXPECT_EQ(ProcessSingleton::PROCESS_NONE,
309 NotifyOtherProcess(false, TestTimeouts::action_timeout()));
310 // If we've gotten to this point without killing ourself, the test succeeded.
313 // Test that we can still notify a process on the same host even after the
314 // hostname changed.
315 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessHostChanged) {
316 CreateProcessSingletonOnThread();
317 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
318 EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
320 EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED,
321 NotifyOtherProcess(false, TestTimeouts::action_timeout()));
322 CheckNotified();
325 // Test that we fail when lock says process is on another host and we can't
326 // notify it over the socket.
327 // Disabled, http://crbug.com/407065 .
328 TEST_F(ProcessSingletonPosixTest, DISABLED_NotifyOtherProcessDifferingHost) {
329 CreateProcessSingletonOnThread();
331 BlockWorkerThread();
333 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
334 EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
336 EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
337 NotifyOtherProcess(false, TestTimeouts::action_timeout()));
339 ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
341 UnblockWorkerThread();
344 // Test that we fail when lock says process is on another host and we can't
345 // notify it over the socket.
346 // Disabled, http://crbug.com/407065 .
347 TEST_F(ProcessSingletonPosixTest,
348 DISABLED_NotifyOtherProcessOrCreate_DifferingHost) {
349 CreateProcessSingletonOnThread();
351 BlockWorkerThread();
353 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
354 EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
356 std::string url("about:blank");
357 EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
358 NotifyOtherProcessOrCreate(url, TestTimeouts::action_timeout()));
360 ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
362 UnblockWorkerThread();
365 // Test that Create fails when another browser is using the profile directory.
366 TEST_F(ProcessSingletonPosixTest, CreateFailsWithExistingBrowser) {
367 CreateProcessSingletonOnThread();
369 scoped_ptr<TestableProcessSingleton> process_singleton(
370 CreateProcessSingleton());
371 process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
372 EXPECT_FALSE(process_singleton->Create());
375 // Test that Create fails when another browser is using the profile directory
376 // but with the old socket location.
377 TEST_F(ProcessSingletonPosixTest, CreateChecksCompatibilitySocket) {
378 CreateProcessSingletonOnThread();
379 scoped_ptr<TestableProcessSingleton> process_singleton(
380 CreateProcessSingleton());
381 process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
383 // Do some surgery so as to look like the old configuration.
384 char buf[PATH_MAX];
385 ssize_t len = readlink(socket_path_.value().c_str(), buf, sizeof(buf));
386 ASSERT_GT(len, 0);
387 base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
388 ASSERT_EQ(0, unlink(socket_path_.value().c_str()));
389 ASSERT_EQ(0, rename(socket_target_path.value().c_str(),
390 socket_path_.value().c_str()));
391 ASSERT_EQ(0, unlink(cookie_path_.value().c_str()));
393 EXPECT_FALSE(process_singleton->Create());
396 // Test that we fail when lock says process is on another host and we can't
397 // notify it over the socket before of a bad cookie.
398 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessOrCreate_BadCookie) {
399 CreateProcessSingletonOnThread();
400 // Change the cookie.
401 EXPECT_EQ(0, unlink(cookie_path_.value().c_str()));
402 EXPECT_EQ(0, symlink("INCORRECTCOOKIE", cookie_path_.value().c_str()));
404 // Also change the hostname, so the remote does not retry.
405 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
406 EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
408 std::string url("about:blank");
409 EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
410 NotifyOtherProcessOrCreate(url, TestTimeouts::action_timeout()));
413 #if defined(OS_MACOSX)
414 // Test that if there is an existing lock file, and we could not flock()
415 // it, then exit.
416 TEST_F(ProcessSingletonPosixTest, CreateRespectsOldMacLock) {
417 scoped_ptr<TestableProcessSingleton> process_singleton(
418 CreateProcessSingleton());
419 base::ScopedFD lock_fd(HANDLE_EINTR(
420 open(lock_path_.value().c_str(), O_RDWR | O_CREAT | O_EXLOCK, 0644)));
421 ASSERT_TRUE(lock_fd.is_valid());
422 EXPECT_FALSE(process_singleton->Create());
423 base::File::Info info;
424 EXPECT_TRUE(base::GetFileInfo(lock_path_, &info));
425 EXPECT_FALSE(info.is_directory);
426 EXPECT_FALSE(info.is_symbolic_link);
429 // Test that if there is an existing lock file, and it's not locked, we replace
430 // it.
431 TEST_F(ProcessSingletonPosixTest, CreateReplacesOldMacLock) {
432 scoped_ptr<TestableProcessSingleton> process_singleton(
433 CreateProcessSingleton());
434 EXPECT_EQ(0, base::WriteFile(lock_path_, "", 0));
435 EXPECT_TRUE(process_singleton->Create());
436 VerifyFiles();
438 #endif // defined(OS_MACOSX)