268 lines
7.7 KiB
C++
268 lines
7.7 KiB
C++
//
|
|
// Copyright (C) 2012 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
#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_process_manager.h"
|
|
#include "shill/nice_mock_control.h"
|
|
#include "shill/test_event_dispatcher.h"
|
|
|
|
using std::map;
|
|
using std::set;
|
|
using std::string;
|
|
using std::vector;
|
|
using testing::_;
|
|
using testing::Matcher;
|
|
using testing::MatchesRegex;
|
|
using testing::Mock;
|
|
using testing::NiceMock;
|
|
using testing::Return;
|
|
using testing::SetArgumentPointee;
|
|
using testing::StrEq;
|
|
|
|
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) {}
|
|
|
|
virtual ~ExternalTaskTest() {}
|
|
|
|
virtual void TearDown() {
|
|
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_METHOD2(GetLogin, void(string* user, string* password));
|
|
MOCK_METHOD2(Notify, void(const string& reason,
|
|
const map<string, string>& dict));
|
|
|
|
MOCK_METHOD2(TaskDiedCallback, void(pid_t pid, int exit_status));
|
|
|
|
NiceMockControl 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, DestroyLater) {
|
|
const unsigned int kTag = 123;
|
|
const int kPID = 123456;
|
|
FakeUpRunningProcess(kTag, kPID);
|
|
ExpectStop(kTag, kPID);
|
|
external_task_.release()->DestroyLater(&dispatcher_);
|
|
dispatcher_.DispatchPendingEvents();
|
|
VerifyStop();
|
|
}
|
|
|
|
namespace {
|
|
|
|
// Returns true iff. there is at least one anchored match in |arg|,
|
|
// for each item in |expected_values|. Order of items does not matter.
|
|
//
|
|
// |arg| is a NULL-terminated array of C-strings.
|
|
// |expected_values| is a container of regular expressions (as strings).
|
|
MATCHER_P(HasElementsMatching, expected_values, "") {
|
|
for (const auto& expected_value : expected_values) {
|
|
auto regex_matcher(MatchesRegex(expected_value).impl());
|
|
char** arg_local = arg;
|
|
while (*arg_local) {
|
|
if (regex_matcher.MatchAndExplain(*arg_local, result_listener)) {
|
|
break;
|
|
}
|
|
++arg_local;
|
|
}
|
|
if (*arg_local == nullptr) {
|
|
*result_listener << "missing value " << expected_value << "\n";
|
|
arg_local = arg;
|
|
while (*arg_local) {
|
|
*result_listener << "received: " << *arg_local << "\n";
|
|
++arg_local;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_F(ExternalTaskTest, Start) {
|
|
const string kCommand = "/run/me";
|
|
const vector<string> kCommandOptions{"arg1", "arg2"};
|
|
const map<string, string> kCommandEnv{{"env1", "val1"}, {"env2", "val2"}};
|
|
map<string, string> expected_env;
|
|
expected_env.emplace(kRPCTaskServiceVariable, RPCTaskMockAdaptor::kRpcConnId);
|
|
expected_env.emplace(kRPCTaskPathVariable, RPCTaskMockAdaptor::kRpcId);
|
|
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) {
|
|
string username;
|
|
string password;
|
|
EXPECT_CALL(*this, GetLogin(&username, &password));
|
|
EXPECT_CALL(*this, Notify(_, _)).Times(0);
|
|
external_task_->GetLogin(&username, &password);
|
|
}
|
|
|
|
TEST_F(ExternalTaskTest, Notify) {
|
|
const string kReason("you may already have won!");
|
|
const map<string, 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
|