blob: 8230d5bb2e15bba9877e7739659507a096c43546 [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <signal.h>
#include <string>
#include <base/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/string_number_conversions.h>
#include <chromeos/process_mock.h>
#include <chromeos/test_helpers.h>
#include <gtest/gtest.h>
#include "vpn-manager/daemon.h"
using ::base::FilePath;
using ::base::IntToString;
using ::chromeos::Process;
using ::chromeos::ProcessImpl;
using ::chromeos::ProcessMock;
using ::std::string;
using ::testing::_;
using ::testing::InvokeWithoutArgs;
using ::testing::Mock;
using ::testing::Return;
namespace vpn_manager {
class DaemonTest : public ::testing::Test {
public:
void SetUp() {
FilePath cwd;
CHECK(temp_dir_.CreateUniqueTempDir());
FilePath test_path = temp_dir_.path().Append("daemon_testdir");
file_util::Delete(test_path, true);
file_util::CreateDirectory(test_path);
pid_file_path_ = test_path.Append("process.pid");
daemon_.reset(new Daemon(pid_file_path_.value()));
}
// Needs to be public so we can use the testing::Invoke() family of functions.
bool KillRealProcess() {
return real_process_->Kill(SIGTERM, 5);
}
protected:
void WritePidFile(const string &pid) {
if (file_util::WriteFile(pid_file_path_, pid.c_str(), pid.size()) < 0) {
LOG(ERROR) << "Unable to create " << pid_file_path_.value();
}
}
void MakeRealProcess() {
real_process_.reset(new ProcessImpl);
real_process_->AddArg("sleep");
real_process_->AddArg("12345");
CHECK(real_process_->Start());
}
const string &GetPidFile() {
return daemon_->pid_file_;
}
Process *GetProcess() {
return daemon_->process_.get();
}
void SetProcess(Process *process) {
daemon_->SetProcess(process);
}
FilePath pid_file_path_;
scoped_ptr<Daemon> daemon_;
scoped_ptr<Process> real_process_;
base::ScopedTempDir temp_dir_;
};
TEST_F(DaemonTest, Construction) {
EXPECT_EQ(NULL, GetProcess());
EXPECT_EQ(pid_file_path_.value(), GetPidFile());
EXPECT_FALSE(daemon_->IsRunning());
}
TEST_F(DaemonTest, FindProcess) {
EXPECT_FALSE(daemon_->FindProcess());
EXPECT_FALSE(daemon_->IsRunning());
// Start a real process and note its pid, then kill it so we know we have
// a non-running pid.
MakeRealProcess();
pid_t pid = real_process_->pid();
KillRealProcess();
WritePidFile(IntToString(pid));
EXPECT_FALSE(daemon_->FindProcess());
EXPECT_EQ(NULL, GetProcess());
MakeRealProcess();
pid = real_process_->pid();
WritePidFile(IntToString(pid));
EXPECT_TRUE(daemon_->FindProcess());
EXPECT_TRUE(GetProcess());
EXPECT_EQ(pid, GetProcess()->pid());
}
TEST_F(DaemonTest, IsRunningAndGetPid) {
EXPECT_FALSE(daemon_->IsRunning());
EXPECT_EQ(0, daemon_->GetPid());
MakeRealProcess();
pid_t pid = real_process_->pid();
ASSERT_NE(0, pid);
SetProcess(real_process_.release());
EXPECT_TRUE(daemon_->IsRunning());
EXPECT_EQ(pid, daemon_->GetPid());
// Kill the process outside of the view of the process owned by the daemon.
scoped_ptr<Process> killed_process(new ProcessImpl);
killed_process->Reset(pid);
killed_process->Kill(SIGTERM, 5);
EXPECT_FALSE(daemon_->IsRunning());
EXPECT_EQ(pid, daemon_->GetPid());
SetProcess(NULL);
EXPECT_EQ(0, daemon_->GetPid());
}
TEST_F(DaemonTest, SetProcessFromNull) {
EXPECT_EQ(NULL, GetProcess());
SetProcess(NULL); // Should be a no-op.
ProcessMock *process0 = new ProcessMock;
SetProcess(process0); // Passes ownership.
EXPECT_EQ(process0, GetProcess());
// Called during destructor.
EXPECT_CALL(*process0, pid()).WillOnce(Return(0));
}
TEST_F(DaemonTest, SetProcessToNullFromNotRunning) {
ProcessMock *process = new ProcessMock;
EXPECT_CALL(*process, Release()).Times(0);
EXPECT_CALL(*process, pid()).WillOnce(Return(0));
SetProcess(process); // Passes ownership.
SetProcess(NULL);
EXPECT_EQ(NULL, GetProcess());
}
TEST_F(DaemonTest, SetProcessToNullFromRunning) {
MakeRealProcess();
ProcessMock *process = new ProcessMock;
EXPECT_CALL(*process, Release()).Times(0);
EXPECT_CALL(*process, pid()).WillRepeatedly(Return(real_process_->pid()));
EXPECT_CALL(*process, Kill(SIGKILL, _)).Times(1);
SetProcess(process); // Passes ownership.
SetProcess(NULL);
EXPECT_EQ(NULL, GetProcess());
}
TEST_F(DaemonTest, SetProcessToDifferentPid) {
MakeRealProcess();
ProcessMock *process0 = new ProcessMock;
EXPECT_CALL(*process0, Release()).Times(0);
EXPECT_CALL(*process0, pid()).WillRepeatedly(Return(real_process_->pid()));
EXPECT_CALL(*process0, Kill(SIGKILL, _)).Times(1);
ProcessMock *process1 = new ProcessMock;
EXPECT_CALL(*process1, Release()).Times(0);
EXPECT_CALL(*process1, pid()).WillOnce(Return(2));
SetProcess(process0); // Passes ownership.
SetProcess(process1); // Passes ownership.
EXPECT_EQ(process1, GetProcess());
// Verify expectations now so we don't trigger on calls during the destructor.
Mock::VerifyAndClearExpectations(process1);
EXPECT_CALL(*process1, pid()).WillOnce(Return(0));
}
TEST_F(DaemonTest, SetProcessToSamePid) {
ProcessMock *process0 = new ProcessMock;
EXPECT_CALL(*process0, Release()).Times(1);
EXPECT_CALL(*process0, pid()).WillOnce(Return(1));
ProcessMock *process1 = new ProcessMock;
EXPECT_CALL(*process1, Release()).Times(0);
EXPECT_CALL(*process1, pid()).WillOnce(Return(1));
SetProcess(process0); // Passes ownership.
SetProcess(process1); // Passes ownership.
EXPECT_EQ(process1, GetProcess());
// Reset expectations now so we don't trigger on calls during the destructor.
Mock::VerifyAndClearExpectations(process1);
EXPECT_CALL(*process1, pid()).WillOnce(Return(0));
}
TEST_F(DaemonTest, TerminateNoProcess) {
WritePidFile("");
ASSERT_TRUE(file_util::PathExists(pid_file_path_));
EXPECT_TRUE(daemon_->Terminate());
EXPECT_FALSE(file_util::PathExists(pid_file_path_));
}
TEST_F(DaemonTest, TerminateDeadProcess) {
ProcessMock *process = new ProcessMock;
EXPECT_CALL(*process, pid()).Times(2).WillRepeatedly(Return(0));
EXPECT_CALL(*process, Kill(SIGTERM, _)).Times(0);
SetProcess(process); // Passes ownership.
WritePidFile("");
ASSERT_TRUE(file_util::PathExists(pid_file_path_));
EXPECT_TRUE(daemon_->Terminate());
EXPECT_FALSE(file_util::PathExists(pid_file_path_));
}
TEST_F(DaemonTest, TerminateLiveProcess) {
MakeRealProcess();
ProcessMock *process = new ProcessMock;
EXPECT_CALL(*process, pid()).WillRepeatedly(Return(real_process_->pid()));
EXPECT_CALL(*process, Kill(SIGTERM, _))
.WillOnce(InvokeWithoutArgs(this, &DaemonTest::KillRealProcess));
EXPECT_CALL(*process, Kill(SIGKILL, _)).Times(0);
SetProcess(process); // Passes ownership.
WritePidFile("");
ASSERT_TRUE(file_util::PathExists(pid_file_path_));
// Returns false since the daemon was unable to terminate the process.
EXPECT_TRUE(daemon_->Terminate());
EXPECT_FALSE(file_util::PathExists(pid_file_path_));
}
TEST_F(DaemonTest, Destructor) {
// This doesn't directly unit-test the Daemon class, but it does illuminate
// a side effect of the destruction of the underlying Process it holds.
MakeRealProcess();
ProcessMock *process = new ProcessMock;
EXPECT_CALL(*process, pid()).WillRepeatedly(Return(real_process_->pid()));
EXPECT_CALL(*process, Kill(SIGKILL, _)).Times(1);
SetProcess(process); // Passes ownership.
EXPECT_TRUE(daemon_->IsRunning());
SetProcess(NULL);
daemon_.reset();
}
} // namespace vpn_manager
int main(int argc, char** argv) {
SetUpTests(&argc, argv, true);
return RUN_ALL_TESTS();
}