blob: 234c3f1825b22ef77b57e290bed1e37e9d9cd6c2 [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 "germ/launcher.h"
#include <sys/types.h>
#include <memory>
#include <base/at_exit.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <chromeos/process_mock.h>
#include <soma/read_only_container_spec.h>
#include "germ/proto_bindings/soma_container_spec.pb.h"
using ::testing::_;
using ::testing::Return;
namespace {
const std::string kInitctlOutput =
"germ_template (test) start/running, process 8117";
const pid_t kServicePid = 8117;
}
namespace germ {
class MockLauncher : public Launcher {
public:
MockLauncher() = default;
~MockLauncher() override = default;
// This takes ownership of the pointer.
void SetProcessInstance(chromeos::Process* instance) {
instance_.reset(instance);
}
protected:
std::string ReadFromStdout(chromeos::Process* process) override {
return kInitctlOutput;
}
std::unique_ptr<chromeos::Process> GetProcessInstance() override {
return std::move(instance_);
}
private:
std::unique_ptr<chromeos::Process> instance_;
};
class LauncherTest : public ::testing::Test {
public:
LauncherTest() = default;
~LauncherTest() override = default;
void SetUp() override {
const char name[] = "yes";
const char path[] = "/path/to/bundle";
const char* command_line[2] = {"/usr/bin/yes", "no"};
const uid_t uid = 1;
const gid_t gid = 8;
spec_.set_name(name);
spec_.set_service_bundle_path(path);
soma::ContainerSpec::Executable* executable = spec_.add_executables();
executable->set_uid(uid);
executable->set_gid(gid);
for (const char* arg : command_line) {
executable->add_command_line(arg);
}
ASSERT_TRUE(ro_spec_.Init(spec_));
}
protected:
base::AtExitManager exit_manager_;
soma::ContainerSpec spec_;
soma::ReadOnlyContainerSpec ro_spec_;
MockLauncher launcher_;
private:
DISALLOW_COPY_AND_ASSIGN(LauncherTest);
};
TEST_F(LauncherTest, LaunchDaemonizedSuccess) {
chromeos::ProcessMock* process = new chromeos::ProcessMock();
// This is here to prevent useless logging.
EXPECT_CALL(*process, AddArg(_)).WillRepeatedly(Return());
EXPECT_CALL(*process, RedirectUsingPipe(_, _)).WillRepeatedly(Return());
EXPECT_CALL(*process, Start()).WillOnce(Return(true));
EXPECT_CALL(*process, Wait()).WillOnce(Return(0 /* success */));
launcher_.SetProcessInstance(process);
pid_t pid;
ASSERT_TRUE(launcher_.RunDaemonized(ro_spec_, &pid));
// This pid is parsed from |kInitctlOutput|.
ASSERT_EQ(kServicePid, pid);
}
TEST_F(LauncherTest, LaunchDaemonizedInitctlFailure) {
chromeos::ProcessMock* process = new chromeos::ProcessMock();
// This is here to prevent useless logging.
EXPECT_CALL(*process, AddArg(_)).WillRepeatedly(Return());
EXPECT_CALL(*process, RedirectUsingPipe(_, _)).WillRepeatedly(Return());
EXPECT_CALL(*process, Start()).WillOnce(Return(true));
EXPECT_CALL(*process, Wait()).WillOnce(Return(1 /* failure */));
launcher_.SetProcessInstance(process);
pid_t pid;
ASSERT_FALSE(launcher_.RunDaemonized(ro_spec_, &pid));
ASSERT_EQ(-1, pid);
}
TEST_F(LauncherTest, TerminateSuccess) {
chromeos::ProcessMock* start_process = new chromeos::ProcessMock();
// This is here to prevent useless logging.
EXPECT_CALL(*start_process, AddArg(_)).WillRepeatedly(Return());
EXPECT_CALL(*start_process, RedirectUsingPipe(_, _)).WillRepeatedly(Return());
EXPECT_CALL(*start_process, Start()).WillOnce(Return(true));
EXPECT_CALL(*start_process, Wait()).WillOnce(Return(0 /* success */));
launcher_.SetProcessInstance(start_process);
pid_t pid;
ASSERT_TRUE(launcher_.RunDaemonized(ro_spec_, &pid));
// This pid is parsed from the string in ReadFromStdout() above.
ASSERT_EQ(kServicePid, pid);
chromeos::ProcessMock* stop_process = new chromeos::ProcessMock();
EXPECT_CALL(*stop_process, AddArg(_)).WillRepeatedly(Return());
EXPECT_CALL(*stop_process, RedirectUsingPipe(_, _)).WillRepeatedly(Return());
EXPECT_CALL(*stop_process, Run()).WillOnce(Return(0 /* success */));
launcher_.SetProcessInstance(stop_process);
ASSERT_TRUE(launcher_.Terminate(kServicePid));
}
TEST_F(LauncherTest, TerminateInitctlFailure) {
chromeos::ProcessMock* start_process = new chromeos::ProcessMock();
// This is here to prevent useless logging.
EXPECT_CALL(*start_process, AddArg(_)).WillRepeatedly(Return());
EXPECT_CALL(*start_process, RedirectUsingPipe(_, _)).WillRepeatedly(Return());
EXPECT_CALL(*start_process, Start()).WillOnce(Return(true));
EXPECT_CALL(*start_process, Wait()).WillOnce(Return(0 /* success */));
launcher_.SetProcessInstance(start_process);
pid_t pid;
ASSERT_TRUE(launcher_.RunDaemonized(ro_spec_, &pid));
// This pid is parsed from the string in ReadFromStdout() above.
ASSERT_EQ(kServicePid, pid);
chromeos::ProcessMock* stop_process = new chromeos::ProcessMock();
EXPECT_CALL(*stop_process, AddArg(_)).WillRepeatedly(Return());
EXPECT_CALL(*stop_process, RedirectUsingPipe(_, _)).WillRepeatedly(Return());
EXPECT_CALL(*stop_process, Run()).WillOnce(Return(1 /* success */));
launcher_.SetProcessInstance(stop_process);
ASSERT_FALSE(launcher_.Terminate(kServicePid));
}
TEST_F(LauncherTest, TerminateInvalidPidFailure) {
chromeos::ProcessMock* process = new chromeos::ProcessMock();
// This is here to prevent useless logging.
EXPECT_CALL(*process, AddArg(_)).WillRepeatedly(Return());
launcher_.SetProcessInstance(process);
// Invalid pid.
ASSERT_FALSE(launcher_.Terminate(-1));
// Unknown pid.
ASSERT_FALSE(launcher_.Terminate(kServicePid + 1));
}
} // namespace germ