You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
142 lines
4.3 KiB
142 lines
4.3 KiB
// 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 <brillo/process_reaper.h>
|
|
|
|
#include <signal.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
#include <base/bind.h>
|
|
#include <base/location.h>
|
|
#include <base/message_loop/message_loop.h>
|
|
#include <brillo/asynchronous_signal_handler.h>
|
|
#include <brillo/message_loops/base_message_loop.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
namespace {
|
|
|
|
pid_t ForkChildAndExit(int exit_code) {
|
|
pid_t pid = fork();
|
|
PCHECK(pid != -1);
|
|
if (pid == 0) {
|
|
_exit(exit_code);
|
|
}
|
|
return pid;
|
|
}
|
|
|
|
pid_t ForkChildAndKill(int sig) {
|
|
pid_t pid = fork();
|
|
PCHECK(pid != -1);
|
|
if (pid == 0) {
|
|
if (raise(sig) != 0) {
|
|
PLOG(ERROR) << "raise(" << sig << ")";
|
|
}
|
|
_exit(0); // Not reached. This value will cause the test to fail.
|
|
}
|
|
return pid;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace brillo {
|
|
|
|
class ProcessReaperTest : public ::testing::Test {
|
|
public:
|
|
void SetUp() override {
|
|
brillo_loop_.SetAsCurrent();
|
|
async_signal_handler_.Init();
|
|
process_reaper_.Register(&async_signal_handler_);
|
|
}
|
|
|
|
protected:
|
|
base::MessageLoopForIO base_loop_;
|
|
brillo::BaseMessageLoop brillo_loop_{&base_loop_};
|
|
brillo::AsynchronousSignalHandler async_signal_handler_;
|
|
|
|
// ProcessReaper under test.
|
|
ProcessReaper process_reaper_;
|
|
};
|
|
|
|
TEST_F(ProcessReaperTest, UnregisterWhenNotRegistered) {
|
|
ProcessReaper another_process_reaper_;
|
|
another_process_reaper_.Unregister();
|
|
}
|
|
|
|
TEST_F(ProcessReaperTest, UnregisterAndReregister) {
|
|
process_reaper_.Unregister();
|
|
process_reaper_.Register(&async_signal_handler_);
|
|
// This checks that we can unregister the ProcessReaper and then destroy it.
|
|
process_reaper_.Unregister();
|
|
}
|
|
|
|
TEST_F(ProcessReaperTest, ReapExitedChild) {
|
|
pid_t pid = ForkChildAndExit(123);
|
|
EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::BindOnce(
|
|
[](MessageLoop* loop, const siginfo_t& info) {
|
|
EXPECT_EQ(CLD_EXITED, info.si_code);
|
|
EXPECT_EQ(123, info.si_status);
|
|
loop->BreakLoop();
|
|
}, &brillo_loop_)));
|
|
brillo_loop_.Run();
|
|
}
|
|
|
|
// Test that simultaneous child processes fire their respective callbacks when
|
|
// exiting.
|
|
TEST_F(ProcessReaperTest, ReapedChildrenMatchCallbacks) {
|
|
int running_children = 10;
|
|
for (int i = 0; i < running_children; ++i) {
|
|
// Different processes will have different exit values.
|
|
int exit_value = 1 + i;
|
|
pid_t pid = ForkChildAndExit(exit_value);
|
|
EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::BindOnce(
|
|
[](MessageLoop* loop, int exit_value, int* running_children,
|
|
const siginfo_t& info) {
|
|
EXPECT_EQ(CLD_EXITED, info.si_code);
|
|
EXPECT_EQ(exit_value, info.si_status);
|
|
(*running_children)--;
|
|
if (*running_children == 0)
|
|
loop->BreakLoop();
|
|
}, &brillo_loop_, exit_value, &running_children)));
|
|
}
|
|
// This sleep is optional. It helps to have more processes exit before we
|
|
// start watching for them in the message loop.
|
|
usleep(10 * 1000);
|
|
brillo_loop_.Run();
|
|
EXPECT_EQ(0, running_children);
|
|
}
|
|
|
|
TEST_F(ProcessReaperTest, ReapKilledChild) {
|
|
pid_t pid = ForkChildAndKill(SIGKILL);
|
|
EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::BindOnce(
|
|
[](MessageLoop* loop, const siginfo_t& info) {
|
|
EXPECT_EQ(CLD_KILLED, info.si_code);
|
|
EXPECT_EQ(SIGKILL, info.si_status);
|
|
loop->BreakLoop();
|
|
}, &brillo_loop_)));
|
|
brillo_loop_.Run();
|
|
}
|
|
|
|
TEST_F(ProcessReaperTest, ReapKilledAndForgottenChild) {
|
|
pid_t pid = ForkChildAndExit(0);
|
|
EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::BindOnce(
|
|
[](MessageLoop* loop, const siginfo_t& /* info */) {
|
|
ADD_FAILURE() << "Child process was still tracked.";
|
|
loop->BreakLoop();
|
|
}, &brillo_loop_)));
|
|
EXPECT_TRUE(process_reaper_.ForgetChild(pid));
|
|
|
|
// A second call should return failure.
|
|
EXPECT_FALSE(process_reaper_.ForgetChild(pid));
|
|
|
|
// Run the loop with a timeout, as the BreakLoop() above is not expected.
|
|
brillo_loop_.PostDelayedTask(FROM_HERE,
|
|
base::Bind(&MessageLoop::BreakLoop,
|
|
base::Unretained(&brillo_loop_)),
|
|
base::TimeDelta::FromMilliseconds(100));
|
|
brillo_loop_.Run();
|
|
}
|
|
|
|
} // namespace brillo
|