1// 2// Copyright (C) 2012 The Android Open Source Project 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15// 16 17#include "shill/external_task.h" 18 19#include <map> 20#include <memory> 21#include <set> 22#include <string> 23#include <vector> 24 25#include <base/bind.h> 26#include <base/files/file_path.h> 27#include <base/memory/weak_ptr.h> 28#include <base/strings/string_util.h> 29#include <gmock/gmock.h> 30#include <gtest/gtest.h> 31 32#include "shill/mock_adaptors.h" 33#include "shill/mock_process_manager.h" 34#include "shill/nice_mock_control.h" 35#include "shill/test_event_dispatcher.h" 36 37using std::map; 38using std::set; 39using std::string; 40using std::vector; 41using testing::_; 42using testing::Matcher; 43using testing::MatchesRegex; 44using testing::Mock; 45using testing::NiceMock; 46using testing::Return; 47using testing::SetArgumentPointee; 48using testing::StrEq; 49 50namespace shill { 51 52class ExternalTaskTest : public testing::Test, 53 public RPCTaskDelegate { 54 public: 55 ExternalTaskTest() 56 : weak_ptr_factory_(this), 57 death_callback_( 58 base::Bind(&ExternalTaskTest::TaskDiedCallback, 59 weak_ptr_factory_.GetWeakPtr())), 60 external_task_( 61 new ExternalTask(&control_, &process_manager_, 62 weak_ptr_factory_.GetWeakPtr(), 63 death_callback_)), 64 test_rpc_task_destroyed_(false) {} 65 66 virtual ~ExternalTaskTest() {} 67 68 virtual void TearDown() { 69 if (!external_task_) { 70 return; 71 } 72 73 if (external_task_->pid_) { 74 EXPECT_CALL(process_manager_, StopProcess(external_task_->pid_)); 75 } 76 } 77 78 void set_test_rpc_task_destroyed(bool destroyed) { 79 test_rpc_task_destroyed_ = destroyed; 80 } 81 82 // Defined out-of-line, due to dependency on TestRPCTask. 83 void FakeUpRunningProcess(unsigned int tag, int pid); 84 85 void ExpectStop(unsigned int tag, int pid) { 86 EXPECT_CALL(process_manager_, StopProcess(pid)); 87 } 88 89 void VerifyStop() { 90 if (external_task_) { 91 EXPECT_EQ(0, external_task_->pid_); 92 EXPECT_FALSE(external_task_->rpc_task_); 93 } 94 EXPECT_TRUE(test_rpc_task_destroyed_); 95 // Make sure EXPECTations were met before the fixture's dtor. 96 Mock::VerifyAndClearExpectations(&process_manager_); 97 } 98 99 protected: 100 // Implements RPCTaskDelegate interface. 101 MOCK_METHOD2(GetLogin, void(string* user, string* password)); 102 MOCK_METHOD2(Notify, void(const string& reason, 103 const map<string, string>& dict)); 104 105 MOCK_METHOD2(TaskDiedCallback, void(pid_t pid, int exit_status)); 106 107 NiceMockControl control_; 108 EventDispatcherForTest dispatcher_; 109 MockProcessManager process_manager_; 110 base::WeakPtrFactory<ExternalTaskTest> weak_ptr_factory_; 111 base::Callback<void(pid_t, int)> death_callback_; 112 std::unique_ptr<ExternalTask> external_task_; 113 bool test_rpc_task_destroyed_; 114}; 115 116namespace { 117 118class TestRPCTask : public RPCTask { 119 public: 120 TestRPCTask(ControlInterface* control, ExternalTaskTest* test); 121 virtual ~TestRPCTask(); 122 123 private: 124 ExternalTaskTest* test_; 125}; 126 127TestRPCTask::TestRPCTask(ControlInterface* control, ExternalTaskTest* test) 128 : RPCTask(control, test), 129 test_(test) { 130 test_->set_test_rpc_task_destroyed(false); 131} 132 133TestRPCTask::~TestRPCTask() { 134 test_->set_test_rpc_task_destroyed(true); 135 test_ = nullptr; 136} 137 138} // namespace 139 140void ExternalTaskTest::FakeUpRunningProcess(unsigned int tag, int pid) { 141 external_task_->pid_ = pid; 142 external_task_->rpc_task_.reset(new TestRPCTask(&control_, this)); 143} 144 145TEST_F(ExternalTaskTest, Destructor) { 146 const unsigned int kTag = 123; 147 const int kPID = 123456; 148 FakeUpRunningProcess(kTag, kPID); 149 ExpectStop(kTag, kPID); 150 external_task_.reset(); 151 VerifyStop(); 152} 153 154TEST_F(ExternalTaskTest, DestroyLater) { 155 const unsigned int kTag = 123; 156 const int kPID = 123456; 157 FakeUpRunningProcess(kTag, kPID); 158 ExpectStop(kTag, kPID); 159 external_task_.release()->DestroyLater(&dispatcher_); 160 dispatcher_.DispatchPendingEvents(); 161 VerifyStop(); 162} 163 164namespace { 165 166// Returns true iff. there is at least one anchored match in |arg|, 167// for each item in |expected_values|. Order of items does not matter. 168// 169// |arg| is a NULL-terminated array of C-strings. 170// |expected_values| is a container of regular expressions (as strings). 171MATCHER_P(HasElementsMatching, expected_values, "") { 172 for (const auto& expected_value : expected_values) { 173 auto regex_matcher(MatchesRegex(expected_value).impl()); 174 char** arg_local = arg; 175 while (*arg_local) { 176 if (regex_matcher.MatchAndExplain(*arg_local, result_listener)) { 177 break; 178 } 179 ++arg_local; 180 } 181 if (*arg_local == nullptr) { 182 *result_listener << "missing value " << expected_value << "\n"; 183 arg_local = arg; 184 while (*arg_local) { 185 *result_listener << "received: " << *arg_local << "\n"; 186 ++arg_local; 187 } 188 return false; 189 } 190 } 191 return true; 192} 193 194} // namespace 195 196TEST_F(ExternalTaskTest, Start) { 197 const string kCommand = "/run/me"; 198 const vector<string> kCommandOptions{"arg1", "arg2"}; 199 const map<string, string> kCommandEnv{{"env1", "val1"}, {"env2", "val2"}}; 200 map<string, string> expected_env; 201 expected_env.emplace(kRPCTaskServiceVariable, RPCTaskMockAdaptor::kRpcConnId); 202 expected_env.emplace(kRPCTaskPathVariable, RPCTaskMockAdaptor::kRpcId); 203 expected_env.insert(kCommandEnv.begin(), kCommandEnv.end()); 204 const int kPID = 234678; 205 EXPECT_CALL(process_manager_, 206 StartProcess(_, base::FilePath(kCommand), kCommandOptions, 207 expected_env, false, _)) 208 .WillOnce(Return(-1)) 209 .WillOnce(Return(kPID)); 210 Error error; 211 EXPECT_FALSE(external_task_->Start( 212 base::FilePath(kCommand), kCommandOptions, kCommandEnv, false, &error)); 213 EXPECT_EQ(Error::kInternalError, error.type()); 214 EXPECT_FALSE(external_task_->rpc_task_); 215 216 error.Reset(); 217 EXPECT_TRUE(external_task_->Start( 218 base::FilePath(kCommand), kCommandOptions, kCommandEnv, false, &error)); 219 EXPECT_TRUE(error.IsSuccess()); 220 EXPECT_EQ(kPID, external_task_->pid_); 221 EXPECT_NE(nullptr, external_task_->rpc_task_); 222} 223 224TEST_F(ExternalTaskTest, Stop) { 225 const unsigned int kTag = 123; 226 const int kPID = 123456; 227 FakeUpRunningProcess(kTag, kPID); 228 ExpectStop(kTag, kPID); 229 external_task_->Stop(); 230 ASSERT_NE(nullptr, external_task_); 231 VerifyStop(); 232} 233 234TEST_F(ExternalTaskTest, StopNotStarted) { 235 EXPECT_CALL(process_manager_, StopProcess(_)).Times(0); 236 external_task_->Stop(); 237 EXPECT_FALSE(test_rpc_task_destroyed_); 238} 239 240TEST_F(ExternalTaskTest, GetLogin) { 241 string username; 242 string password; 243 EXPECT_CALL(*this, GetLogin(&username, &password)); 244 EXPECT_CALL(*this, Notify(_, _)).Times(0); 245 external_task_->GetLogin(&username, &password); 246} 247 248TEST_F(ExternalTaskTest, Notify) { 249 const string kReason("you may already have won!"); 250 const map<string, string>& kArgs{ 251 {"arg1", "val1"}, 252 {"arg2", "val2"}}; 253 EXPECT_CALL(*this, GetLogin(_, _)).Times(0); 254 EXPECT_CALL(*this, Notify(kReason, kArgs)); 255 external_task_->Notify(kReason, kArgs); 256} 257 258TEST_F(ExternalTaskTest, OnTaskDied) { 259 const int kPID = 99999; 260 const int kExitStatus = 1; 261 external_task_->pid_ = kPID; 262 EXPECT_CALL(process_manager_, StopProcess(_)).Times(0); 263 EXPECT_CALL(*this, TaskDiedCallback(kPID, kExitStatus)); 264 external_task_->OnTaskDied(kExitStatus); 265 EXPECT_EQ(0, external_task_->pid_); 266} 267 268} // namespace shill 269