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