• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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