blob: ca815d095ca2ddfaee5d0219f3ca91b8b916c11c [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
Gary Moraina9fb3252012-05-31 12:05:31 -07007#include <string>
8
Gary Morainf80ef062012-05-16 14:57:04 -07009#include <base/bind.h>
10#include <base/callback.h>
Gary Moraina9fb3252012-05-31 12:05:31 -070011#include <base/message_loop.h>
Gary Morainf80ef062012-05-16 14:57:04 -070012#include <gmock/gmock.h>
13#include <gtest/gtest.h>
14
15#include "shill/error.h"
Gary Moraina9fb3252012-05-31 12:05:31 -070016#include "shill/event_dispatcher.h"
Gary Morainf80ef062012-05-16 14:57:04 -070017
18using base::Callback;
19using base::Closure;
20using base::Bind;
21using base::Unretained;
Gary Moraina9fb3252012-05-31 12:05:31 -070022using std::string;
Gary Morainf80ef062012-05-16 14:57:04 -070023using ::testing::_;
24using ::testing::InSequence;
25using ::testing::Return;
26using ::testing::SaveArg;
27
28namespace shill {
29
30class HookTableTest : public testing::Test {
31 public:
Gary Moraina9fb3252012-05-31 12:05:31 -070032 static const char kName[];
Gary Morainf80ef062012-05-16 14:57:04 -070033 MOCK_METHOD0(StartAction, void());
Gary Moraina9fb3252012-05-31 12:05:31 -070034 MOCK_METHOD0(StartAction2, void());
Gary Morainf80ef062012-05-16 14:57:04 -070035 MOCK_METHOD1(DoneAction, void(const Error &));
36
37 protected:
38 HookTableTest()
39 : hook_table_(&event_dispatcher_) {}
40
Darin Petkov3ec55342012-09-28 14:04:44 +020041 base::Callback<void(const Error &)> *GetDoneCallback() {
42 return &hook_table_.done_cb_;
43 }
44
Gary Moraina9fb3252012-05-31 12:05:31 -070045 EventDispatcher event_dispatcher_;
Gary Morainf80ef062012-05-16 14:57:04 -070046 HookTable hook_table_;
47};
48
Gary Moraina9fb3252012-05-31 12:05:31 -070049const char HookTableTest::kName[] = "test";
50
Gary Morainf80ef062012-05-16 14:57:04 -070051MATCHER(IsSuccess, "") {
52 return arg.IsSuccess();
53}
54
55MATCHER(IsFailure, "") {
56 return arg.IsFailure();
57}
58
Gary Moraina9fb3252012-05-31 12:05:31 -070059TEST_F(HookTableTest, ActionCompletes) {
Gary Morainf80ef062012-05-16 14:57:04 -070060 EXPECT_CALL(*this, StartAction());
Gary Morainf80ef062012-05-16 14:57:04 -070061 EXPECT_CALL(*this, DoneAction(IsSuccess()));
Gary Morainf80ef062012-05-16 14:57:04 -070062 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
Gary Morainf80ef062012-05-16 14:57:04 -070063 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
64 Unretained(this));
Gary Moraina9fb3252012-05-31 12:05:31 -070065 hook_table_.Add(kName, start_cb);
Gary Morainf80ef062012-05-16 14:57:04 -070066 hook_table_.Run(0, done_cb);
Gary Moraina9fb3252012-05-31 12:05:31 -070067 hook_table_.ActionComplete(kName);
68
69 // Ensure that the timeout callback got cancelled. If it did not get
70 // cancelled, done_cb will be run twice and make this test fail.
71 event_dispatcher_.DispatchPendingEvents();
Gary Morainf80ef062012-05-16 14:57:04 -070072}
73
Gary Moraina9fb3252012-05-31 12:05:31 -070074ACTION_P2(CompleteAction, hook_table, name) {
75 hook_table->ActionComplete(name);
76}
77
78TEST_F(HookTableTest, ActionCompletesInline) {
79 // StartAction completes immediately before HookTable::Run() returns.
80 EXPECT_CALL(*this, StartAction())
81 .WillOnce(CompleteAction(&hook_table_, kName));
82 EXPECT_CALL(*this, DoneAction(IsSuccess()));
Gary Morainf80ef062012-05-16 14:57:04 -070083 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
Gary Morainf80ef062012-05-16 14:57:04 -070084 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
85 Unretained(this));
Gary Moraina9fb3252012-05-31 12:05:31 -070086 hook_table_.Add(kName, start_cb);
87 hook_table_.Run(0, done_cb);
Gary Morainf80ef062012-05-16 14:57:04 -070088
Gary Moraina9fb3252012-05-31 12:05:31 -070089 // Ensure that the timeout callback got cancelled. If it did not get
90 // cancelled, done_cb will be run twice and make this test fail.
91 event_dispatcher_.DispatchPendingEvents();
Gary Morainf80ef062012-05-16 14:57:04 -070092}
93
94TEST_F(HookTableTest, ActionTimesOut) {
Gary Moraina9fb3252012-05-31 12:05:31 -070095 const int kTimeout = 1;
Gary Morainf80ef062012-05-16 14:57:04 -070096 EXPECT_CALL(*this, StartAction());
Gary Morainf80ef062012-05-16 14:57:04 -070097 EXPECT_CALL(*this, DoneAction(IsFailure()));
98
99 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
Gary Morainf80ef062012-05-16 14:57:04 -0700100 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
101 Unretained(this));
102
Gary Moraina9fb3252012-05-31 12:05:31 -0700103 hook_table_.Add(kName, start_cb);
Gary Morainf80ef062012-05-16 14:57:04 -0700104 hook_table_.Run(kTimeout, done_cb);
Gary Moraina9fb3252012-05-31 12:05:31 -0700105
106 // Cause the event dispatcher to exit after kTimeout + 1 ms.
107 event_dispatcher_.PostDelayedTask(MessageLoop::QuitClosure(),
Darin Petkov3ec55342012-09-28 14:04:44 +0200108 kTimeout + 1);
Gary Moraina9fb3252012-05-31 12:05:31 -0700109 event_dispatcher_.DispatchForever();
Darin Petkov3ec55342012-09-28 14:04:44 +0200110 EXPECT_TRUE(GetDoneCallback()->is_null());
Gary Morainf80ef062012-05-16 14:57:04 -0700111}
112
113TEST_F(HookTableTest, MultipleActionsAllSucceed) {
Gary Moraina9fb3252012-05-31 12:05:31 -0700114 const string kName1 = "test1";
115 const string kName2 = "test2";
116 const string kName3 = "test3";
Gary Morainf80ef062012-05-16 14:57:04 -0700117 Closure pending_cb;
118 const int kTimeout = 10;
Gary Moraina9fb3252012-05-31 12:05:31 -0700119 EXPECT_CALL(*this, StartAction()).Times(2);
120
121 // StartAction2 completes immediately before HookTable::Run() returns.
122 EXPECT_CALL(*this, StartAction2())
123 .WillOnce(CompleteAction(&hook_table_, kName1));
Gary Morainf80ef062012-05-16 14:57:04 -0700124 EXPECT_CALL(*this, DoneAction(IsSuccess()));
125
126 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
Gary Moraina9fb3252012-05-31 12:05:31 -0700127 Closure start2_cb = Bind(&HookTableTest::StartAction2, Unretained(this));
Gary Morainf80ef062012-05-16 14:57:04 -0700128 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
129 Unretained(this));
130
Gary Moraina9fb3252012-05-31 12:05:31 -0700131 hook_table_.Add(kName1, start2_cb);
132 hook_table_.Add(kName2, start_cb);
133 hook_table_.Add(kName3, start_cb);
Gary Morainf80ef062012-05-16 14:57:04 -0700134 hook_table_.Run(kTimeout, done_cb);
Gary Moraina9fb3252012-05-31 12:05:31 -0700135 hook_table_.ActionComplete(kName2);
136 hook_table_.ActionComplete(kName3);
Gary Morainf80ef062012-05-16 14:57:04 -0700137}
138
139TEST_F(HookTableTest, MultipleActionsAndOneTimesOut) {
Gary Moraina9fb3252012-05-31 12:05:31 -0700140 const string kName1 = "test1";
141 const string kName2 = "test2";
142 const string kName3 = "test3";
Gary Morainf80ef062012-05-16 14:57:04 -0700143 Closure pending_cb;
Gary Moraina9fb3252012-05-31 12:05:31 -0700144 const int kTimeout = 1;
Gary Morainf80ef062012-05-16 14:57:04 -0700145 EXPECT_CALL(*this, StartAction()).Times(3);
Gary Morainf80ef062012-05-16 14:57:04 -0700146 EXPECT_CALL(*this, DoneAction(IsFailure()));
147
148 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
Gary Morainf80ef062012-05-16 14:57:04 -0700149 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
150 Unretained(this));
151
Gary Moraina9fb3252012-05-31 12:05:31 -0700152 hook_table_.Add(kName1, start_cb);
153 hook_table_.Add(kName2, start_cb);
154 hook_table_.Add(kName3, start_cb);
Gary Morainf80ef062012-05-16 14:57:04 -0700155 hook_table_.Run(kTimeout, done_cb);
Gary Moraina9fb3252012-05-31 12:05:31 -0700156 hook_table_.ActionComplete(kName1);
157 hook_table_.ActionComplete(kName3);
158 // Cause the event dispatcher to exit after kTimeout + 1 ms.
159 event_dispatcher_.PostDelayedTask(MessageLoop::QuitClosure(),
160 kTimeout + 1);
161 event_dispatcher_.DispatchForever();
162}
163
164TEST_F(HookTableTest, AddActionsWithSameName) {
165 EXPECT_CALL(*this, StartAction()).Times(0);
166 EXPECT_CALL(*this, StartAction2());
167 EXPECT_CALL(*this, DoneAction(IsSuccess()));
168 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
169 Closure start2_cb = Bind(&HookTableTest::StartAction2, Unretained(this));
170 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
171 Unretained(this));
172 hook_table_.Add(kName, start_cb);
173
174 // Adding an action with the same name kName. New callbacks should replace
175 // old ones.
176 hook_table_.Add(kName, start2_cb);
177 hook_table_.Run(0, done_cb);
178 hook_table_.ActionComplete(kName);
179
180 // Ensure that the timeout callback got cancelled. If it did not get
181 // cancelled, done_cb will be run twice and make this test fail.
182 event_dispatcher_.DispatchPendingEvents();
183}
184
185TEST_F(HookTableTest, RemoveAction) {
186 EXPECT_CALL(*this, StartAction()).Times(0);
187 EXPECT_CALL(*this, DoneAction(IsSuccess()));
188 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
189 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
190 Unretained(this));
191 hook_table_.Add(kName, start_cb);
192 hook_table_.Remove(kName);
193 hook_table_.Run(0, done_cb);
194}
195
196TEST_F(HookTableTest, ActionCompleteFollowedByRemove) {
197 EXPECT_CALL(*this, StartAction()).Times(0);
198 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
199 hook_table_.Add(kName, start_cb);
200 hook_table_.ActionComplete(kName);
201 hook_table_.Remove(kName);
202}
203
Darin Petkov3ec55342012-09-28 14:04:44 +0200204TEST_F(HookTableTest, IsEmpty) {
205 EXPECT_TRUE(hook_table_.IsEmpty());
206 hook_table_.Add(kName, Closure());
207 EXPECT_FALSE(hook_table_.IsEmpty());
208 hook_table_.Remove(kName);
209 EXPECT_TRUE(hook_table_.IsEmpty());
210}
211
Gary Moraina9fb3252012-05-31 12:05:31 -0700212class SomeClass : public base::RefCounted<SomeClass> {
213 public:
214 SomeClass() {}
215 void StartAction() {}
216
217 private:
218 DISALLOW_COPY_AND_ASSIGN(SomeClass);
219};
220
221// This test verifies that a class that removes itself from a hook table upon
222// destruction does not crash if the hook table is destroyed first.
223TEST_F(HookTableTest, RefcountedObject) {
224 scoped_ptr<HookTable> ht(new HookTable(&event_dispatcher_));
225 {
226 scoped_refptr<SomeClass> ref_counted_object = new SomeClass();
227 Closure start_cb = Bind(&SomeClass::StartAction, ref_counted_object);
228 ht->Add(kName, start_cb);
Gary Morainf80ef062012-05-16 14:57:04 -0700229 }
230}
231
Gary Moraina9fb3252012-05-31 12:05:31 -0700232TEST_F(HookTableTest, ActionAddedBeforePreviousActionCompletes) {
233 EXPECT_CALL(*this, StartAction());
234 EXPECT_CALL(*this, StartAction2()).Times(0);
235 EXPECT_CALL(*this, DoneAction(IsSuccess()));
236 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
237 Closure start2_cb = Bind(&HookTableTest::StartAction2, Unretained(this));
238 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
239 Unretained(this));
240 hook_table_.Add(kName, start_cb);
241 hook_table_.Run(0, done_cb);
242
243 // An action with the same name is added before the previous actions complete.
244 // It should not be run.
245 hook_table_.Add(kName, start2_cb);
246 hook_table_.ActionComplete(kName);
247}
248
249
Gary Morainf80ef062012-05-16 14:57:04 -0700250} // namespace shill