| // 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 std::map; |
| using std::string; |
| using std::vector; |
| 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_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)); |
| |
| 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, DestroyLater) { |
| const unsigned int kTag = 123; |
| const int kPID = 123456; |
| FakeUpRunningProcess(kTag, kPID); |
| ExpectStop(kTag, kPID); |
| external_task_.release()->DestroyLater(&dispatcher_); |
| dispatcher_.DispatchPendingEvents(); |
| VerifyStop(); |
| } |
| |
| 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 |