blob: 019f98ce36c87f6196afd2a26954cb09bf4edf18 [file] [log] [blame]
Gary Morainf80ef062012-05-16 14:57:04 -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/hook_table.h"
6
Ben Chancd477322014-10-17 14:19:30 -07007#include <memory>
Gary Moraina9fb3252012-05-31 12:05:31 -07008#include <string>
9
Gary Morainf80ef062012-05-16 14:57:04 -070010#include <base/bind.h>
Ben Chana0ddf462014-02-06 11:32:42 -080011#include <base/message_loop/message_loop.h>
Gary Morainf80ef062012-05-16 14:57:04 -070012
13#include "shill/error.h"
Gary Moraina9fb3252012-05-31 12:05:31 -070014#include "shill/event_dispatcher.h"
Ben Chanbe277dd2014-02-05 17:26:47 -080015#include "shill/testing.h"
Gary Morainf80ef062012-05-16 14:57:04 -070016
Ben Chana0ddf462014-02-06 11:32:42 -080017using base::Bind;
Gary Morainf80ef062012-05-16 14:57:04 -070018using base::Closure;
Gary Morainf80ef062012-05-16 14:57:04 -070019using base::Unretained;
Gary Moraina9fb3252012-05-31 12:05:31 -070020using std::string;
Gary Morainf80ef062012-05-16 14:57:04 -070021using ::testing::_;
22using ::testing::InSequence;
23using ::testing::Return;
24using ::testing::SaveArg;
25
26namespace shill {
27
Ben Chan255f5652014-06-03 22:14:15 -070028namespace {
29
30const char kName[] = "test";
31const char kName1[] = "test1";
32const char kName2[] = "test2";
33const char kName3[] = "test3";
34
35} // namespace
36
Gary Morainf80ef062012-05-16 14:57:04 -070037class HookTableTest : public testing::Test {
38 public:
39 MOCK_METHOD0(StartAction, void());
Gary Moraina9fb3252012-05-31 12:05:31 -070040 MOCK_METHOD0(StartAction2, void());
Gary Morainf80ef062012-05-16 14:57:04 -070041 MOCK_METHOD1(DoneAction, void(const Error &));
42
43 protected:
44 HookTableTest()
45 : hook_table_(&event_dispatcher_) {}
46
Ben Chan3fbf8bd2014-06-07 20:49:52 -070047 ResultCallback *GetDoneCallback() { return &hook_table_.done_callback_; }
Darin Petkov3ec55342012-09-28 14:04:44 +020048
Gary Moraina9fb3252012-05-31 12:05:31 -070049 EventDispatcher event_dispatcher_;
Gary Morainf80ef062012-05-16 14:57:04 -070050 HookTable hook_table_;
51};
52
Gary Moraina9fb3252012-05-31 12:05:31 -070053TEST_F(HookTableTest, ActionCompletes) {
Gary Morainf80ef062012-05-16 14:57:04 -070054 EXPECT_CALL(*this, StartAction());
Gary Morainf80ef062012-05-16 14:57:04 -070055 EXPECT_CALL(*this, DoneAction(IsSuccess()));
Ben Chan3fbf8bd2014-06-07 20:49:52 -070056 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
57 ResultCallback done_callback =
58 Bind(&HookTableTest::DoneAction, Unretained(this));
59 hook_table_.Add(kName, start_callback);
60 hook_table_.Run(0, done_callback);
Gary Moraina9fb3252012-05-31 12:05:31 -070061 hook_table_.ActionComplete(kName);
62
63 // Ensure that the timeout callback got cancelled. If it did not get
Ben Chan3fbf8bd2014-06-07 20:49:52 -070064 // cancelled, done_callback will be run twice and make this test fail.
Gary Moraina9fb3252012-05-31 12:05:31 -070065 event_dispatcher_.DispatchPendingEvents();
Gary Morainf80ef062012-05-16 14:57:04 -070066}
67
Gary Moraina9fb3252012-05-31 12:05:31 -070068ACTION_P2(CompleteAction, hook_table, name) {
69 hook_table->ActionComplete(name);
70}
71
Ben Chan255f5652014-06-03 22:14:15 -070072ACTION_P2(CompleteActionAndRemoveAction, hook_table, name) {
73 hook_table->ActionComplete(name);
74 hook_table->Remove(name);
75}
76
77TEST_F(HookTableTest, ActionCompletesAndRemovesActionInDoneCallback) {
78 EXPECT_CALL(*this, StartAction())
79 .WillOnce(CompleteActionAndRemoveAction(&hook_table_, kName));
80 EXPECT_CALL(*this, StartAction2())
81 .WillOnce(CompleteAction(&hook_table_, kName2));
82 EXPECT_CALL(*this, DoneAction(IsSuccess()));
Ben Chan3fbf8bd2014-06-07 20:49:52 -070083 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
84 Closure start2_callback =
85 Bind(&HookTableTest::StartAction2, Unretained(this));
86 ResultCallback done_callback =
87 Bind(&HookTableTest::DoneAction, Unretained(this));
88 hook_table_.Add(kName, start_callback);
89 hook_table_.Add(kName2, start2_callback);
90 hook_table_.Run(0, done_callback);
Ben Chan255f5652014-06-03 22:14:15 -070091
92 // Ensure that the timeout callback got cancelled. If it did not get
Ben Chan3fbf8bd2014-06-07 20:49:52 -070093 // cancelled, done_callback will be run twice and make this test fail.
Ben Chan255f5652014-06-03 22:14:15 -070094 event_dispatcher_.DispatchPendingEvents();
95}
96
Gary Moraina9fb3252012-05-31 12:05:31 -070097TEST_F(HookTableTest, ActionCompletesInline) {
98 // StartAction completes immediately before HookTable::Run() returns.
99 EXPECT_CALL(*this, StartAction())
100 .WillOnce(CompleteAction(&hook_table_, kName));
101 EXPECT_CALL(*this, DoneAction(IsSuccess()));
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700102 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
103 ResultCallback done_callback =
104 Bind(&HookTableTest::DoneAction, Unretained(this));
105 hook_table_.Add(kName, start_callback);
106 hook_table_.Run(0, done_callback);
Gary Morainf80ef062012-05-16 14:57:04 -0700107
Gary Moraina9fb3252012-05-31 12:05:31 -0700108 // Ensure that the timeout callback got cancelled. If it did not get
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700109 // cancelled, done_callback will be run twice and make this test fail.
Gary Moraina9fb3252012-05-31 12:05:31 -0700110 event_dispatcher_.DispatchPendingEvents();
Gary Morainf80ef062012-05-16 14:57:04 -0700111}
112
113TEST_F(HookTableTest, ActionTimesOut) {
Gary Moraina9fb3252012-05-31 12:05:31 -0700114 const int kTimeout = 1;
Gary Morainf80ef062012-05-16 14:57:04 -0700115 EXPECT_CALL(*this, StartAction());
Gary Morainf80ef062012-05-16 14:57:04 -0700116 EXPECT_CALL(*this, DoneAction(IsFailure()));
117
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700118 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
119 ResultCallback done_callback =
120 Bind(&HookTableTest::DoneAction, Unretained(this));
Gary Morainf80ef062012-05-16 14:57:04 -0700121
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700122 hook_table_.Add(kName, start_callback);
123 hook_table_.Run(kTimeout, done_callback);
Gary Moraina9fb3252012-05-31 12:05:31 -0700124
125 // Cause the event dispatcher to exit after kTimeout + 1 ms.
Ben Chana0ddf462014-02-06 11:32:42 -0800126 event_dispatcher_.PostDelayedTask(base::MessageLoop::QuitClosure(),
Darin Petkov3ec55342012-09-28 14:04:44 +0200127 kTimeout + 1);
Gary Moraina9fb3252012-05-31 12:05:31 -0700128 event_dispatcher_.DispatchForever();
Darin Petkov3ec55342012-09-28 14:04:44 +0200129 EXPECT_TRUE(GetDoneCallback()->is_null());
Gary Morainf80ef062012-05-16 14:57:04 -0700130}
131
132TEST_F(HookTableTest, MultipleActionsAllSucceed) {
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700133 Closure pending_callback;
Gary Morainf80ef062012-05-16 14:57:04 -0700134 const int kTimeout = 10;
Gary Moraina9fb3252012-05-31 12:05:31 -0700135 EXPECT_CALL(*this, StartAction()).Times(2);
136
137 // StartAction2 completes immediately before HookTable::Run() returns.
138 EXPECT_CALL(*this, StartAction2())
139 .WillOnce(CompleteAction(&hook_table_, kName1));
Gary Morainf80ef062012-05-16 14:57:04 -0700140 EXPECT_CALL(*this, DoneAction(IsSuccess()));
141
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700142 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
143 Closure start2_callback =
144 Bind(&HookTableTest::StartAction2, Unretained(this));
145 ResultCallback done_callback =
146 Bind(&HookTableTest::DoneAction, Unretained(this));
Gary Morainf80ef062012-05-16 14:57:04 -0700147
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700148 hook_table_.Add(kName1, start2_callback);
149 hook_table_.Add(kName2, start_callback);
150 hook_table_.Add(kName3, start_callback);
151 hook_table_.Run(kTimeout, done_callback);
Gary Moraina9fb3252012-05-31 12:05:31 -0700152 hook_table_.ActionComplete(kName2);
153 hook_table_.ActionComplete(kName3);
Gary Morainf80ef062012-05-16 14:57:04 -0700154}
155
156TEST_F(HookTableTest, MultipleActionsAndOneTimesOut) {
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700157 Closure pending_callback;
Gary Moraina9fb3252012-05-31 12:05:31 -0700158 const int kTimeout = 1;
Gary Morainf80ef062012-05-16 14:57:04 -0700159 EXPECT_CALL(*this, StartAction()).Times(3);
Gary Morainf80ef062012-05-16 14:57:04 -0700160 EXPECT_CALL(*this, DoneAction(IsFailure()));
161
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700162 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
163 ResultCallback done_callback =
164 Bind(&HookTableTest::DoneAction, Unretained(this));
Gary Morainf80ef062012-05-16 14:57:04 -0700165
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700166 hook_table_.Add(kName1, start_callback);
167 hook_table_.Add(kName2, start_callback);
168 hook_table_.Add(kName3, start_callback);
169 hook_table_.Run(kTimeout, done_callback);
Gary Moraina9fb3252012-05-31 12:05:31 -0700170 hook_table_.ActionComplete(kName1);
171 hook_table_.ActionComplete(kName3);
172 // Cause the event dispatcher to exit after kTimeout + 1 ms.
Ben Chana0ddf462014-02-06 11:32:42 -0800173 event_dispatcher_.PostDelayedTask(base::MessageLoop::QuitClosure(),
Gary Moraina9fb3252012-05-31 12:05:31 -0700174 kTimeout + 1);
175 event_dispatcher_.DispatchForever();
176}
177
178TEST_F(HookTableTest, AddActionsWithSameName) {
179 EXPECT_CALL(*this, StartAction()).Times(0);
180 EXPECT_CALL(*this, StartAction2());
181 EXPECT_CALL(*this, DoneAction(IsSuccess()));
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700182 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
183 Closure start2_callback =
184 Bind(&HookTableTest::StartAction2, Unretained(this));
185 ResultCallback done_callback =
186 Bind(&HookTableTest::DoneAction, Unretained(this));
187 hook_table_.Add(kName, start_callback);
Gary Moraina9fb3252012-05-31 12:05:31 -0700188
189 // Adding an action with the same name kName. New callbacks should replace
190 // old ones.
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700191 hook_table_.Add(kName, start2_callback);
192 hook_table_.Run(0, done_callback);
Gary Moraina9fb3252012-05-31 12:05:31 -0700193 hook_table_.ActionComplete(kName);
194
195 // Ensure that the timeout callback got cancelled. If it did not get
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700196 // cancelled, done_callback will be run twice and make this test fail.
Gary Moraina9fb3252012-05-31 12:05:31 -0700197 event_dispatcher_.DispatchPendingEvents();
198}
199
200TEST_F(HookTableTest, RemoveAction) {
201 EXPECT_CALL(*this, StartAction()).Times(0);
202 EXPECT_CALL(*this, DoneAction(IsSuccess()));
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700203 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
204 ResultCallback done_callback =
205 Bind(&HookTableTest::DoneAction, Unretained(this));
206 hook_table_.Add(kName, start_callback);
Gary Moraina9fb3252012-05-31 12:05:31 -0700207 hook_table_.Remove(kName);
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700208 hook_table_.Run(0, done_callback);
Gary Moraina9fb3252012-05-31 12:05:31 -0700209}
210
211TEST_F(HookTableTest, ActionCompleteFollowedByRemove) {
212 EXPECT_CALL(*this, StartAction()).Times(0);
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700213 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
214 hook_table_.Add(kName, start_callback);
Gary Moraina9fb3252012-05-31 12:05:31 -0700215 hook_table_.ActionComplete(kName);
216 hook_table_.Remove(kName);
217}
218
Darin Petkov3ec55342012-09-28 14:04:44 +0200219TEST_F(HookTableTest, IsEmpty) {
220 EXPECT_TRUE(hook_table_.IsEmpty());
221 hook_table_.Add(kName, Closure());
222 EXPECT_FALSE(hook_table_.IsEmpty());
223 hook_table_.Remove(kName);
224 EXPECT_TRUE(hook_table_.IsEmpty());
225}
226
Gary Moraina9fb3252012-05-31 12:05:31 -0700227class SomeClass : public base::RefCounted<SomeClass> {
228 public:
229 SomeClass() {}
230 void StartAction() {}
231
232 private:
233 DISALLOW_COPY_AND_ASSIGN(SomeClass);
234};
235
236// This test verifies that a class that removes itself from a hook table upon
237// destruction does not crash if the hook table is destroyed first.
238TEST_F(HookTableTest, RefcountedObject) {
Ben Chancd477322014-10-17 14:19:30 -0700239 std::unique_ptr<HookTable> ht(new HookTable(&event_dispatcher_));
Gary Moraina9fb3252012-05-31 12:05:31 -0700240 {
241 scoped_refptr<SomeClass> ref_counted_object = new SomeClass();
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700242 Closure start_callback = Bind(&SomeClass::StartAction, ref_counted_object);
243 ht->Add(kName, start_callback);
Gary Morainf80ef062012-05-16 14:57:04 -0700244 }
245}
246
Gary Moraina9fb3252012-05-31 12:05:31 -0700247TEST_F(HookTableTest, ActionAddedBeforePreviousActionCompletes) {
248 EXPECT_CALL(*this, StartAction());
249 EXPECT_CALL(*this, StartAction2()).Times(0);
250 EXPECT_CALL(*this, DoneAction(IsSuccess()));
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700251 Closure start_callback = Bind(&HookTableTest::StartAction, Unretained(this));
252 Closure start2_callback =
253 Bind(&HookTableTest::StartAction2, Unretained(this));
254 ResultCallback done_callback =
255 Bind(&HookTableTest::DoneAction, Unretained(this));
256 hook_table_.Add(kName, start_callback);
257 hook_table_.Run(0, done_callback);
Gary Moraina9fb3252012-05-31 12:05:31 -0700258
259 // An action with the same name is added before the previous actions complete.
260 // It should not be run.
Ben Chan3fbf8bd2014-06-07 20:49:52 -0700261 hook_table_.Add(kName, start2_callback);
Gary Moraina9fb3252012-05-31 12:05:31 -0700262 hook_table_.ActionComplete(kName);
263}
264
Gary Morainf80ef062012-05-16 14:57:04 -0700265} // namespace shill