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:
ExternalTaskTest()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
~ExternalTaskTest()66 virtual ~ExternalTaskTest() {}
67
TearDown()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
set_test_rpc_task_destroyed(bool destroyed)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
ExpectStop(unsigned int tag,int pid)85 void ExpectStop(unsigned int tag, int pid) {
86 EXPECT_CALL(process_manager_, StopProcess(pid));
87 }
88
VerifyStop()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
TestRPCTask(ControlInterface * control,ExternalTaskTest * test)127 TestRPCTask::TestRPCTask(ControlInterface* control, ExternalTaskTest* test)
128 : RPCTask(control, test),
129 test_(test) {
130 test_->set_test_rpc_task_destroyed(false);
131 }
132
~TestRPCTask()133 TestRPCTask::~TestRPCTask() {
134 test_->set_test_rpc_task_destroyed(true);
135 test_ = nullptr;
136 }
137
138 } // namespace
139
FakeUpRunningProcess(unsigned int tag,int pid)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
TEST_F(ExternalTaskTest,Destructor)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
TEST_F(ExternalTaskTest,DestroyLater)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
TEST_F(ExternalTaskTest,Start)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
TEST_F(ExternalTaskTest,Stop)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
TEST_F(ExternalTaskTest,StopNotStarted)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
TEST_F(ExternalTaskTest,GetLogin)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
TEST_F(ExternalTaskTest,Notify)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
TEST_F(ExternalTaskTest,OnTaskDied)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