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: 56 HookTableTest() 57 : hook_table_(&event_dispatcher_) {} 58 59 ResultCallback* GetDoneCallback() { return &hook_table_.done_callback_; } 60 61 EventDispatcherForTest event_dispatcher_; 62 HookTable hook_table_; 63 }; 64 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 80 ACTION_P2(CompleteAction, hook_table, name) { 81 hook_table->ActionComplete(name); 82 } 83 84 ACTION_P2(CompleteActionAndRemoveAction, hook_table, name) { 85 hook_table->ActionComplete(name); 86 hook_table->Remove(name); 87 } 88 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 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 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 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 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 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 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 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 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: 241 SomeClass() {} 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. 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 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