Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / process_singleton_linux_unittest.cc
blob689a2dfaf1e0c0e07d07f8c6dc564c3fa191cccf
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.
5 #include "chrome/browser/process_singleton.h"
7 #include <signal.h>
8 #include <sys/types.h>
9 #include <sys/wait.h>
10 #include <unistd.h>
12 #include <string>
13 #include <vector>
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"
30 using content::BrowserThread;
32 namespace {
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))) {}
46 std::vector<CommandLine::StringVector> callback_command_lines_;
48 using ProcessSingleton::NotifyOtherProcessWithTimeout;
49 using ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate;
50 using ProcessSingleton::OverrideCurrentPidForTesting;
51 using ProcessSingleton::OverrideKillCallbackForTesting;
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;
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();
70 virtual void SetUp() {
71 testing::Test::SetUp();
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);
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());
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)));
95 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
96 worker_thread_->message_loop_proxy().get()));
97 ASSERT_TRUE(helper->Run());
100 io_thread_.Stop();
101 testing::Test::TearDown();
104 void CreateProcessSingletonOnThread() {
105 ASSERT_EQ(NULL, worker_thread_.get());
106 worker_thread_.reset(new base::Thread("BlockingThread"));
107 worker_thread_->Start();
109 worker_thread_->message_loop()->PostTask(
110 FROM_HERE,
111 base::Bind(&ProcessSingletonLinuxTest::
112 CreateProcessSingletonInternal,
113 base::Unretained(this)));
115 scoped_refptr<base::ThreadTestHelper> helper(
116 new base::ThreadTestHelper(worker_thread_->message_loop_proxy().get()));
117 ASSERT_TRUE(helper->Run());
120 TestableProcessSingleton* CreateProcessSingleton() {
121 return new TestableProcessSingleton(temp_dir_.path());
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)));
139 return process_singleton->NotifyOtherProcessWithTimeout(
140 command_line, timeout.InSeconds(), true);
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());
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;
168 ASSERT_TRUE(found);
169 ASSERT_EQ(0, kill_callbacks_);
172 void BlockWorkerThread() {
173 worker_thread_->message_loop()->PostTask(
174 FROM_HERE,
175 base::Bind(&ProcessSingletonLinuxTest::BlockThread,
176 base::Unretained(this)));
179 void UnblockWorkerThread() {
180 wait_event_.Signal(); // Unblock the worker thread for shutdown.
181 signal_event_.Wait(); // Ensure thread unblocks before continuing.
184 void BlockThread() {
185 wait_event_.Wait();
186 signal_event_.Signal();
189 base::FilePath lock_path_;
190 base::FilePath socket_path_;
191 base::FilePath cookie_path_;
192 int kill_callbacks_;
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());
202 void DestructProcessSingleton() {
203 ASSERT_TRUE(process_singleton_on_thread_);
204 delete process_singleton_on_thread_;
207 void KillCallback(int pid) {
208 kill_callbacks_++;
211 content::TestBrowserThread io_thread_;
212 base::ScopedTempDir temp_dir_;
213 base::WaitableEvent wait_event_;
214 base::WaitableEvent signal_event_;
216 scoped_ptr<base::Thread> worker_thread_;
217 TestableProcessSingleton* process_singleton_on_thread_;
220 } // namespace
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);
234 ASSERT_EQ(0, lstat(socket_path_.value().c_str(), &statbuf));
235 ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
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));
241 ASSERT_EQ(0, lstat(socket_target_path.value().c_str(), &statbuf));
242 ASSERT_TRUE(S_ISSOCK(statbuf.st_mode));
244 len = readlink(cookie_path_.value().c_str(), buf, PATH_MAX);
245 ASSERT_GT(len, 0);
246 std::string cookie(buf, len);
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));
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();
264 // Test failure case of NotifyOtherProcess().
265 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessFailure) {
266 CreateProcessSingletonOnThread();
268 BlockWorkerThread();
269 EXPECT_EQ(ProcessSingleton::PROCESS_NONE,
270 NotifyOtherProcess(true, TestTimeouts::action_timeout()));
272 ASSERT_EQ(1, kill_callbacks_);
273 UnblockWorkerThread();
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()));
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()));
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.
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()));
304 EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED,
305 NotifyOtherProcess(false, TestTimeouts::action_timeout()));
306 CheckNotified();
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();
314 BlockWorkerThread();
316 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
317 EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
319 EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
320 NotifyOtherProcess(false, TestTimeouts::action_timeout()));
322 ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
324 UnblockWorkerThread();
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();
332 BlockWorkerThread();
334 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
335 EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
337 std::string url("about:blank");
338 EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
339 NotifyOtherProcessOrCreate(url, TestTimeouts::action_timeout()));
341 ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
343 UnblockWorkerThread();
346 // Test that Create fails when another browser is using the profile directory.
347 TEST_F(ProcessSingletonLinuxTest, CreateFailsWithExistingBrowser) {
348 CreateProcessSingletonOnThread();
350 scoped_ptr<TestableProcessSingleton> process_singleton(
351 CreateProcessSingleton());
352 process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
353 EXPECT_FALSE(process_singleton->Create());
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);
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()));
374 EXPECT_FALSE(process_singleton->Create());
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()));
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()));
389 std::string url("about:blank");
390 EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
391 NotifyOtherProcessOrCreate(url, TestTimeouts::action_timeout()));