Ensure tests have an active task runner
[chromium-blink-merge.git] / chrome / browser / process_singleton_posix_unittest.cc
blobb62e861b18186fce41837b8e640717b0e0e6414f
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))) {}
49 std::vector<base::CommandLine::StringVector> callback_command_lines_;
51 using ProcessSingleton::NotifyOtherProcessWithTimeout;
52 using ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate;
53 using ProcessSingleton::OverrideCurrentPidForTesting;
54 using ProcessSingleton::OverrideKillCallbackForTesting;
56 private:
57 bool NotificationCallback(const base::CommandLine& command_line,
58 const base::FilePath& current_directory) {
59 callback_command_lines_.push_back(command_line.argv());
60 return true;
64 ProcessSingletonPosixTest()
65 : kill_callbacks_(0),
66 io_thread_(BrowserThread::IO),
67 wait_event_(true, false),
68 signal_event_(true, false),
69 process_singleton_on_thread_(NULL) {
70 io_thread_.StartIOThread();
73 void SetUp() override {
74 testing::Test::SetUp();
76 ProcessSingleton::DisablePromptForTesting();
77 // Put the lock in a temporary directory. Doesn't need to be a
78 // full profile to test this code.
79 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
80 // Use a long directory name to ensure that the socket isn't opened through
81 // the symlink.
82 user_data_path_ = temp_dir_.path().Append(
83 std::string(sizeof(sockaddr_un::sun_path), 'a'));
84 ASSERT_TRUE(CreateDirectory(user_data_path_));
86 lock_path_ = user_data_path_.Append(chrome::kSingletonLockFilename);
87 socket_path_ = user_data_path_.Append(chrome::kSingletonSocketFilename);
88 cookie_path_ = user_data_path_.Append(chrome::kSingletonCookieFilename);
91 void TearDown() override {
92 scoped_refptr<base::ThreadTestHelper> io_helper(new base::ThreadTestHelper(
93 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get()));
94 ASSERT_TRUE(io_helper->Run());
96 // Destruct the ProcessSingleton object before the IO thread so that its
97 // internals are destructed properly.
98 if (process_singleton_on_thread_) {
99 worker_thread_->message_loop()->PostTask(
100 FROM_HERE,
101 base::Bind(&ProcessSingletonPosixTest::DestructProcessSingleton,
102 base::Unretained(this)));
104 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
105 worker_thread_->message_loop_proxy().get()));
106 ASSERT_TRUE(helper->Run());
109 io_thread_.Stop();
110 testing::Test::TearDown();
113 void CreateProcessSingletonOnThread() {
114 ASSERT_EQ(NULL, worker_thread_.get());
115 worker_thread_.reset(new base::Thread("BlockingThread"));
116 worker_thread_->Start();
118 worker_thread_->message_loop()->PostTask(
119 FROM_HERE,
120 base::Bind(&ProcessSingletonPosixTest::
121 CreateProcessSingletonInternal,
122 base::Unretained(this)));
124 scoped_refptr<base::ThreadTestHelper> helper(
125 new base::ThreadTestHelper(worker_thread_->message_loop_proxy().get()));
126 ASSERT_TRUE(helper->Run());
129 TestableProcessSingleton* CreateProcessSingleton() {
130 return new TestableProcessSingleton(user_data_path_);
133 void VerifyFiles() {
134 struct stat statbuf;
135 ASSERT_EQ(0, lstat(lock_path_.value().c_str(), &statbuf));
136 ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
137 char buf[PATH_MAX];
138 ssize_t len = readlink(lock_path_.value().c_str(), buf, PATH_MAX);
139 ASSERT_GT(len, 0);
141 ASSERT_EQ(0, lstat(socket_path_.value().c_str(), &statbuf));
142 ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
144 len = readlink(socket_path_.value().c_str(), buf, PATH_MAX);
145 ASSERT_GT(len, 0);
146 base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
148 ASSERT_EQ(0, lstat(socket_target_path.value().c_str(), &statbuf));
149 ASSERT_TRUE(S_ISSOCK(statbuf.st_mode));
151 len = readlink(cookie_path_.value().c_str(), buf, PATH_MAX);
152 ASSERT_GT(len, 0);
153 std::string cookie(buf, len);
155 base::FilePath remote_cookie_path = socket_target_path.DirName().
156 Append(chrome::kSingletonCookieFilename);
157 len = readlink(remote_cookie_path.value().c_str(), buf, PATH_MAX);
158 ASSERT_GT(len, 0);
159 EXPECT_EQ(cookie, std::string(buf, len));
162 ProcessSingleton::NotifyResult NotifyOtherProcess(bool override_kill) {
163 scoped_ptr<TestableProcessSingleton> process_singleton(
164 CreateProcessSingleton());
165 base::CommandLine command_line(
166 base::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)));
176 return process_singleton->NotifyOtherProcessWithTimeout(
177 command_line, kRetryAttempts, timeout(), true);
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 base::CommandLine command_line(
186 base::CommandLine::ForCurrentProcess()->GetProgram());
187 command_line.AppendArg(url);
188 return process_singleton->NotifyOtherProcessWithTimeoutOrCreate(
189 command_line, kRetryAttempts, timeout());
192 void CheckNotified() {
193 ASSERT_TRUE(process_singleton_on_thread_ != NULL);
194 ASSERT_EQ(1u, process_singleton_on_thread_->callback_command_lines_.size());
195 bool found = false;
196 for (size_t i = 0;
197 i < process_singleton_on_thread_->callback_command_lines_[0].size();
198 ++i) {
199 if (process_singleton_on_thread_->callback_command_lines_[0][i] ==
200 "about:blank") {
201 found = true;
202 break;
205 ASSERT_TRUE(found);
206 ASSERT_EQ(0, kill_callbacks_);
209 void BlockWorkerThread() {
210 worker_thread_->message_loop()->PostTask(
211 FROM_HERE,
212 base::Bind(&ProcessSingletonPosixTest::BlockThread,
213 base::Unretained(this)));
216 void UnblockWorkerThread() {
217 wait_event_.Signal(); // Unblock the worker thread for shutdown.
218 signal_event_.Wait(); // Ensure thread unblocks before continuing.
221 void BlockThread() {
222 wait_event_.Wait();
223 signal_event_.Signal();
226 base::FilePath user_data_path_;
227 base::FilePath lock_path_;
228 base::FilePath socket_path_;
229 base::FilePath cookie_path_;
230 int kill_callbacks_;
232 private:
233 static const int kRetryAttempts = 2;
235 base::TimeDelta timeout() const {
236 return TestTimeouts::tiny_timeout() * kRetryAttempts;
239 void CreateProcessSingletonInternal() {
240 ASSERT_TRUE(!process_singleton_on_thread_);
241 process_singleton_on_thread_ = CreateProcessSingleton();
242 ASSERT_EQ(ProcessSingleton::PROCESS_NONE,
243 process_singleton_on_thread_->NotifyOtherProcessOrCreate());
246 void DestructProcessSingleton() {
247 ASSERT_TRUE(process_singleton_on_thread_);
248 delete process_singleton_on_thread_;
251 void KillCallback(int pid) {
252 kill_callbacks_++;
255 base::MessageLoop message_loop_;
256 content::TestBrowserThread io_thread_;
257 base::ScopedTempDir temp_dir_;
258 base::WaitableEvent wait_event_;
259 base::WaitableEvent signal_event_;
261 scoped_ptr<base::Thread> worker_thread_;
262 TestableProcessSingleton* process_singleton_on_thread_;
265 } // namespace
267 // Test if the socket file and symbol link created by ProcessSingletonPosix
268 // are valid.
269 // If this test flakes, use http://crbug.com/74554.
270 TEST_F(ProcessSingletonPosixTest, CheckSocketFile) {
271 CreateProcessSingletonOnThread();
272 VerifyFiles();
275 // TODO(james.su@gmail.com): port following tests to Windows.
276 // Test success case of NotifyOtherProcess().
277 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessSuccess) {
278 CreateProcessSingletonOnThread();
279 EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED, NotifyOtherProcess(true));
280 CheckNotified();
283 // Test failure case of NotifyOtherProcess().
284 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessFailure) {
285 CreateProcessSingletonOnThread();
287 BlockWorkerThread();
288 EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcess(true));
289 ASSERT_EQ(1, kill_callbacks_);
290 UnblockWorkerThread();
293 // Test that we don't kill ourselves by accident if a lockfile with the same pid
294 // happens to exist.
295 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessNoSuicide) {
296 CreateProcessSingletonOnThread();
297 // Replace lockfile with one containing our own pid.
298 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
299 std::string symlink_content = base::StringPrintf(
300 "%s%c%u",
301 net::GetHostName().c_str(),
302 '-',
303 base::GetCurrentProcId());
304 EXPECT_EQ(0, symlink(symlink_content.c_str(), lock_path_.value().c_str()));
306 // Remove socket so that we will not be able to notify the existing browser.
307 EXPECT_EQ(0, unlink(socket_path_.value().c_str()));
309 EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcess(false));
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, NotifyOtherProcess(false));
321 CheckNotified();
324 // Test that we fail when lock says process is on another host and we can't
325 // notify it over the socket.
326 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessDifferingHost) {
327 CreateProcessSingletonOnThread();
329 BlockWorkerThread();
331 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
332 EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
334 EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE, NotifyOtherProcess(false));
336 ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
338 UnblockWorkerThread();
341 // Test that we fail when lock says process is on another host and we can't
342 // notify it over the socket.
343 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessOrCreate_DifferingHost) {
344 CreateProcessSingletonOnThread();
346 BlockWorkerThread();
348 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
349 EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
351 std::string url("about:blank");
352 EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE, NotifyOtherProcessOrCreate(url));
354 ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
356 UnblockWorkerThread();
359 // Test that Create fails when another browser is using the profile directory.
360 TEST_F(ProcessSingletonPosixTest, CreateFailsWithExistingBrowser) {
361 CreateProcessSingletonOnThread();
363 scoped_ptr<TestableProcessSingleton> process_singleton(
364 CreateProcessSingleton());
365 process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
366 EXPECT_FALSE(process_singleton->Create());
369 // Test that Create fails when another browser is using the profile directory
370 // but with the old socket location.
371 TEST_F(ProcessSingletonPosixTest, CreateChecksCompatibilitySocket) {
372 CreateProcessSingletonOnThread();
373 scoped_ptr<TestableProcessSingleton> process_singleton(
374 CreateProcessSingleton());
375 process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
377 // Do some surgery so as to look like the old configuration.
378 char buf[PATH_MAX];
379 ssize_t len = readlink(socket_path_.value().c_str(), buf, sizeof(buf));
380 ASSERT_GT(len, 0);
381 base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
382 ASSERT_EQ(0, unlink(socket_path_.value().c_str()));
383 ASSERT_EQ(0, rename(socket_target_path.value().c_str(),
384 socket_path_.value().c_str()));
385 ASSERT_EQ(0, unlink(cookie_path_.value().c_str()));
387 EXPECT_FALSE(process_singleton->Create());
390 // Test that we fail when lock says process is on another host and we can't
391 // notify it over the socket before of a bad cookie.
392 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessOrCreate_BadCookie) {
393 CreateProcessSingletonOnThread();
394 // Change the cookie.
395 EXPECT_EQ(0, unlink(cookie_path_.value().c_str()));
396 EXPECT_EQ(0, symlink("INCORRECTCOOKIE", cookie_path_.value().c_str()));
398 // Also change the hostname, so the remote does not retry.
399 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
400 EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
402 std::string url("about:blank");
403 EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE, NotifyOtherProcessOrCreate(url));
406 #if defined(OS_MACOSX)
407 // Test that if there is an existing lock file, and we could not flock()
408 // it, then exit.
409 TEST_F(ProcessSingletonPosixTest, CreateRespectsOldMacLock) {
410 scoped_ptr<TestableProcessSingleton> process_singleton(
411 CreateProcessSingleton());
412 base::ScopedFD lock_fd(HANDLE_EINTR(
413 open(lock_path_.value().c_str(), O_RDWR | O_CREAT | O_EXLOCK, 0644)));
414 ASSERT_TRUE(lock_fd.is_valid());
415 EXPECT_FALSE(process_singleton->Create());
416 base::File::Info info;
417 EXPECT_TRUE(base::GetFileInfo(lock_path_, &info));
418 EXPECT_FALSE(info.is_directory);
419 EXPECT_FALSE(info.is_symbolic_link);
422 // Test that if there is an existing lock file, and it's not locked, we replace
423 // it.
424 TEST_F(ProcessSingletonPosixTest, CreateReplacesOldMacLock) {
425 scoped_ptr<TestableProcessSingleton> process_singleton(
426 CreateProcessSingleton());
427 EXPECT_EQ(0, base::WriteFile(lock_path_, "", 0));
428 EXPECT_TRUE(process_singleton->Create());
429 VerifyFiles();
431 #endif // defined(OS_MACOSX)