Chromium Code Reviews| 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(void)> TestCallback; | |
|
awong
2013/08/29 19:30:48
TestCallback here is just a Closure. Why not use b
Cait (Slow)
2013/08/30 00:28:02
Done.
| |
| 19 typedef CallbackList TestCallbackList; | |
| 20 | |
| 21 typedef base::Callback<void(const int&)> OneParamCallback; | |
| 22 typedef CallbackListWithDetails<int> TestCallbackListWithParam; | |
| 23 | |
| 24 class Listener { | |
| 25 public: | |
| 26 explicit Listener() : total(0), scaler_(1) {} | |
| 27 Listener(int scaler) : total(0), scaler_(scaler) {} | |
| 28 virtual ~Listener() {} | |
| 29 virtual void OnEvent() { | |
|
awong
2013/08/29 19:30:48
Naming the function with what their action is, rat
Cait (Slow)
2013/08/30 00:28:02
Done.
| |
| 30 total++; | |
| 31 } | |
| 32 virtual void OnEventWithParam(const int& x) { | |
| 33 total += x * scaler_; | |
| 34 } | |
| 35 int total; | |
| 36 private: | |
| 37 int scaler_; | |
| 38 }; | |
| 39 | |
| 40 class Remover : public Listener { | |
|
awong
2013/08/29 19:30:48
Is there a reason for the inheritance?
If it'st j
Cait (Slow)
2013/08/30 00:28:02
Done.
| |
| 41 public: | |
| 42 explicit Remover() : removal_cb_(base::Closure()) {} | |
| 43 virtual ~Remover() {} | |
| 44 virtual void OnEvent() OVERRIDE { | |
| 45 Listener::OnEvent(); | |
| 46 removal_cb_.Run(); | |
| 47 } | |
| 48 virtual void SetCallback(const base::Closure& cb) { | |
| 49 removal_cb_ = cb; | |
| 50 } | |
| 51 | |
| 52 private: | |
| 53 base::Closure removal_cb_; | |
| 54 }; | |
| 55 | |
| 56 class Adder : public Listener { | |
| 57 public: | |
| 58 explicit Adder(TestCallbackList* cb_list) | |
| 59 : added(false), | |
| 60 cb_list_(cb_list) {} | |
| 61 virtual ~Adder() {} | |
| 62 virtual void OnAddedEvent() { | |
| 63 if (!added) { | |
| 64 added = true; | |
| 65 cb_list_->Add( | |
| 66 base::Bind(&Adder::OnEvent, base::Unretained(this))); | |
| 67 } | |
| 68 } | |
| 69 bool added; | |
|
awong
2013/08/29 19:30:48
Should be added_
Cait (Slow)
2013/08/30 00:28:02
Done.
| |
| 70 | |
| 71 private: | |
| 72 TestCallbackList* cb_list_; | |
| 73 }; | |
| 74 | |
| 75 class Clearer : public Listener { | |
| 76 public: | |
| 77 explicit Clearer(TestCallbackList* cb_list) | |
| 78 : added(false), | |
| 79 cb_list_(cb_list) {} | |
| 80 virtual ~Clearer() {} | |
| 81 virtual void OnClearAndAddEvent() { | |
| 82 cb_list_->Clear(); | |
| 83 cb_list_->Add( | |
| 84 base::Bind(&Clearer::OnEvent, base::Unretained(this))); | |
| 85 added = true; | |
| 86 } | |
| 87 bool added; | |
|
awong
2013/08/29 19:30:48
nit: added -> added_
Cait (Slow)
2013/08/30 00:28:02
Done.
| |
| 88 | |
| 89 private: | |
| 90 TestCallbackList* cb_list_; | |
| 91 }; | |
| 92 | |
| 93 class ListDestructor { | |
| 94 public: | |
| 95 explicit ListDestructor(TestCallbackList* cb_list) | |
| 96 : cb_list_(cb_list) {} | |
| 97 virtual ~ListDestructor() {} | |
| 98 virtual void OnEvent() { | |
| 99 cb_list_->Clear(); | |
| 100 delete cb_list_; | |
| 101 } | |
| 102 | |
| 103 private: | |
| 104 TestCallbackList* cb_list_; | |
| 105 }; | |
| 106 | |
| 107 TEST(CallbackListTest, BasicTest) { | |
|
awong
2013/08/29 19:30:48
Can you add a comment listing the invariants being
| |
| 108 TestCallbackList cb_list(TestCallbackList::NOTIFY_ALL); | |
| 109 Listener a, b, c; | |
| 110 TestCallback a_cb = base::Bind(&Listener::OnEvent, base::Unretained(&a)); | |
| 111 TestCallback b_cb = base::Bind(&Listener::OnEvent, base::Unretained(&b)); | |
| 112 | |
| 113 base::Closure remove_a = cb_list.Add(a_cb); | |
| 114 base::Closure remove_b = cb_list.Add(b_cb); | |
| 115 | |
| 116 EXPECT_FALSE(remove_a.is_null()); | |
| 117 EXPECT_FALSE(remove_b.is_null()); | |
| 118 | |
| 119 cb_list.Run(); | |
| 120 | |
| 121 EXPECT_EQ(1, a.total); | |
| 122 EXPECT_EQ(1, b.total); | |
| 123 | |
| 124 remove_b.Run(); | |
| 125 | |
| 126 base::Closure remove_c = cb_list.Add( | |
| 127 base::Bind(&Listener::OnEvent, base::Unretained(&c))); | |
| 128 | |
| 129 cb_list.Run(); | |
| 130 | |
| 131 EXPECT_EQ(2, a.total); | |
| 132 EXPECT_EQ(1, b.total); | |
| 133 EXPECT_EQ(1, c.total); | |
| 134 | |
| 135 { | |
| 136 scoped_ptr<ScopedClosureRunner> r1; | |
| 137 r1.reset(new ScopedClosureRunner(remove_a)); | |
| 138 } | |
| 139 | |
| 140 //remove_a.Run(); | |
|
awong
2013/08/29 19:30:48
is this supposed to be commented out?
Cait (Slow)
2013/08/30 00:28:02
Done.
| |
| 141 remove_b.Run(); | |
| 142 remove_c.Run(); | |
| 143 | |
| 144 EXPECT_FALSE(cb_list.might_have_callbacks()); | |
| 145 } | |
| 146 | |
| 147 TEST(CallbackListTest, BasicTestWithParams) { | |
| 148 TestCallbackListWithParam cb_list(TestCallbackListWithParam::NOTIFY_ALL); | |
| 149 Listener a(1), b(-1), c(1); | |
| 150 OneParamCallback a_cb = | |
| 151 base::Bind(&Listener::OnEventWithParam, base::Unretained(&a)); | |
| 152 | |
| 153 OneParamCallback b_cb = | |
| 154 base::Bind(&Listener::OnEventWithParam, base::Unretained(&b)); | |
| 155 | |
| 156 base::Closure remove_a = cb_list.Add(a_cb); | |
| 157 | |
|
awong
2013/08/29 19:30:48
this newline seems inconsistent with how the previ
Cait (Slow)
2013/08/30 00:28:02
Done.
| |
| 158 base::Closure remove_b = cb_list.Add(b_cb); | |
| 159 | |
| 160 EXPECT_FALSE(remove_a.is_null()); | |
| 161 EXPECT_FALSE(remove_b.is_null()); | |
| 162 | |
| 163 cb_list.Run(10); | |
| 164 | |
| 165 EXPECT_EQ(10, a.total); | |
| 166 EXPECT_EQ(-10, b.total); | |
| 167 | |
| 168 remove_b.Run(); | |
| 169 | |
| 170 base::Closure remove_c = cb_list.Add( | |
| 171 base::Bind(&Listener::OnEventWithParam, base::Unretained(&c))); | |
| 172 | |
| 173 cb_list.Run(10); | |
| 174 | |
| 175 EXPECT_EQ(20, a.total); | |
| 176 EXPECT_EQ(-10, b.total); | |
| 177 EXPECT_EQ(10, c.total); | |
| 178 | |
| 179 remove_a.Run(); | |
| 180 remove_b.Run(); | |
| 181 remove_c.Run(); | |
| 182 | |
| 183 EXPECT_FALSE(cb_list.might_have_callbacks()); | |
| 184 } | |
| 185 | |
| 186 TEST(CallbackListTest, RemoveCallbacksDuringIteration) { | |
| 187 TestCallbackList cb_list(TestCallbackList::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::OnEvent, base::Unretained(&remover_1))); | |
| 193 base::Closure remover_2_cb = cb_list.Add( | |
| 194 base::Bind(&Remover::OnEvent, base::Unretained(&remover_2))); | |
| 195 base::Closure a_cb = cb_list.Add( | |
| 196 base::Bind(&Listener::OnEvent, base::Unretained(&a))); | |
| 197 base::Closure b_cb = cb_list.Add( | |
| 198 base::Bind(&Listener::OnEvent, base::Unretained(&b))); | |
| 199 | |
| 200 // |remover_1| will remove itself. | |
| 201 remover_1.SetCallback(remover_1_cb); | |
| 202 // |remover_2| will remove a. | |
| 203 remover_2.SetCallback(a_cb); | |
| 204 | |
| 205 cb_list.Run(); | |
| 206 | |
| 207 // |remover_1| runs once (and removes itself), |remover_2| runs once (and | |
| 208 // removes a), |a| never runs, and |b| runs once. | |
| 209 EXPECT_EQ(1, remover_1.total); | |
| 210 EXPECT_EQ(1, remover_2.total); | |
| 211 EXPECT_EQ(0, a.total); | |
| 212 EXPECT_EQ(1, b.total); | |
| 213 | |
| 214 cb_list.Run(); | |
| 215 | |
| 216 // Only |remover_2| and |b| run this time. | |
| 217 EXPECT_EQ(1, remover_1.total); | |
| 218 EXPECT_EQ(2, remover_2.total); | |
| 219 EXPECT_EQ(0, a.total); | |
| 220 EXPECT_EQ(2, b.total); | |
| 221 | |
| 222 EXPECT_TRUE(cb_list.might_have_callbacks()); | |
| 223 | |
| 224 remover_2_cb.Run(); | |
| 225 b_cb.Run(); | |
| 226 | |
| 227 EXPECT_FALSE(cb_list.might_have_callbacks()); | |
| 228 } | |
| 229 | |
| 230 TEST(CallbackListTest, AddCallbacksDuringIteration) { | |
| 231 TestCallbackList cb_list(TestCallbackList::NOTIFY_ALL); | |
| 232 Adder a(&cb_list); | |
| 233 Listener b; | |
| 234 cb_list.Add( | |
| 235 base::Bind(&Adder::OnAddedEvent, base::Unretained(&a))); | |
| 236 cb_list.Add( | |
| 237 base::Bind(&Listener::OnEvent, base::Unretained(&b))); | |
| 238 | |
| 239 cb_list.Run(); | |
| 240 | |
| 241 EXPECT_EQ(1, a.total); | |
| 242 EXPECT_EQ(1, b.total); | |
| 243 EXPECT_TRUE(a.added); | |
| 244 | |
| 245 cb_list.Run(); | |
| 246 | |
| 247 EXPECT_EQ(2, a.total); | |
| 248 EXPECT_EQ(2, b.total); | |
| 249 | |
| 250 cb_list.Clear(); | |
| 251 } | |
| 252 | |
| 253 TEST(CallbackListTest, AddCallbacksDuringIteration_Existing) { | |
| 254 TestCallbackList cb_list(TestCallbackList::NOTIFY_EXISTING_ONLY); | |
| 255 Adder a(&cb_list); | |
| 256 Listener b; | |
| 257 cb_list.Add( | |
| 258 base::Bind(&Adder::OnAddedEvent, base::Unretained(&a))); | |
| 259 cb_list.Add( | |
| 260 base::Bind(&Listener::OnEvent, base::Unretained(&b))); | |
| 261 | |
| 262 cb_list.Run(); | |
| 263 | |
| 264 EXPECT_EQ(0, a.total); | |
| 265 EXPECT_EQ(1, b.total); | |
| 266 EXPECT_TRUE(a.added); | |
| 267 | |
| 268 cb_list.Run(); | |
| 269 | |
| 270 EXPECT_EQ(1, a.total); | |
| 271 EXPECT_EQ(2, b.total); | |
| 272 | |
| 273 cb_list.Clear(); | |
| 274 } | |
| 275 | |
| 276 TEST(CallbackListTest, AddAndRemoveCallbacksDuringIteration_Existing) { | |
| 277 TestCallbackList cb_list(TestCallbackList::NOTIFY_EXISTING_ONLY); | |
| 278 Adder a(&cb_list); | |
| 279 Remover b; | |
| 280 Listener c; | |
| 281 | |
| 282 base::Closure c_cb = cb_list.Add( | |
| 283 base::Bind(&Listener::OnEvent, base::Unretained(&c))); | |
| 284 | |
| 285 // |b| removes |c|. | |
| 286 b.SetCallback(c_cb); | |
| 287 cb_list.Add( | |
| 288 base::Bind(&Remover::OnEvent, base::Unretained(&b))); | |
| 289 | |
| 290 // |a| adds a new callback. | |
| 291 base::Closure a_cb = cb_list.Add( | |
| 292 base::Bind(&Adder::OnAddedEvent, base::Unretained(&a))); | |
| 293 | |
| 294 cb_list.Run(); | |
| 295 | |
| 296 // |c| ran once, new callback should have been added but not yet run. | |
| 297 EXPECT_EQ(1, c.total); | |
| 298 EXPECT_EQ(0, a.total); | |
| 299 EXPECT_TRUE(a.added); | |
| 300 | |
| 301 cb_list.Run(); | |
| 302 | |
| 303 // Now it should have been run. | |
| 304 EXPECT_EQ(1, c.total); | |
| 305 EXPECT_EQ(1, a.total); | |
| 306 | |
| 307 cb_list.Clear(); | |
| 308 } | |
| 309 | |
| 310 TEST(CallbackListTest, ClearCallbacksDuringIteration) { | |
| 311 TestCallbackList cb_list(TestCallbackList::NOTIFY_ALL); | |
| 312 Clearer a(&cb_list); | |
| 313 Listener b; | |
| 314 cb_list.Add( | |
| 315 base::Bind(&Clearer::OnClearAndAddEvent, base::Unretained(&a))); | |
| 316 cb_list.Add( | |
| 317 base::Bind(&Listener::OnEvent, base::Unretained(&b))); | |
| 318 | |
| 319 cb_list.Run(); | |
| 320 | |
| 321 EXPECT_EQ(1, a.total); | |
| 322 EXPECT_EQ(0, b.total); | |
| 323 EXPECT_TRUE(a.added); | |
| 324 | |
| 325 cb_list.Clear(); | |
| 326 } | |
| 327 | |
| 328 TEST(CallbackListTest, IteratorOutlivesList) { | |
| 329 TestCallbackList* cb_list = | |
| 330 new TestCallbackList(TestCallbackList::NOTIFY_ALL); | |
| 331 ListDestructor destructor(cb_list); | |
| 332 cb_list->Add( | |
| 333 base::Bind(&ListDestructor::OnEvent, base::Unretained(&destructor))); | |
| 334 Listener a; | |
| 335 cb_list->Add( | |
| 336 base::Bind(&Listener::OnEvent, base::Unretained(&a))); | |
| 337 | |
| 338 cb_list->Run(); | |
| 339 | |
| 340 // |a| never gets called, as |cb_list| got deleted first. | |
| 341 EXPECT_EQ(0, a.total); | |
| 342 } | |
| 343 | |
| 344 } // namespace | |
| 345 } // namespace base | |
| OLD | NEW |