blob: 7922d641338e2fa8f9da5f79896a91964f1013ea [file] [log] [blame]
mukesh agrawalae30e9e2013-05-28 14:09:16 -07001// 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 <set>
9#include <string>
10#include <vector>
11
12#include <base/bind.h>
13#include <base/file_path.h>
14#include <base/memory/weak_ptr.h>
15#include <base/string_util.h>
16#include <gmock/gmock.h>
17#include <gtest/gtest.h>
18
19#include "shill/event_dispatcher.h"
20#include "shill/nice_mock_control.h"
21#include "shill/mock_adaptors.h"
22#include "shill/mock_glib.h"
23#include "shill/mock_process_killer.h"
24
25using std::map;
26using std::set;
27using std::string;
28using std::vector;
29using testing::_;
30using testing::Matcher;
31using testing::MatchesRegex;
32using testing::NiceMock;
33using testing::Return;
34using testing::SetArgumentPointee;
35using testing::StrEq;
36
37namespace shill {
38
39class ExternalTaskTest : public testing::Test,
40 public RPCTaskDelegate {
41 public:
42 ExternalTaskTest()
43 : weak_ptr_factory_(this),
44 death_callback_(
45 base::Bind(&ExternalTaskTest::TaskDiedCallback,
46 weak_ptr_factory_.GetWeakPtr())),
47 external_task_(
48 new ExternalTask(&control_, &glib_, weak_ptr_factory_.GetWeakPtr(),
49 death_callback_)),
50 test_rpc_task_destroyed_(false) {
51 external_task_->process_killer_ = &process_killer_;
52 }
53
54 virtual ~ExternalTaskTest() {}
55
56 virtual void TearDown() {
57 if (!external_task_) {
58 return;
59 }
60
61 if (external_task_->child_watch_tag_) {
62 EXPECT_CALL(glib_, SourceRemove(external_task_->child_watch_tag_));
63 }
64
65 if (external_task_->pid_) {
66 EXPECT_CALL(process_killer_, Kill(external_task_->pid_, _));
67 }
68 }
69
70 void set_test_rpc_task_destroyed(bool destroyed) {
71 test_rpc_task_destroyed_ = destroyed;
72 }
73
74 protected:
75 // Implements RPCTaskDelegate interface.
76 MOCK_METHOD2(GetLogin, void(string *user, string *password));
77 MOCK_METHOD2(Notify, void(const string &reason,
78 const map<string, string> &dict));
79
80 MOCK_METHOD2(TaskDiedCallback, void(pid_t dead_process, int exit_status));
81
82 NiceMockControl control_;
83 EventDispatcher dispatcher_;
84 MockGLib glib_;
85 MockProcessKiller process_killer_;
86 base::WeakPtrFactory<ExternalTaskTest> weak_ptr_factory_;
87 base::Callback<void(pid_t, int)> death_callback_;
88 scoped_ptr<ExternalTask> external_task_;
89 bool test_rpc_task_destroyed_;
90};
91
92namespace {
93
94class TestRPCTask : public RPCTask {
95 public:
96 TestRPCTask(ControlInterface *control, ExternalTaskTest *test);
97 virtual ~TestRPCTask();
98
99 private:
100 ExternalTaskTest *test_;
101};
102
103TestRPCTask::TestRPCTask(ControlInterface *control, ExternalTaskTest *test)
104 : RPCTask(control, test),
105 test_(test) {
106 test_->set_test_rpc_task_destroyed(false);
107}
108
109TestRPCTask::~TestRPCTask() {
110 test_->set_test_rpc_task_destroyed(true);
111 test_ = NULL;
112}
113
114} // namespace
115
116TEST_F(ExternalTaskTest, Destructor) {
117 const unsigned int kTag = 123;
118 external_task_->child_watch_tag_ = kTag;
119 EXPECT_CALL(glib_, SourceRemove(kTag));
120 const int kPID = 123456;
121 external_task_->pid_ = kPID;
122 EXPECT_CALL(process_killer_, Kill(kPID, _));
123 external_task_->rpc_task_.reset(new TestRPCTask(&control_, this));
124 external_task_.reset();
125 EXPECT_TRUE(test_rpc_task_destroyed_);
126}
127
128namespace {
129
130// Returns true iff. there is at least one anchored match in |arg|,
131// for each item in |expected_values|. Order of items does not matter.
132//
133// |arg| is a NULL-terminated array of C-strings.
134// |expected_values| is a container of regular expressions (as strings).
135MATCHER_P(HasElementsMatching, expected_values, "") {
136 for (const auto &expected_value : expected_values) {
137 auto regex_matcher(MatchesRegex(expected_value).impl());
138 char **arg_local = arg;
139 while (*arg_local) {
140 if (regex_matcher.MatchAndExplain(*arg_local, result_listener)) {
141 break;
142 }
143 ++arg_local;
144 }
145 if (*arg_local == NULL) {
146 *result_listener << "missing value " << expected_value << "\n";
147 arg_local = arg;
148 while (*arg_local) {
149 *result_listener << "received: " << *arg_local << "\n";
150 ++arg_local;
151 }
152 return false;
153 }
154 }
155 return true;
156}
157
158} // namespace
159
160TEST_F(ExternalTaskTest, Start) {
161 const string kCommand = "/run/me";
162 const vector<string> kCommandOptions{"arg1", "arg2"};
163 const map<string, string> kCommandEnv{{"env1", "val1"}, {"env2", "val2"}};
164 const vector<string> kExpectedEnv{"SHILL_TASK_SERVICE=.+",
165 "SHILL_TASK_PATH=.+", "env1=val1",
166 "env2=val2"};
167 const int kPID = 234678;
168 EXPECT_CALL(glib_, SpawnAsync(_,
169 HasElementsMatching(kCommandOptions),
170 HasElementsMatching(kExpectedEnv),
171 _, _, _, _, _))
172 .WillOnce(Return(false))
173 .WillOnce(DoAll(SetArgumentPointee<6>(kPID), Return(true)));
174 const int kTag = 6;
175 Error error;
176 EXPECT_CALL(glib_,
177 ChildWatchAdd(
178 kPID, &external_task_->OnTaskDied, external_task_.get()))
179 .WillOnce(Return(kTag));
180 EXPECT_FALSE(
181 external_task_->Start(
182 base::FilePath(kCommand), kCommandOptions, kCommandEnv, &error));
183 EXPECT_EQ(Error::kInternalError, error.type());
184 EXPECT_FALSE(external_task_->rpc_task_);
185
186 error.Reset();
187 EXPECT_TRUE(
188 external_task_->Start(
189 base::FilePath(kCommand), kCommandOptions, kCommandEnv, &error));
190 EXPECT_TRUE(error.IsSuccess());
191 EXPECT_EQ(kPID, external_task_->pid_);
192 EXPECT_EQ(kTag, external_task_->child_watch_tag_);
193 EXPECT_TRUE(external_task_->rpc_task_);
194}
195
196TEST_F(ExternalTaskTest, Stop) {
197 const unsigned int kTag = 123;
198 external_task_->child_watch_tag_ = kTag;
199 EXPECT_CALL(glib_, SourceRemove(kTag));
200 const int kPID = 123456;
201 external_task_->pid_ = kPID;
202 EXPECT_CALL(process_killer_, Kill(kPID, _));
203 external_task_->rpc_task_.reset(new TestRPCTask(&control_, this));
204 external_task_->Stop();
205
206 EXPECT_EQ(0, external_task_->child_watch_tag_);
207 EXPECT_EQ(0, external_task_->pid_);
208 EXPECT_FALSE(external_task_->rpc_task_);
209 EXPECT_TRUE(test_rpc_task_destroyed_);
210}
211
212TEST_F(ExternalTaskTest, StopNotStarted) {
213 EXPECT_CALL(glib_, SourceRemove(_)).Times(0);
214 EXPECT_CALL(process_killer_, Kill(_, _)).Times(0);
215 external_task_->Stop();
216 EXPECT_FALSE(test_rpc_task_destroyed_);
217}
218
219TEST_F(ExternalTaskTest, GetLogin) {
220 string username;
221 string password;
222 EXPECT_CALL(*this, GetLogin(&username, &password));
223 EXPECT_CALL(*this, Notify(_, _)).Times(0);
224 external_task_->GetLogin(&username, &password);
225}
226
227TEST_F(ExternalTaskTest, Notify) {
228 const string kReason("you may already have won!");
229 const map<string, string> &kArgs{
230 {"arg1", "val1"},
231 {"arg2", "val2"}};
232 EXPECT_CALL(*this, GetLogin(_, _)).Times(0);
233 EXPECT_CALL(*this, Notify(kReason, kArgs));
234 external_task_->Notify(kReason, kArgs);
235}
236
237TEST_F(ExternalTaskTest, OnTaskDied) {
238 const int kPID = 99999;
239 const int kExitStatus = 1;
240 external_task_->child_watch_tag_ = 333;
241 external_task_->pid_ = kPID;
242 EXPECT_CALL(process_killer_, Kill(_, _)).Times(0);
243 EXPECT_CALL(*this, TaskDiedCallback(kPID, kExitStatus));
244 ExternalTask::OnTaskDied(kPID, kExitStatus, external_task_.get());
245 EXPECT_EQ(0, external_task_->child_watch_tag_);
246 EXPECT_EQ(0, external_task_->pid_);
247}
248
249} // namespace shill