This source file includes following definitions.
- NotificationCallback
- process_singleton_on_thread_
- SetUp
- TearDown
- CreateProcessSingletonOnThread
- CreateProcessSingleton
- NotifyOtherProcess
- NotifyOtherProcessOrCreate
- CheckNotified
- BlockWorkerThread
- UnblockWorkerThread
- BlockThread
- CreateProcessSingletonInternal
- DestructProcessSingleton
- KillCallback
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
#include "chrome/browser/process_singleton.h"
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/test_timeouts.h"
#include "base/test/thread_test_helper.h"
#include "base/threading/thread.h"
#include "chrome/common/chrome_constants.h"
#include "content/public/test/test_browser_thread.h"
#include "net/base/net_util.h"
#include "testing/gtest/include/gtest/gtest.h"
using content::BrowserThread;
namespace {
class ProcessSingletonLinuxTest : public testing::Test {
public:
class TestableProcessSingleton : public ProcessSingleton {
public:
explicit TestableProcessSingleton(const base::FilePath& user_data_dir)
: ProcessSingleton(
user_data_dir,
base::Bind(&TestableProcessSingleton::NotificationCallback,
base::Unretained(this))) {}
std::vector<CommandLine::StringVector> callback_command_lines_;
using ProcessSingleton::NotifyOtherProcessWithTimeout;
using ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate;
using ProcessSingleton::OverrideCurrentPidForTesting;
using ProcessSingleton::OverrideKillCallbackForTesting;
private:
bool NotificationCallback(const CommandLine& command_line,
const base::FilePath& current_directory) {
callback_command_lines_.push_back(command_line.argv());
return true;
}
};
ProcessSingletonLinuxTest()
: kill_callbacks_(0),
io_thread_(BrowserThread::IO),
wait_event_(true, false),
signal_event_(true, false),
process_singleton_on_thread_(NULL) {
io_thread_.StartIOThread();
}
virtual void SetUp() {
testing::Test::SetUp();
ProcessSingleton::DisablePromptForTesting();
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
lock_path_ = temp_dir_.path().Append(chrome::kSingletonLockFilename);
socket_path_ = temp_dir_.path().Append(chrome::kSingletonSocketFilename);
cookie_path_ = temp_dir_.path().Append(chrome::kSingletonCookieFilename);
}
virtual void TearDown() {
scoped_refptr<base::ThreadTestHelper> io_helper(new base::ThreadTestHelper(
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get()));
ASSERT_TRUE(io_helper->Run());
if (process_singleton_on_thread_) {
worker_thread_->message_loop()->PostTask(
FROM_HERE,
base::Bind(&ProcessSingletonLinuxTest::DestructProcessSingleton,
base::Unretained(this)));
scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
worker_thread_->message_loop_proxy().get()));
ASSERT_TRUE(helper->Run());
}
io_thread_.Stop();
testing::Test::TearDown();
}
void CreateProcessSingletonOnThread() {
ASSERT_EQ(NULL, worker_thread_.get());
worker_thread_.reset(new base::Thread("BlockingThread"));
worker_thread_->Start();
worker_thread_->message_loop()->PostTask(
FROM_HERE,
base::Bind(&ProcessSingletonLinuxTest::
CreateProcessSingletonInternal,
base::Unretained(this)));
scoped_refptr<base::ThreadTestHelper> helper(
new base::ThreadTestHelper(worker_thread_->message_loop_proxy().get()));
ASSERT_TRUE(helper->Run());
}
TestableProcessSingleton* CreateProcessSingleton() {
return new TestableProcessSingleton(temp_dir_.path());
}
ProcessSingleton::NotifyResult NotifyOtherProcess(
bool override_kill,
base::TimeDelta timeout) {
scoped_ptr<TestableProcessSingleton> process_singleton(
CreateProcessSingleton());
CommandLine command_line(CommandLine::ForCurrentProcess()->GetProgram());
command_line.AppendArg("about:blank");
if (override_kill) {
process_singleton->OverrideCurrentPidForTesting(
base::GetCurrentProcId() + 1);
process_singleton->OverrideKillCallbackForTesting(
base::Bind(&ProcessSingletonLinuxTest::KillCallback,
base::Unretained(this)));
}
return process_singleton->NotifyOtherProcessWithTimeout(
command_line, timeout.InSeconds(), true);
}
ProcessSingleton::NotifyResult NotifyOtherProcessOrCreate(
const std::string& url,
base::TimeDelta timeout) {
scoped_ptr<TestableProcessSingleton> process_singleton(
CreateProcessSingleton());
CommandLine command_line(CommandLine::ForCurrentProcess()->GetProgram());
command_line.AppendArg(url);
return process_singleton->NotifyOtherProcessWithTimeoutOrCreate(
command_line, timeout.InSeconds());
}
void CheckNotified() {
ASSERT_TRUE(process_singleton_on_thread_ != NULL);
ASSERT_EQ(1u, process_singleton_on_thread_->callback_command_lines_.size());
bool found = false;
for (size_t i = 0;
i < process_singleton_on_thread_->callback_command_lines_[0].size();
++i) {
if (process_singleton_on_thread_->callback_command_lines_[0][i] ==
"about:blank") {
found = true;
break;
}
}
ASSERT_TRUE(found);
ASSERT_EQ(0, kill_callbacks_);
}
void BlockWorkerThread() {
worker_thread_->message_loop()->PostTask(
FROM_HERE,
base::Bind(&ProcessSingletonLinuxTest::BlockThread,
base::Unretained(this)));
}
void UnblockWorkerThread() {
wait_event_.Signal();
signal_event_.Wait();
}
void BlockThread() {
wait_event_.Wait();
signal_event_.Signal();
}
base::FilePath lock_path_;
base::FilePath socket_path_;
base::FilePath cookie_path_;
int kill_callbacks_;
private:
void CreateProcessSingletonInternal() {
ASSERT_TRUE(!process_singleton_on_thread_);
process_singleton_on_thread_ = CreateProcessSingleton();
ASSERT_EQ(ProcessSingleton::PROCESS_NONE,
process_singleton_on_thread_->NotifyOtherProcessOrCreate());
}
void DestructProcessSingleton() {
ASSERT_TRUE(process_singleton_on_thread_);
delete process_singleton_on_thread_;
}
void KillCallback(int pid) {
kill_callbacks_++;
}
content::TestBrowserThread io_thread_;
base::ScopedTempDir temp_dir_;
base::WaitableEvent wait_event_;
base::WaitableEvent signal_event_;
scoped_ptr<base::Thread> worker_thread_;
TestableProcessSingleton* process_singleton_on_thread_;
};
}
TEST_F(ProcessSingletonLinuxTest, CheckSocketFile) {
CreateProcessSingletonOnThread();
struct stat statbuf;
ASSERT_EQ(0, lstat(lock_path_.value().c_str(), &statbuf));
ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
char buf[PATH_MAX];
ssize_t len = readlink(lock_path_.value().c_str(), buf, PATH_MAX);
ASSERT_GT(len, 0);
ASSERT_EQ(0, lstat(socket_path_.value().c_str(), &statbuf));
ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
len = readlink(socket_path_.value().c_str(), buf, PATH_MAX);
ASSERT_GT(len, 0);
base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
ASSERT_EQ(0, lstat(socket_target_path.value().c_str(), &statbuf));
ASSERT_TRUE(S_ISSOCK(statbuf.st_mode));
len = readlink(cookie_path_.value().c_str(), buf, PATH_MAX);
ASSERT_GT(len, 0);
std::string cookie(buf, len);
base::FilePath remote_cookie_path = socket_target_path.DirName().
Append(chrome::kSingletonCookieFilename);
len = readlink(remote_cookie_path.value().c_str(), buf, PATH_MAX);
ASSERT_GT(len, 0);
EXPECT_EQ(cookie, std::string(buf, len));
}
TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessSuccess) {
CreateProcessSingletonOnThread();
EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED,
NotifyOtherProcess(true, TestTimeouts::action_timeout()));
CheckNotified();
}
TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessFailure) {
CreateProcessSingletonOnThread();
BlockWorkerThread();
EXPECT_EQ(ProcessSingleton::PROCESS_NONE,
NotifyOtherProcess(true, TestTimeouts::action_timeout()));
ASSERT_EQ(1, kill_callbacks_);
UnblockWorkerThread();
}
TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessNoSuicide) {
CreateProcessSingletonOnThread();
EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
std::string symlink_content = base::StringPrintf(
"%s%c%u",
net::GetHostName().c_str(),
'-',
base::GetCurrentProcId());
EXPECT_EQ(0, symlink(symlink_content.c_str(), lock_path_.value().c_str()));
EXPECT_EQ(0, unlink(socket_path_.value().c_str()));
EXPECT_EQ(ProcessSingleton::PROCESS_NONE,
NotifyOtherProcess(false, TestTimeouts::action_timeout()));
}
TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessHostChanged) {
CreateProcessSingletonOnThread();
EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED,
NotifyOtherProcess(false, TestTimeouts::action_timeout()));
CheckNotified();
}
TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessDifferingHost) {
CreateProcessSingletonOnThread();
BlockWorkerThread();
EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
NotifyOtherProcess(false, TestTimeouts::action_timeout()));
ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
UnblockWorkerThread();
}
TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessOrCreate_DifferingHost) {
CreateProcessSingletonOnThread();
BlockWorkerThread();
EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
std::string url("about:blank");
EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
NotifyOtherProcessOrCreate(url, TestTimeouts::action_timeout()));
ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
UnblockWorkerThread();
}
TEST_F(ProcessSingletonLinuxTest, CreateFailsWithExistingBrowser) {
CreateProcessSingletonOnThread();
scoped_ptr<TestableProcessSingleton> process_singleton(
CreateProcessSingleton());
process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
EXPECT_FALSE(process_singleton->Create());
}
TEST_F(ProcessSingletonLinuxTest, CreateChecksCompatibilitySocket) {
CreateProcessSingletonOnThread();
scoped_ptr<TestableProcessSingleton> process_singleton(
CreateProcessSingleton());
process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
char buf[PATH_MAX];
ssize_t len = readlink(socket_path_.value().c_str(), buf, sizeof(buf));
ASSERT_GT(len, 0);
base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
ASSERT_EQ(0, unlink(socket_path_.value().c_str()));
ASSERT_EQ(0, rename(socket_target_path.value().c_str(),
socket_path_.value().c_str()));
ASSERT_EQ(0, unlink(cookie_path_.value().c_str()));
EXPECT_FALSE(process_singleton->Create());
}
TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessOrCreate_BadCookie) {
CreateProcessSingletonOnThread();
EXPECT_EQ(0, unlink(cookie_path_.value().c_str()));
EXPECT_EQ(0, symlink("INCORRECTCOOKIE", cookie_path_.value().c_str()));
EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
std::string url("about:blank");
EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
NotifyOtherProcessOrCreate(url, TestTimeouts::action_timeout()));
}