blob: abeb84f4651ef14035c651acb001e26c49f5d4d3 [file] [log] [blame]
// Copyright 2015 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 "shill/process_manager.h"
#include <string>
#include <vector>
#include <base/bind.h>
#include <chromeos/minijail/mock_minijail.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "shill/mock_event_dispatcher.h"
using base::Bind;
using base::Callback;
using base::CancelableClosure;
using base::Closure;
using base::Unretained;
using std::string;
using std::vector;
using testing::_;
using testing::DoAll;
using testing::Return;
using testing::SetArgumentPointee;
using testing::StrEq;
namespace shill {
class ProcessManagerTest : public testing::Test {
public:
ProcessManagerTest() : process_manager_(ProcessManager::GetInstance()) {}
virtual void SetUp() {
process_manager_->dispatcher_ = &dispatcher_;
process_manager_->minijail_ = &minijail_;
}
virtual void TearDown() {
process_manager_->watched_processes_.clear();
process_manager_->pending_termination_processes_.clear();
}
void AddWatchedProcess(pid_t pid, const Callback<void(int)>& callback) {
process_manager_->watched_processes_.emplace(pid, callback);
}
void AddTerminateProcess(pid_t pid,
std::unique_ptr<CancelableClosure> timeout_handler) {
process_manager_->pending_termination_processes_.emplace(
pid, std::move(timeout_handler));
}
void AssertEmptyWatchedProcesses() {
EXPECT_TRUE(process_manager_->watched_processes_.empty());
}
void AssertNonEmptyWatchedProcesses() {
EXPECT_FALSE(process_manager_->watched_processes_.empty());
}
void AssertEmptyTerminateProcesses() {
EXPECT_TRUE(process_manager_->pending_termination_processes_.empty());
}
void OnProcessExited(pid_t pid, int exit_status) {
siginfo_t info;
info.si_status = exit_status;
process_manager_->OnProcessExited(pid, info);
}
void OnTerminationTimeout(pid_t pid, bool kill_signal) {
process_manager_->ProcessTerminationTimeoutHandler(pid, kill_signal);
}
protected:
class CallbackObserver {
public:
CallbackObserver()
: exited_callback_(
Bind(&CallbackObserver::OnProcessExited, Unretained(this))),
termination_timeout_callback_(
Bind(&CallbackObserver::OnTerminationTimeout,
Unretained(this))) {}
virtual ~CallbackObserver() {}
MOCK_METHOD1(OnProcessExited, void(int exit_status));
MOCK_METHOD0(OnTerminationTimeout, void());
Callback<void(int)> exited_callback_;
Closure termination_timeout_callback_;
};
MockEventDispatcher dispatcher_;
chromeos::MockMinijail minijail_;
ProcessManager* process_manager_;
};
MATCHER_P2(IsProcessArgs, program, args, "") {
if (string(arg[0]) != program) {
return false;
}
int index = 1;
for (const auto& option : args) {
if (string(arg[index++]) != option) {
return false;
}
}
return arg[index] == nullptr;
}
TEST_F(ProcessManagerTest, WatchedProcessExited) {
const pid_t kPid = 123;
const int kExitStatus = 1;
CallbackObserver observer;
AddWatchedProcess(kPid, observer.exited_callback_);
EXPECT_CALL(observer, OnProcessExited(kExitStatus)).Times(1);
OnProcessExited(kPid, kExitStatus);
AssertEmptyWatchedProcesses();
}
TEST_F(ProcessManagerTest, TerminateProcessExited) {
const pid_t kPid = 123;
CallbackObserver observer;
std::unique_ptr<CancelableClosure> timeout_handler(
new CancelableClosure(observer.termination_timeout_callback_));
AddTerminateProcess(kPid, std::move(timeout_handler));
EXPECT_CALL(observer, OnTerminationTimeout()).Times(0);
OnProcessExited(kPid, 1);
AssertEmptyTerminateProcesses();
}
TEST_F(ProcessManagerTest, StartProcessInMinijail) {
const string kProgram = "/usr/bin/dump";
const vector<string> kArgs = { "-b", "-g" };
const string kUser = "user";
const string kGroup = "group";
const uint64_t kCapMask = 1;
const pid_t kPid = 123;
EXPECT_CALL(minijail_, DropRoot(_, StrEq(kUser), StrEq(kGroup))).Times(1);
EXPECT_CALL(minijail_, UseCapabilities(_, kCapMask)).Times(1);
EXPECT_CALL(minijail_, RunAndDestroy(_, IsProcessArgs(kProgram, kArgs), _))
.WillOnce(DoAll(SetArgumentPointee<2>(kPid), Return(true)));
pid_t actual_pid =
process_manager_->StartProcessInMinijail(FROM_HERE,
base::FilePath(kProgram),
kArgs,
kUser,
kGroup,
kCapMask,
Callback<void(int)>());
EXPECT_EQ(kPid, actual_pid);
AssertNonEmptyWatchedProcesses();
}
TEST_F(ProcessManagerTest, StartProcessInMinijailFailed) {
const string kProgram = "/usr/bin/dump";
const vector<string> kArgs = { "-b", "-g" };
const string kUser = "user";
const string kGroup = "group";
const uint64_t kCapMask = 1;
EXPECT_CALL(minijail_, DropRoot(_, StrEq(kUser), StrEq(kGroup))).Times(1);
EXPECT_CALL(minijail_, UseCapabilities(_, kCapMask)).Times(1);
EXPECT_CALL(minijail_, RunAndDestroy(_, IsProcessArgs(kProgram, kArgs), _))
.WillOnce(Return(false));
pid_t actual_pid =
process_manager_->StartProcessInMinijail(FROM_HERE,
base::FilePath(kProgram),
kArgs,
kUser,
kGroup,
kCapMask,
Callback<void(int)>());
EXPECT_EQ(-1, actual_pid);
AssertEmptyWatchedProcesses();
}
} // namespace shill