blob: f70458e9741e373097a26eb8f33d1bd6ebb94a21 [file] [log] [blame]
// Copyright 2018 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/external_task.h"
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include <base/bind.h>
#include <base/files/file_path.h>
#include <base/memory/weak_ptr.h>
#include <base/strings/string_util.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "shill/mock_adaptors.h"
#include "shill/mock_control.h"
#include "shill/mock_process_manager.h"
#include "shill/test_event_dispatcher.h"
using testing::_;
using testing::Mock;
using testing::Return;
namespace shill {
class ExternalTaskTest : public testing::Test, public RpcTaskDelegate {
public:
ExternalTaskTest()
: weak_ptr_factory_(this),
death_callback_(base::Bind(&ExternalTaskTest::TaskDiedCallback,
weak_ptr_factory_.GetWeakPtr())),
external_task_(new ExternalTask(&control_,
&process_manager_,
weak_ptr_factory_.GetWeakPtr(),
death_callback_)),
test_rpc_task_destroyed_(false) {}
~ExternalTaskTest() override = default;
void TearDown() override {
if (!external_task_) {
return;
}
if (external_task_->pid_) {
EXPECT_CALL(process_manager_, StopProcess(external_task_->pid_));
}
}
void set_test_rpc_task_destroyed(bool destroyed) {
test_rpc_task_destroyed_ = destroyed;
}
// Defined out-of-line, due to dependency on TestRpcTask.
void FakeUpRunningProcess(unsigned int tag, int pid);
void ExpectStop(unsigned int tag, int pid) {
EXPECT_CALL(process_manager_, StopProcess(pid));
}
void VerifyStop() {
if (external_task_) {
EXPECT_EQ(0, external_task_->pid_);
EXPECT_FALSE(external_task_->rpc_task_);
}
EXPECT_TRUE(test_rpc_task_destroyed_);
// Make sure EXPECTations were met before the fixture's dtor.
Mock::VerifyAndClearExpectations(&process_manager_);
}
protected:
// Implements RpcTaskDelegate interface.
MOCK_METHOD(void, GetLogin, (std::string*, std::string*), (override));
MOCK_METHOD(void,
Notify,
(const std::string&, (const std::map<std::string, std::string>&)),
(override));
MOCK_METHOD(void, TaskDiedCallback, (pid_t, int));
MockControl control_;
EventDispatcherForTest dispatcher_;
MockProcessManager process_manager_;
base::WeakPtrFactory<ExternalTaskTest> weak_ptr_factory_;
base::Callback<void(pid_t, int)> death_callback_;
std::unique_ptr<ExternalTask> external_task_;
bool test_rpc_task_destroyed_;
};
namespace {
class TestRpcTask : public RpcTask {
public:
TestRpcTask(ControlInterface* control, ExternalTaskTest* test);
virtual ~TestRpcTask();
private:
ExternalTaskTest* test_;
};
TestRpcTask::TestRpcTask(ControlInterface* control, ExternalTaskTest* test)
: RpcTask(control, test), test_(test) {
test_->set_test_rpc_task_destroyed(false);
}
TestRpcTask::~TestRpcTask() {
test_->set_test_rpc_task_destroyed(true);
test_ = nullptr;
}
} // namespace
void ExternalTaskTest::FakeUpRunningProcess(unsigned int tag, int pid) {
external_task_->pid_ = pid;
external_task_->rpc_task_.reset(new TestRpcTask(&control_, this));
}
TEST_F(ExternalTaskTest, Destructor) {
const unsigned int kTag = 123;
const int kPID = 123456;
FakeUpRunningProcess(kTag, kPID);
ExpectStop(kTag, kPID);
external_task_.reset();
VerifyStop();
}
TEST_F(ExternalTaskTest, Start) {
const std::string kCommand = "/run/me";
const std::vector<std::string> kCommandOptions{"arg1", "arg2"};
const std::map<std::string, std::string> kCommandEnv{{"env1", "val1"},
{"env2", "val2"}};
std::map<std::string, std::string> expected_env;
expected_env.emplace(kRpcTaskServiceVariable,
RpcTaskMockAdaptor::kRpcConnId.value());
expected_env.emplace(kRpcTaskPathVariable,
RpcTaskMockAdaptor::kRpcId.value());
expected_env.insert(kCommandEnv.begin(), kCommandEnv.end());
const int kPID = 234678;
EXPECT_CALL(process_manager_,
StartProcess(_, base::FilePath(kCommand), kCommandOptions,
expected_env, false, _))
.WillOnce(Return(-1))
.WillOnce(Return(kPID));
Error error;
EXPECT_FALSE(external_task_->Start(base::FilePath(kCommand), kCommandOptions,
kCommandEnv, false, &error));
EXPECT_EQ(Error::kInternalError, error.type());
EXPECT_FALSE(external_task_->rpc_task_);
error.Reset();
EXPECT_TRUE(external_task_->Start(base::FilePath(kCommand), kCommandOptions,
kCommandEnv, false, &error));
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ(kPID, external_task_->pid_);
EXPECT_NE(nullptr, external_task_->rpc_task_);
}
TEST_F(ExternalTaskTest, Stop) {
const unsigned int kTag = 123;
const int kPID = 123456;
FakeUpRunningProcess(kTag, kPID);
ExpectStop(kTag, kPID);
external_task_->Stop();
ASSERT_NE(nullptr, external_task_);
VerifyStop();
}
TEST_F(ExternalTaskTest, StopNotStarted) {
EXPECT_CALL(process_manager_, StopProcess(_)).Times(0);
external_task_->Stop();
EXPECT_FALSE(test_rpc_task_destroyed_);
}
TEST_F(ExternalTaskTest, GetLogin) {
std::string username;
std::string password;
EXPECT_CALL(*this, GetLogin(&username, &password));
EXPECT_CALL(*this, Notify(_, _)).Times(0);
external_task_->GetLogin(&username, &password);
}
TEST_F(ExternalTaskTest, Notify) {
const std::string kReason("you may already have won!");
const std::map<std::string, std::string>& kArgs{{"arg1", "val1"},
{"arg2", "val2"}};
EXPECT_CALL(*this, GetLogin(_, _)).Times(0);
EXPECT_CALL(*this, Notify(kReason, kArgs));
external_task_->Notify(kReason, kArgs);
}
TEST_F(ExternalTaskTest, OnTaskDied) {
const int kPID = 99999;
const int kExitStatus = 1;
external_task_->pid_ = kPID;
EXPECT_CALL(process_manager_, StopProcess(_)).Times(0);
EXPECT_CALL(*this, TaskDiedCallback(kPID, kExitStatus));
external_task_->OnTaskDied(kExitStatus);
EXPECT_EQ(0, external_task_->pid_);
}
} // namespace shill