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