blob: 74fc621eed69ead7d874293ead8344909cd84811 [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
Gary Moraina9fb3252012-05-31 12:05:31 -070041 EventDispatcher event_dispatcher_;
Gary Morainf80ef062012-05-16 14:57:04 -070042 HookTable hook_table_;
43};
44
Gary Moraina9fb3252012-05-31 12:05:31 -070045const char HookTableTest::kName[] = "test";
46
Gary Morainf80ef062012-05-16 14:57:04 -070047MATCHER(IsSuccess, "") {
48 return arg.IsSuccess();
49}
50
51MATCHER(IsFailure, "") {
52 return arg.IsFailure();
53}
54
Gary Moraina9fb3252012-05-31 12:05:31 -070055TEST_F(HookTableTest, ActionCompletes) {
Gary Morainf80ef062012-05-16 14:57:04 -070056 EXPECT_CALL(*this, StartAction());
Gary Morainf80ef062012-05-16 14:57:04 -070057 EXPECT_CALL(*this, DoneAction(IsSuccess()));
Gary Morainf80ef062012-05-16 14:57:04 -070058 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
Gary Morainf80ef062012-05-16 14:57:04 -070059 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
60 Unretained(this));
Gary Moraina9fb3252012-05-31 12:05:31 -070061 hook_table_.Add(kName, start_cb);
Gary Morainf80ef062012-05-16 14:57:04 -070062 hook_table_.Run(0, done_cb);
Gary Moraina9fb3252012-05-31 12:05:31 -070063 hook_table_.ActionComplete(kName);
64
65 // Ensure that the timeout callback got cancelled. If it did not get
66 // cancelled, done_cb will be run twice and make this test fail.
67 event_dispatcher_.DispatchPendingEvents();
Gary Morainf80ef062012-05-16 14:57:04 -070068}
69
Gary Moraina9fb3252012-05-31 12:05:31 -070070ACTION_P2(CompleteAction, hook_table, name) {
71 hook_table->ActionComplete(name);
72}
73
74TEST_F(HookTableTest, ActionCompletesInline) {
75 // StartAction completes immediately before HookTable::Run() returns.
76 EXPECT_CALL(*this, StartAction())
77 .WillOnce(CompleteAction(&hook_table_, kName));
78 EXPECT_CALL(*this, DoneAction(IsSuccess()));
Gary Morainf80ef062012-05-16 14:57:04 -070079 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
Gary Morainf80ef062012-05-16 14:57:04 -070080 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
81 Unretained(this));
Gary Moraina9fb3252012-05-31 12:05:31 -070082 hook_table_.Add(kName, start_cb);
83 hook_table_.Run(0, done_cb);
Gary Morainf80ef062012-05-16 14:57:04 -070084
Gary Moraina9fb3252012-05-31 12:05:31 -070085 // Ensure that the timeout callback got cancelled. If it did not get
86 // cancelled, done_cb will be run twice and make this test fail.
87 event_dispatcher_.DispatchPendingEvents();
Gary Morainf80ef062012-05-16 14:57:04 -070088}
89
90TEST_F(HookTableTest, ActionTimesOut) {
Gary Moraina9fb3252012-05-31 12:05:31 -070091 const int kTimeout = 1;
Gary Morainf80ef062012-05-16 14:57:04 -070092 EXPECT_CALL(*this, StartAction());
Gary Morainf80ef062012-05-16 14:57:04 -070093 EXPECT_CALL(*this, DoneAction(IsFailure()));
94
95 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
Gary Morainf80ef062012-05-16 14:57:04 -070096 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
97 Unretained(this));
98
Gary Moraina9fb3252012-05-31 12:05:31 -070099 hook_table_.Add(kName, start_cb);
Gary Morainf80ef062012-05-16 14:57:04 -0700100 hook_table_.Run(kTimeout, done_cb);
Gary Moraina9fb3252012-05-31 12:05:31 -0700101
102 // Cause the event dispatcher to exit after kTimeout + 1 ms.
103 event_dispatcher_.PostDelayedTask(MessageLoop::QuitClosure(),
104 kTimeout * + 1);
105 event_dispatcher_.DispatchForever();
Gary Morainf80ef062012-05-16 14:57:04 -0700106}
107
108TEST_F(HookTableTest, MultipleActionsAllSucceed) {
Gary Moraina9fb3252012-05-31 12:05:31 -0700109 const string kName1 = "test1";
110 const string kName2 = "test2";
111 const string kName3 = "test3";
Gary Morainf80ef062012-05-16 14:57:04 -0700112 Closure pending_cb;
113 const int kTimeout = 10;
Gary Moraina9fb3252012-05-31 12:05:31 -0700114 EXPECT_CALL(*this, StartAction()).Times(2);
115
116 // StartAction2 completes immediately before HookTable::Run() returns.
117 EXPECT_CALL(*this, StartAction2())
118 .WillOnce(CompleteAction(&hook_table_, kName1));
Gary Morainf80ef062012-05-16 14:57:04 -0700119 EXPECT_CALL(*this, DoneAction(IsSuccess()));
120
121 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
Gary Moraina9fb3252012-05-31 12:05:31 -0700122 Closure start2_cb = Bind(&HookTableTest::StartAction2, Unretained(this));
Gary Morainf80ef062012-05-16 14:57:04 -0700123 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
124 Unretained(this));
125
Gary Moraina9fb3252012-05-31 12:05:31 -0700126 hook_table_.Add(kName1, start2_cb);
127 hook_table_.Add(kName2, start_cb);
128 hook_table_.Add(kName3, start_cb);
Gary Morainf80ef062012-05-16 14:57:04 -0700129 hook_table_.Run(kTimeout, done_cb);
Gary Moraina9fb3252012-05-31 12:05:31 -0700130 hook_table_.ActionComplete(kName2);
131 hook_table_.ActionComplete(kName3);
Gary Morainf80ef062012-05-16 14:57:04 -0700132}
133
134TEST_F(HookTableTest, MultipleActionsAndOneTimesOut) {
Gary Moraina9fb3252012-05-31 12:05:31 -0700135 const string kName1 = "test1";
136 const string kName2 = "test2";
137 const string kName3 = "test3";
Gary Morainf80ef062012-05-16 14:57:04 -0700138 Closure pending_cb;
Gary Moraina9fb3252012-05-31 12:05:31 -0700139 const int kTimeout = 1;
Gary Morainf80ef062012-05-16 14:57:04 -0700140 EXPECT_CALL(*this, StartAction()).Times(3);
Gary Morainf80ef062012-05-16 14:57:04 -0700141 EXPECT_CALL(*this, DoneAction(IsFailure()));
142
143 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
Gary Morainf80ef062012-05-16 14:57:04 -0700144 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
145 Unretained(this));
146
Gary Moraina9fb3252012-05-31 12:05:31 -0700147 hook_table_.Add(kName1, start_cb);
148 hook_table_.Add(kName2, start_cb);
149 hook_table_.Add(kName3, start_cb);
Gary Morainf80ef062012-05-16 14:57:04 -0700150 hook_table_.Run(kTimeout, done_cb);
Gary Moraina9fb3252012-05-31 12:05:31 -0700151 hook_table_.ActionComplete(kName1);
152 hook_table_.ActionComplete(kName3);
153 // Cause the event dispatcher to exit after kTimeout + 1 ms.
154 event_dispatcher_.PostDelayedTask(MessageLoop::QuitClosure(),
155 kTimeout + 1);
156 event_dispatcher_.DispatchForever();
157}
158
159TEST_F(HookTableTest, AddActionsWithSameName) {
160 EXPECT_CALL(*this, StartAction()).Times(0);
161 EXPECT_CALL(*this, StartAction2());
162 EXPECT_CALL(*this, DoneAction(IsSuccess()));
163 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
164 Closure start2_cb = Bind(&HookTableTest::StartAction2, Unretained(this));
165 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
166 Unretained(this));
167 hook_table_.Add(kName, start_cb);
168
169 // Adding an action with the same name kName. New callbacks should replace
170 // old ones.
171 hook_table_.Add(kName, start2_cb);
172 hook_table_.Run(0, done_cb);
173 hook_table_.ActionComplete(kName);
174
175 // Ensure that the timeout callback got cancelled. If it did not get
176 // cancelled, done_cb will be run twice and make this test fail.
177 event_dispatcher_.DispatchPendingEvents();
178}
179
180TEST_F(HookTableTest, RemoveAction) {
181 EXPECT_CALL(*this, StartAction()).Times(0);
182 EXPECT_CALL(*this, DoneAction(IsSuccess()));
183 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
184 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
185 Unretained(this));
186 hook_table_.Add(kName, start_cb);
187 hook_table_.Remove(kName);
188 hook_table_.Run(0, done_cb);
189}
190
191TEST_F(HookTableTest, ActionCompleteFollowedByRemove) {
192 EXPECT_CALL(*this, StartAction()).Times(0);
193 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
194 hook_table_.Add(kName, start_cb);
195 hook_table_.ActionComplete(kName);
196 hook_table_.Remove(kName);
197}
198
199class SomeClass : public base::RefCounted<SomeClass> {
200 public:
201 SomeClass() {}
202 void StartAction() {}
203
204 private:
205 DISALLOW_COPY_AND_ASSIGN(SomeClass);
206};
207
208// This test verifies that a class that removes itself from a hook table upon
209// destruction does not crash if the hook table is destroyed first.
210TEST_F(HookTableTest, RefcountedObject) {
211 scoped_ptr<HookTable> ht(new HookTable(&event_dispatcher_));
212 {
213 scoped_refptr<SomeClass> ref_counted_object = new SomeClass();
214 Closure start_cb = Bind(&SomeClass::StartAction, ref_counted_object);
215 ht->Add(kName, start_cb);
Gary Morainf80ef062012-05-16 14:57:04 -0700216 }
217}
218
Gary Moraina9fb3252012-05-31 12:05:31 -0700219TEST_F(HookTableTest, ActionAddedBeforePreviousActionCompletes) {
220 EXPECT_CALL(*this, StartAction());
221 EXPECT_CALL(*this, StartAction2()).Times(0);
222 EXPECT_CALL(*this, DoneAction(IsSuccess()));
223 Closure start_cb = Bind(&HookTableTest::StartAction, Unretained(this));
224 Closure start2_cb = Bind(&HookTableTest::StartAction2, Unretained(this));
225 Callback<void(const Error &)> done_cb = Bind(&HookTableTest::DoneAction,
226 Unretained(this));
227 hook_table_.Add(kName, start_cb);
228 hook_table_.Run(0, done_cb);
229
230 // An action with the same name is added before the previous actions complete.
231 // It should not be run.
232 hook_table_.Add(kName, start2_cb);
233 hook_table_.ActionComplete(kName);
234}
235
236
Gary Morainf80ef062012-05-16 14:57:04 -0700237} // namespace shill