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 37 using std::map; 38 using std::set; 39 using std::string; 40 using std::vector; 41 using testing::_; 42 using testing::Matcher; 43 using testing::MatchesRegex; 44 using testing::Mock; 45 using testing::NiceMock; 46 using testing::Return; 47 using testing::SetArgumentPointee; 48 using testing::StrEq; 49 50 namespace shill { 51 52 class 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 116 namespace { 117 118 class TestRPCTask : public RPCTask { 119 public: 120 TestRPCTask(ControlInterface* control, ExternalTaskTest* test); 121 virtual ~TestRPCTask(); 122 123 private: 124 ExternalTaskTest* test_; 125 }; 126 127 TestRPCTask::TestRPCTask(ControlInterface* control, ExternalTaskTest* test) 128 : RPCTask(control, test), 129 test_(test) { 130 test_->set_test_rpc_task_destroyed(false); 131 } 132 133 TestRPCTask::~TestRPCTask() { 134 test_->set_test_rpc_task_destroyed(true); 135 test_ = nullptr; 136 } 137 138 } // namespace 139 140 void ExternalTaskTest::FakeUpRunningProcess(unsigned int tag, int pid) { 141 external_task_->pid_ = pid; 142 external_task_->rpc_task_.reset(new TestRPCTask(&control_, this)); 143 } 144 145 TEST_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 154 TEST_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 164 namespace { 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). 171 MATCHER_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 196 TEST_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 224 TEST_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 234 TEST_F(ExternalTaskTest, StopNotStarted) { 235 EXPECT_CALL(process_manager_, StopProcess(_)).Times(0); 236 external_task_->Stop(); 237 EXPECT_FALSE(test_rpc_task_destroyed_); 238 } 239 240 TEST_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 248 TEST_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 258 TEST_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