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/hook_table.h"
18
19 #include <memory>
20 #include <string>
21
22 #include <base/bind.h>
23 #include <base/message_loop/message_loop.h>
24
25 #include "shill/error.h"
26 #include "shill/test_event_dispatcher.h"
27 #include "shill/testing.h"
28
29 using base::Bind;
30 using base::Closure;
31 using base::Unretained;
32 using std::string;
33 using ::testing::_;
34 using ::testing::InSequence;
35 using ::testing::Return;
36 using ::testing::SaveArg;
37
38 namespace shill {
39
40 namespace {
41
42 const char kName[] = "test";
43 const char kName1[] = "test1";
44 const char kName2[] = "test2";
45 const char kName3[] = "test3";
46
47 } // namespace
48
49 class HookTableTest : public testing::Test {
50 public:
51 MOCK_METHOD0(StartAction, void());
52 MOCK_METHOD0(StartAction2, void());
53 MOCK_METHOD1(DoneAction, void(const Error&));
54
55 protected:
HookTableTest()56 HookTableTest()
57 : hook_table_(&event_dispatcher_) {}
58
GetDoneCallback()59 ResultCallback* GetDoneCallback() { return &hook_table_.done_callback_; }
60
61 EventDispatcherForTest event_dispatcher_;
62 HookTable hook_table_;
63 };
64
TEST_F(HookTableTest,ActionCompletes)65 TEST_F(HookTableTest, ActionCompletes) {
66 EXPECT_CALL(*this, StartAction());
67 EXPECT_CALL(*this, DoneAction(IsSuccess()));
68 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
69 ResultCallback done_callback =
70 Bind(&HookTableTest::DoneAction, Unretained(this));
71 hook_table_.Add(kName, start_callback);
72 hook_table_.Run(0, done_callback);
73 hook_table_.ActionComplete(kName);
74
75 // Ensure that the timeout callback got cancelled. If it did not get
76 // cancelled, done_callback will be run twice and make this test fail.
77 event_dispatcher_.DispatchPendingEvents();
78 }
79
ACTION_P2(CompleteAction,hook_table,name)80 ACTION_P2(CompleteAction, hook_table, name) {
81 hook_table->ActionComplete(name);
82 }
83
ACTION_P2(CompleteActionAndRemoveAction,hook_table,name)84 ACTION_P2(CompleteActionAndRemoveAction, hook_table, name) {
85 hook_table->ActionComplete(name);
86 hook_table->Remove(name);
87 }
88
TEST_F(HookTableTest,ActionCompletesAndRemovesActionInDoneCallback)89 TEST_F(HookTableTest, ActionCompletesAndRemovesActionInDoneCallback) {
90 EXPECT_CALL(*this, StartAction())
91 .WillOnce(CompleteActionAndRemoveAction(&hook_table_, kName));
92 EXPECT_CALL(*this, StartAction2())
93 .WillOnce(CompleteAction(&hook_table_, kName2));
94 EXPECT_CALL(*this, DoneAction(IsSuccess()));
95 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
96 Closure start2_callback =
97 Bind(&HookTableTest::StartAction2, Unretained(this));
98 ResultCallback done_callback =
99 Bind(&HookTableTest::DoneAction, Unretained(this));
100 hook_table_.Add(kName, start_callback);
101 hook_table_.Add(kName2, start2_callback);
102 hook_table_.Run(0, done_callback);
103
104 // Ensure that the timeout callback got cancelled. If it did not get
105 // cancelled, done_callback will be run twice and make this test fail.
106 event_dispatcher_.DispatchPendingEvents();
107 }
108
TEST_F(HookTableTest,ActionCompletesInline)109 TEST_F(HookTableTest, ActionCompletesInline) {
110 // StartAction completes immediately before HookTable::Run() returns.
111 EXPECT_CALL(*this, StartAction())
112 .WillOnce(CompleteAction(&hook_table_, kName));
113 EXPECT_CALL(*this, DoneAction(IsSuccess()));
114 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
115 ResultCallback done_callback =
116 Bind(&HookTableTest::DoneAction, Unretained(this));
117 hook_table_.Add(kName, start_callback);
118 hook_table_.Run(0, done_callback);
119
120 // Ensure that the timeout callback got cancelled. If it did not get
121 // cancelled, done_callback will be run twice and make this test fail.
122 event_dispatcher_.DispatchPendingEvents();
123 }
124
TEST_F(HookTableTest,ActionTimesOut)125 TEST_F(HookTableTest, ActionTimesOut) {
126 const int kTimeout = 1;
127 EXPECT_CALL(*this, StartAction());
128 EXPECT_CALL(*this, DoneAction(IsFailure()));
129
130 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
131 ResultCallback done_callback =
132 Bind(&HookTableTest::DoneAction, Unretained(this));
133
134 hook_table_.Add(kName, start_callback);
135 hook_table_.Run(kTimeout, done_callback);
136
137 // Cause the event dispatcher to exit after kTimeout + 1 ms.
138 event_dispatcher_.PostDelayedTask(base::MessageLoop::QuitWhenIdleClosure(),
139 kTimeout + 1);
140 event_dispatcher_.DispatchForever();
141 EXPECT_TRUE(GetDoneCallback()->is_null());
142 }
143
TEST_F(HookTableTest,MultipleActionsAllSucceed)144 TEST_F(HookTableTest, MultipleActionsAllSucceed) {
145 Closure pending_callback;
146 const int kTimeout = 10;
147 EXPECT_CALL(*this, StartAction()).Times(2);
148
149 // StartAction2 completes immediately before HookTable::Run() returns.
150 EXPECT_CALL(*this, StartAction2())
151 .WillOnce(CompleteAction(&hook_table_, kName1));
152 EXPECT_CALL(*this, DoneAction(IsSuccess()));
153
154 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
155 Closure start2_callback =
156 Bind(&HookTableTest::StartAction2, Unretained(this));
157 ResultCallback done_callback =
158 Bind(&HookTableTest::DoneAction, Unretained(this));
159
160 hook_table_.Add(kName1, start2_callback);
161 hook_table_.Add(kName2, start_callback);
162 hook_table_.Add(kName3, start_callback);
163 hook_table_.Run(kTimeout, done_callback);
164 hook_table_.ActionComplete(kName2);
165 hook_table_.ActionComplete(kName3);
166 }
167
TEST_F(HookTableTest,MultipleActionsAndOneTimesOut)168 TEST_F(HookTableTest, MultipleActionsAndOneTimesOut) {
169 Closure pending_callback;
170 const int kTimeout = 1;
171 EXPECT_CALL(*this, StartAction()).Times(3);
172 EXPECT_CALL(*this, DoneAction(IsFailure()));
173
174 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
175 ResultCallback done_callback =
176 Bind(&HookTableTest::DoneAction, Unretained(this));
177
178 hook_table_.Add(kName1, start_callback);
179 hook_table_.Add(kName2, start_callback);
180 hook_table_.Add(kName3, start_callback);
181 hook_table_.Run(kTimeout, done_callback);
182 hook_table_.ActionComplete(kName1);
183 hook_table_.ActionComplete(kName3);
184 // Cause the event dispatcher to exit after kTimeout + 1 ms.
185 event_dispatcher_.PostDelayedTask(base::MessageLoop::QuitWhenIdleClosure(),
186 kTimeout + 1);
187 event_dispatcher_.DispatchForever();
188 }
189
TEST_F(HookTableTest,AddActionsWithSameName)190 TEST_F(HookTableTest, AddActionsWithSameName) {
191 EXPECT_CALL(*this, StartAction()).Times(0);
192 EXPECT_CALL(*this, StartAction2());
193 EXPECT_CALL(*this, DoneAction(IsSuccess()));
194 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
195 Closure start2_callback =
196 Bind(&HookTableTest::StartAction2, Unretained(this));
197 ResultCallback done_callback =
198 Bind(&HookTableTest::DoneAction, Unretained(this));
199 hook_table_.Add(kName, start_callback);
200
201 // Adding an action with the same name kName. New callbacks should replace
202 // old ones.
203 hook_table_.Add(kName, start2_callback);
204 hook_table_.Run(0, done_callback);
205 hook_table_.ActionComplete(kName);
206
207 // Ensure that the timeout callback got cancelled. If it did not get
208 // cancelled, done_callback will be run twice and make this test fail.
209 event_dispatcher_.DispatchPendingEvents();
210 }
211
TEST_F(HookTableTest,RemoveAction)212 TEST_F(HookTableTest, RemoveAction) {
213 EXPECT_CALL(*this, StartAction()).Times(0);
214 EXPECT_CALL(*this, DoneAction(IsSuccess()));
215 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
216 ResultCallback done_callback =
217 Bind(&HookTableTest::DoneAction, Unretained(this));
218 hook_table_.Add(kName, start_callback);
219 hook_table_.Remove(kName);
220 hook_table_.Run(0, done_callback);
221 }
222
TEST_F(HookTableTest,ActionCompleteFollowedByRemove)223 TEST_F(HookTableTest, ActionCompleteFollowedByRemove) {
224 EXPECT_CALL(*this, StartAction()).Times(0);
225 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
226 hook_table_.Add(kName, start_callback);
227 hook_table_.ActionComplete(kName);
228 hook_table_.Remove(kName);
229 }
230
TEST_F(HookTableTest,IsEmpty)231 TEST_F(HookTableTest, IsEmpty) {
232 EXPECT_TRUE(hook_table_.IsEmpty());
233 hook_table_.Add(kName, Closure());
234 EXPECT_FALSE(hook_table_.IsEmpty());
235 hook_table_.Remove(kName);
236 EXPECT_TRUE(hook_table_.IsEmpty());
237 }
238
239 class SomeClass : public base::RefCounted<SomeClass> {
240 public:
SomeClass()241 SomeClass() {}
StartAction()242 void StartAction() {}
243
244 private:
245 DISALLOW_COPY_AND_ASSIGN(SomeClass);
246 };
247
248 // This test verifies that a class that removes itself from a hook table upon
249 // destruction does not crash if the hook table is destroyed first.
TEST_F(HookTableTest,RefcountedObject)250 TEST_F(HookTableTest, RefcountedObject) {
251 std::unique_ptr<HookTable> ht(new HookTable(&event_dispatcher_));
252 {
253 scoped_refptr<SomeClass> ref_counted_object = new SomeClass();
254 Closure start_callback = Bind(&SomeClass::StartAction, ref_counted_object);
255 ht->Add(kName, start_callback);
256 }
257 }
258
TEST_F(HookTableTest,ActionAddedBeforePreviousActionCompletes)259 TEST_F(HookTableTest, ActionAddedBeforePreviousActionCompletes) {
260 EXPECT_CALL(*this, StartAction());
261 EXPECT_CALL(*this, StartAction2()).Times(0);
262 EXPECT_CALL(*this, DoneAction(IsSuccess()));
263 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
264 Closure start2_callback =
265 Bind(&HookTableTest::StartAction2, Unretained(this));
266 ResultCallback done_callback =
267 Bind(&HookTableTest::DoneAction, Unretained(this));
268 hook_table_.Add(kName, start_callback);
269 hook_table_.Run(0, done_callback);
270
271 // An action with the same name is added before the previous actions complete.
272 // It should not be run.
273 hook_table_.Add(kName, start2_callback);
274 hook_table_.ActionComplete(kName);
275 }
276
277 } // namespace shill
278