OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 The Chromium 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 "base/callback_list.h" |
| 6 |
| 7 #include <vector> |
| 8 |
| 9 #include "base/bind_helpers.h" |
| 10 #include "base/compiler_specific.h" |
| 11 #include "base/memory/scoped_ptr.h" |
| 12 #include "base/memory/weak_ptr.h" |
| 13 #include "testing/gtest/include/gtest/gtest.h" |
| 14 |
| 15 namespace base { |
| 16 namespace { |
| 17 |
| 18 typedef base::Callback<void(const int&)> OneParamCallback; |
| 19 typedef CallbackListWithDetails<int> TestCallbackListWithParam; |
| 20 |
| 21 class Listener { |
| 22 public: |
| 23 explicit Listener() : total_(0), scaler_(1) {} |
| 24 Listener(int scaler) : total_(0), scaler_(scaler) {} |
| 25 ~Listener() {} |
| 26 void IncrementTotal() { total_++; } |
| 27 void MultiplyTotal(const int& x) { total_ += x * scaler_; } |
| 28 |
| 29 int total_; |
| 30 private: |
| 31 int scaler_; |
| 32 DISALLOW_COPY_AND_ASSIGN(Listener); |
| 33 }; |
| 34 |
| 35 class Remover { |
| 36 public: |
| 37 explicit Remover() : total_(0), removal_cb_(base::Closure()) {} |
| 38 ~Remover() {} |
| 39 void IncrementTotalAndRemove() { |
| 40 total_++; |
| 41 removal_cb_.Run(); |
| 42 } |
| 43 void SetCallback(const base::Closure& cb) { removal_cb_ = cb; } |
| 44 |
| 45 int total_; |
| 46 private: |
| 47 base::Closure removal_cb_; |
| 48 DISALLOW_COPY_AND_ASSIGN(Remover); |
| 49 }; |
| 50 |
| 51 class Adder { |
| 52 public: |
| 53 explicit Adder(CallbackList* cb_list) |
| 54 : added_(false), |
| 55 total_(0), |
| 56 cb_list_(cb_list) {} |
| 57 ~Adder() {} |
| 58 void AddCallback() { |
| 59 if (!added_) { |
| 60 added_ = true; |
| 61 cb_list_->Add( |
| 62 base::Bind(&Adder::IncrementTotal, base::Unretained(this))); |
| 63 } |
| 64 } |
| 65 void IncrementTotal() { total_++; } |
| 66 |
| 67 bool added_; |
| 68 int total_; |
| 69 private: |
| 70 CallbackList* cb_list_; |
| 71 DISALLOW_COPY_AND_ASSIGN(Adder); |
| 72 }; |
| 73 |
| 74 class Clearer { |
| 75 public: |
| 76 explicit Clearer(CallbackList* cb_list) |
| 77 : added_(false), |
| 78 total_(0), |
| 79 cb_list_(cb_list) {} |
| 80 ~Clearer() {} |
| 81 void ClearListAndAddCallback() { |
| 82 cb_list_->Clear(); |
| 83 cb_list_->Add( |
| 84 base::Bind(&Clearer::IncrementTotal, base::Unretained(this))); |
| 85 added_ = true; |
| 86 } |
| 87 void IncrementTotal() { total_++; } |
| 88 |
| 89 bool added_; |
| 90 int total_; |
| 91 private: |
| 92 CallbackList* cb_list_; |
| 93 DISALLOW_COPY_AND_ASSIGN(Clearer); |
| 94 }; |
| 95 |
| 96 class ListDestructor { |
| 97 public: |
| 98 explicit ListDestructor(CallbackList* cb_list) |
| 99 : cb_list_(cb_list) {} |
| 100 ~ListDestructor() {} |
| 101 void DestroyList() { |
| 102 delete cb_list_; |
| 103 } |
| 104 |
| 105 private: |
| 106 CallbackList* cb_list_; |
| 107 DISALLOW_COPY_AND_ASSIGN(ListDestructor); |
| 108 }; |
| 109 |
| 110 // Sanity check that closures added to the list will be run, and those removed |
| 111 // from the list will not be run. |
| 112 TEST(CallbackListTest, BasicTest) { |
| 113 CallbackList cb_list(CALLBACKS_NOTIFY_ALL); |
| 114 Listener a, b, c; |
| 115 |
| 116 base::Closure remove_a = cb_list.Add( |
| 117 base::Bind(&Listener::IncrementTotal, base::Unretained(&a))); |
| 118 base::Closure remove_b = cb_list.Add( |
| 119 base::Bind(&Listener::IncrementTotal, base::Unretained(&b))); |
| 120 |
| 121 EXPECT_FALSE(remove_a.is_null()); |
| 122 EXPECT_FALSE(remove_b.is_null()); |
| 123 |
| 124 cb_list.Run(); |
| 125 |
| 126 EXPECT_EQ(1, a.total_); |
| 127 EXPECT_EQ(1, b.total_); |
| 128 |
| 129 remove_b.Run(); |
| 130 |
| 131 base::Closure remove_c = cb_list.Add( |
| 132 base::Bind(&Listener::IncrementTotal, base::Unretained(&c))); |
| 133 |
| 134 cb_list.Run(); |
| 135 |
| 136 EXPECT_EQ(2, a.total_); |
| 137 EXPECT_EQ(1, b.total_); |
| 138 EXPECT_EQ(1, c.total_); |
| 139 |
| 140 remove_a.Run(); |
| 141 remove_b.Run(); |
| 142 remove_c.Run(); |
| 143 |
| 144 EXPECT_FALSE(cb_list.might_have_callbacks()); |
| 145 } |
| 146 |
| 147 // Sanity check that callbacks with details added to the list will be run, with |
| 148 // the correct details, and those removed from the list will not be run. |
| 149 TEST(CallbackListTest, BasicTestWithParams) { |
| 150 TestCallbackListWithParam cb_list(CALLBACKS_NOTIFY_ALL); |
| 151 Listener a(1), b(-1), c(1); |
| 152 |
| 153 base::Closure remove_a = cb_list.Add( |
| 154 base::Bind(&Listener::MultiplyTotal, base::Unretained(&a))); |
| 155 base::Closure remove_b = cb_list.Add( |
| 156 base::Bind(&Listener::MultiplyTotal, base::Unretained(&b))); |
| 157 |
| 158 EXPECT_FALSE(remove_a.is_null()); |
| 159 EXPECT_FALSE(remove_b.is_null()); |
| 160 |
| 161 cb_list.Run(10); |
| 162 |
| 163 EXPECT_EQ(10, a.total_); |
| 164 EXPECT_EQ(-10, b.total_); |
| 165 |
| 166 remove_b.Run(); |
| 167 |
| 168 base::Closure remove_c = cb_list.Add( |
| 169 base::Bind(&Listener::MultiplyTotal, base::Unretained(&c))); |
| 170 |
| 171 cb_list.Run(10); |
| 172 |
| 173 EXPECT_EQ(20, a.total_); |
| 174 EXPECT_EQ(-10, b.total_); |
| 175 EXPECT_EQ(10, c.total_); |
| 176 |
| 177 remove_a.Run(); |
| 178 remove_b.Run(); |
| 179 remove_c.Run(); |
| 180 |
| 181 EXPECT_FALSE(cb_list.might_have_callbacks()); |
| 182 } |
| 183 |
| 184 // Test the a callback can remove itself or a different callback from the list |
| 185 // during iteration without invalidating the iterator. |
| 186 TEST(CallbackListTest, RemoveCallbacksDuringIteration) { |
| 187 CallbackList cb_list(CALLBACKS_NOTIFY_ALL); |
| 188 Listener a, b; |
| 189 Remover remover_1, remover_2; |
| 190 |
| 191 base::Closure remover_1_cb = cb_list.Add( |
| 192 base::Bind(&Remover::IncrementTotalAndRemove, |
| 193 base::Unretained(&remover_1))); |
| 194 base::Closure remover_2_cb = cb_list.Add( |
| 195 base::Bind(&Remover::IncrementTotalAndRemove, |
| 196 base::Unretained(&remover_2))); |
| 197 base::Closure a_cb = cb_list.Add( |
| 198 base::Bind(&Listener::IncrementTotal, base::Unretained(&a))); |
| 199 base::Closure b_cb = cb_list.Add( |
| 200 base::Bind(&Listener::IncrementTotal, base::Unretained(&b))); |
| 201 |
| 202 // |remover_1| will remove itself. |
| 203 remover_1.SetCallback(remover_1_cb); |
| 204 // |remover_2| will remove a. |
| 205 remover_2.SetCallback(a_cb); |
| 206 |
| 207 cb_list.Run(); |
| 208 |
| 209 // |remover_1| runs once (and removes itself), |remover_2| runs once (and |
| 210 // removes a), |a| never runs, and |b| runs once. |
| 211 EXPECT_EQ(1, remover_1.total_); |
| 212 EXPECT_EQ(1, remover_2.total_); |
| 213 EXPECT_EQ(0, a.total_); |
| 214 EXPECT_EQ(1, b.total_); |
| 215 |
| 216 cb_list.Run(); |
| 217 |
| 218 // Only |remover_2| and |b| run this time. |
| 219 EXPECT_EQ(1, remover_1.total_); |
| 220 EXPECT_EQ(2, remover_2.total_); |
| 221 EXPECT_EQ(0, a.total_); |
| 222 EXPECT_EQ(2, b.total_); |
| 223 |
| 224 EXPECT_TRUE(cb_list.might_have_callbacks()); |
| 225 |
| 226 remover_2_cb.Run(); |
| 227 b_cb.Run(); |
| 228 |
| 229 EXPECT_FALSE(cb_list.might_have_callbacks()); |
| 230 } |
| 231 |
| 232 // Test that a callback can add another callback to the list durning iteration |
| 233 // without invalidating the iterator. |
| 234 // - If the CallbackNotificationType is |CALLBACKS_NOTIFY_ALL|, the newly added |
| 235 // callback will be run on the current iteration. |
| 236 // - All other callbacks in the list will be run as well. |
| 237 TEST(CallbackListTest, AddCallbacksDuringIteration) { |
| 238 CallbackList cb_list(CALLBACKS_NOTIFY_ALL); |
| 239 Adder a(&cb_list); |
| 240 Listener b; |
| 241 cb_list.Add( |
| 242 base::Bind(&Adder::AddCallback, base::Unretained(&a))); |
| 243 cb_list.Add( |
| 244 base::Bind(&Listener::IncrementTotal, base::Unretained(&b))); |
| 245 |
| 246 cb_list.Run(); |
| 247 |
| 248 EXPECT_EQ(1, a.total_); |
| 249 EXPECT_EQ(1, b.total_); |
| 250 EXPECT_TRUE(a.added_); |
| 251 |
| 252 cb_list.Run(); |
| 253 |
| 254 EXPECT_EQ(2, a.total_); |
| 255 EXPECT_EQ(2, b.total_); |
| 256 } |
| 257 |
| 258 // Test that a callback can add another callback to the list durning iteration |
| 259 // without invalidating the iterator. |
| 260 // - If the CallbackNotificationType is |CALLBACKS_NOTIFY_EXISTING_ONLY|, the |
| 261 // newly added will *not* be run during the current iteration, but will run on |
| 262 // subsequent iterations. |
| 263 // - All other callbacks in the list will be run as well. |
| 264 TEST(CallbackListTest, AddCallbacksDuringIteration_Existing) { |
| 265 CallbackList cb_list(CALLBACKS_NOTIFY_EXISTING_ONLY); |
| 266 Adder a(&cb_list); |
| 267 Listener b; |
| 268 cb_list.Add( |
| 269 base::Bind(&Adder::AddCallback, base::Unretained(&a))); |
| 270 cb_list.Add( |
| 271 base::Bind(&Listener::IncrementTotal, base::Unretained(&b))); |
| 272 |
| 273 cb_list.Run(); |
| 274 |
| 275 // |a| should have been added, but not run yet, as we are in |
| 276 // |CALLBACKS_NOTIFY_EXISTING_ONLY| mode. |
| 277 EXPECT_EQ(0, a.total_); |
| 278 EXPECT_EQ(1, b.total_); |
| 279 EXPECT_TRUE(a.added_); |
| 280 |
| 281 cb_list.Run(); |
| 282 |
| 283 // Now |a| should have run. |
| 284 EXPECT_EQ(1, a.total_); |
| 285 EXPECT_EQ(2, b.total_); |
| 286 } |
| 287 |
| 288 // Test that the list behaves as expected when callbacks are both added and |
| 289 // removed during iteration. In particular: |
| 290 // - If the CallbackNotificationType is |CALLBACKS_NOTIFY_EXISTING_ONLY|, the |
| 291 // newly added callback should *not* be called during the current iteration. |
| 292 // - The callback which was removed should not run again after removal |
| 293 TEST(CallbackListTest, AddAndRemoveCallbacksDuringIteration_Existing) { |
| 294 CallbackList cb_list(CALLBACKS_NOTIFY_EXISTING_ONLY); |
| 295 Adder a(&cb_list); |
| 296 Remover b; |
| 297 Listener c; |
| 298 |
| 299 base::Closure c_cb = cb_list.Add( |
| 300 base::Bind(&Listener::IncrementTotal, base::Unretained(&c))); |
| 301 |
| 302 // |b| removes |c|. |
| 303 b.SetCallback(c_cb); |
| 304 cb_list.Add( |
| 305 base::Bind(&Remover::IncrementTotalAndRemove, base::Unretained(&b))); |
| 306 |
| 307 // |a| adds a new callback. |
| 308 cb_list.Add(base::Bind(&Adder::AddCallback, base::Unretained(&a))); |
| 309 |
| 310 cb_list.Run(); |
| 311 |
| 312 // |c| ran once, new callback (|a|) should have been added but not yet run. |
| 313 EXPECT_EQ(1, c.total_); |
| 314 EXPECT_EQ(0, a.total_); |
| 315 EXPECT_TRUE(a.added_); |
| 316 |
| 317 cb_list.Run(); |
| 318 |
| 319 // Now |a| should have been run. |
| 320 EXPECT_EQ(1, c.total_); |
| 321 EXPECT_EQ(1, a.total_); |
| 322 } |
| 323 |
| 324 // Test that if we clear the list during iteration, no callbacks are run after |
| 325 // the one that did the clearing. |
| 326 TEST(CallbackListTest, ClearCallbacksDuringIteration) { |
| 327 CallbackList cb_list(CALLBACKS_NOTIFY_ALL); |
| 328 Clearer a(&cb_list); |
| 329 Listener b; |
| 330 cb_list.Add( |
| 331 base::Bind(&Clearer::ClearListAndAddCallback, base::Unretained(&a))); |
| 332 cb_list.Add( |
| 333 base::Bind(&Listener::IncrementTotal, base::Unretained(&b))); |
| 334 |
| 335 cb_list.Run(); |
| 336 |
| 337 EXPECT_EQ(1, a.total_); |
| 338 // |b| never runs because we cleared the list first. |
| 339 EXPECT_EQ(0, b.total_); |
| 340 EXPECT_TRUE(a.added_); |
| 341 } |
| 342 |
| 343 // Test that if the iterator outlives the list (that is, for some reason or |
| 344 // we delete the list mid-iteration) then: |
| 345 // - no callbacks run after deletion. |
| 346 // - the list is cleaned up properly (there will be memory errors if this is not |
| 347 // the case). |
| 348 TEST(CallbackListTest, IteratorOutlivesList) { |
| 349 CallbackList* cb_list = |
| 350 new CallbackList(CALLBACKS_NOTIFY_ALL); |
| 351 ListDestructor destructor(cb_list); |
| 352 cb_list->Add( |
| 353 base::Bind(&ListDestructor::DestroyList, base::Unretained(&destructor))); |
| 354 Listener a; |
| 355 cb_list->Add( |
| 356 base::Bind(&Listener::IncrementTotal, base::Unretained(&a))); |
| 357 |
| 358 cb_list->Run(); |
| 359 |
| 360 // |a| never gets called, as |cb_list| got deleted first. |
| 361 EXPECT_EQ(0, a.total_); |
| 362 } |
| 363 |
| 364 } // namespace |
| 365 } // namespace base |
OLD | NEW |