• 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/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