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