blob: 90f9f3edf23d970c9d587d7066789fdfdefdbce0 [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 <utility>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_number_conversions.h>
#include <brillo/process/process_mock.h>
#include <brillo/test_helpers.h>
#include <gtest/gtest.h>
#include "vpn-manager/daemon.h"
using ::base::FilePath;
using ::base::NumberToString;
using ::brillo::Process;
using ::brillo::ProcessImpl;
using ::brillo::ProcessMock;
using ::std::string;
using ::testing::_;
using ::testing::InvokeWithoutArgs;
using ::testing::Mock;
using ::testing::Return;
namespace vpn_manager {
namespace {
const char kBinSleep[] = "/bin/sleep";
} // namespace
class DaemonTest : public ::testing::Test {
public:
void SetUp() override {
FilePath cwd;
CHECK(temp_dir_.CreateUniqueTempDir());
FilePath test_path = temp_dir_.GetPath().Append("daemon_testdir");
base::DeletePathRecursively(test_path);
base::CreateDirectory(test_path);
pid_file_path_ = test_path.Append("process.pid");
daemon_.reset(new Daemon(pid_file_path_.value()));
}
void TearDown() override {
if (real_process_)
KillRealProcess();
}
// 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 (base::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(kBinSleep);
real_process_->AddArg("12345");
CHECK(real_process_->Start());
}
const string& GetPidFile() { return daemon_->pid_file_; }
Process* GetProcess() { return daemon_->process_.get(); }
void SetProcess(std::unique_ptr<Process> process) {
daemon_->SetProcess(std::move(process));
}
FilePath pid_file_path_;
std::unique_ptr<Daemon> daemon_;
std::unique_ptr<Process> real_process_;
base::ScopedTempDir temp_dir_;
};
TEST_F(DaemonTest, Construction) {
EXPECT_EQ(nullptr, 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(NumberToString(pid));
EXPECT_FALSE(daemon_->FindProcess());
EXPECT_EQ(nullptr, GetProcess());
MakeRealProcess();
pid = real_process_->pid();
WritePidFile(NumberToString(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(std::move(real_process_));
EXPECT_TRUE(daemon_->IsRunning());
EXPECT_EQ(pid, daemon_->GetPid());
// Kill the process outside of the view of the process owned by the daemon.
std::unique_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(nullptr);
EXPECT_EQ(0, daemon_->GetPid());
}
TEST_F(DaemonTest, SetProcessFromNull) {
EXPECT_EQ(nullptr, GetProcess());
SetProcess(nullptr); // Should be a no-op.
auto process = std::make_unique<ProcessMock>();
ProcessMock* process_ptr = process.get();
SetProcess(std::move(process));
EXPECT_EQ(process_ptr, GetProcess());
// Called during destructor.
EXPECT_CALL(*process_ptr, pid()).WillOnce(Return(0));
}
TEST_F(DaemonTest, SetProcessToNullFromNotRunning) {
auto process = std::make_unique<ProcessMock>();
EXPECT_CALL(*process, Release()).Times(0);
EXPECT_CALL(*process, pid()).WillOnce(Return(0));
SetProcess(std::move(process));
SetProcess(nullptr);
EXPECT_EQ(nullptr, GetProcess());
}
TEST_F(DaemonTest, SetProcessToNullFromRunning) {
MakeRealProcess();
auto process = std::make_unique<ProcessMock>();
EXPECT_CALL(*process, Release()).Times(0);
EXPECT_CALL(*process, pid()).WillRepeatedly(Return(real_process_->pid()));
EXPECT_CALL(*process, Kill(SIGKILL, _)).Times(1);
SetProcess(std::move(process));
SetProcess(nullptr);
EXPECT_EQ(nullptr, GetProcess());
}
TEST_F(DaemonTest, SetProcessToDifferentPid) {
MakeRealProcess();
auto process0 = std::make_unique<ProcessMock>();
EXPECT_CALL(*process0, Release()).Times(0);
EXPECT_CALL(*process0, pid()).WillRepeatedly(Return(real_process_->pid()));
EXPECT_CALL(*process0, Kill(SIGKILL, _)).Times(1);
auto process1 = std::make_unique<ProcessMock>();
ProcessMock* process1_ptr = process1.get();
EXPECT_CALL(*process1, Release()).Times(0);
EXPECT_CALL(*process1, pid()).WillOnce(Return(2));
SetProcess(std::move(process0));
SetProcess(std::move(process1));
EXPECT_EQ(process1_ptr, GetProcess());
// Verify expectations now so we don't trigger on calls during the destructor.
Mock::VerifyAndClearExpectations(process1_ptr);
EXPECT_CALL(*process1_ptr, pid()).WillOnce(Return(0));
}
TEST_F(DaemonTest, SetProcessToSamePid) {
auto process0 = std::make_unique<ProcessMock>();
EXPECT_CALL(*process0, Release()).Times(1);
EXPECT_CALL(*process0, pid()).WillOnce(Return(1));
auto process1 = std::make_unique<ProcessMock>();
ProcessMock* process1_ptr = process1.get();
EXPECT_CALL(*process1, Release()).Times(0);
EXPECT_CALL(*process1, pid()).WillOnce(Return(1));
SetProcess(std::move(process0));
SetProcess(std::move(process1));
EXPECT_EQ(process1_ptr, GetProcess());
// Reset expectations now so we don't trigger on calls during the destructor.
Mock::VerifyAndClearExpectations(process1_ptr);
EXPECT_CALL(*process1_ptr, pid()).WillOnce(Return(0));
}
TEST_F(DaemonTest, TerminateNoProcess) {
WritePidFile("");
ASSERT_TRUE(base::PathExists(pid_file_path_));
EXPECT_TRUE(daemon_->Terminate());
EXPECT_FALSE(base::PathExists(pid_file_path_));
}
TEST_F(DaemonTest, TerminateDeadProcess) {
auto process = std::make_unique<ProcessMock>();
EXPECT_CALL(*process, pid()).Times(2).WillRepeatedly(Return(0));
EXPECT_CALL(*process, Kill(SIGTERM, _)).Times(0);
SetProcess(std::move(process));
WritePidFile("");
ASSERT_TRUE(base::PathExists(pid_file_path_));
EXPECT_TRUE(daemon_->Terminate());
EXPECT_FALSE(base::PathExists(pid_file_path_));
}
TEST_F(DaemonTest, TerminateLiveProcess) {
MakeRealProcess();
auto process = std::make_unique<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(std::move(process));
WritePidFile("");
ASSERT_TRUE(base::PathExists(pid_file_path_));
// Returns false since the daemon was unable to terminate the process.
EXPECT_TRUE(daemon_->Terminate());
EXPECT_FALSE(base::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();
auto process = std::make_unique<ProcessMock>();
EXPECT_CALL(*process, pid()).WillRepeatedly(Return(real_process_->pid()));
EXPECT_CALL(*process, Kill(SIGKILL, _)).Times(1);
SetProcess(std::move(process));
EXPECT_TRUE(daemon_->IsRunning());
SetProcess(nullptr);
daemon_.reset();
}
} // namespace vpn_manager