observer_list_unittest.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2006-2008 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/observer_list.h" 6#include "base/observer_list_threadsafe.h" 7 8#include <vector> 9 10#include "base/message_loop.h" 11#include "base/platform_thread.h" 12#include "base/ref_counted.h" 13#include "testing/gtest/include/gtest/gtest.h" 14 15using base::Time; 16 17namespace { 18 19class ObserverListTest : public testing::Test { 20}; 21 22class Foo { 23 public: 24 virtual void Observe(int x) = 0; 25 virtual ~Foo() {} 26}; 27 28class Adder : public Foo { 29 public: 30 explicit Adder(int scaler) : total(0), scaler_(scaler) {} 31 virtual void Observe(int x) { 32 total += x * scaler_; 33 } 34 virtual ~Adder() { } 35 int total; 36 private: 37 int scaler_; 38}; 39 40class Disrupter : public Foo { 41 public: 42 Disrupter(ObserverList<Foo>* list, Foo* doomed) 43 : list_(list), doomed_(doomed) { } 44 virtual ~Disrupter() { } 45 virtual void Observe(int x) { 46 list_->RemoveObserver(doomed_); 47 } 48 private: 49 ObserverList<Foo>* list_; 50 Foo* doomed_; 51}; 52 53class ThreadSafeDisrupter : public Foo { 54 public: 55 ThreadSafeDisrupter(ObserverListThreadSafe<Foo>* list, Foo* doomed) 56 : list_(list), doomed_(doomed) { } 57 virtual ~ThreadSafeDisrupter() { } 58 virtual void Observe(int x) { 59 list_->RemoveObserver(doomed_); 60 } 61 private: 62 ObserverListThreadSafe<Foo>* list_; 63 Foo* doomed_; 64}; 65 66class AddInObserve : public Foo { 67 public: 68 explicit AddInObserve(ObserverList<Foo>* observer_list) 69 : added(false), 70 observer_list(observer_list), 71 adder(1) { 72 } 73 virtual void Observe(int x) { 74 if (!added) { 75 added = true; 76 observer_list->AddObserver(&adder); 77 } 78 } 79 80 bool added; 81 ObserverList<Foo>* observer_list; 82 Adder adder; 83}; 84 85 86class ObserverListThreadSafeTest : public testing::Test { 87}; 88 89static const int kThreadRunTime = 2000; // ms to run the multi-threaded test. 90 91// A thread for use in the ThreadSafeObserver test 92// which will add and remove itself from the notification 93// list repeatedly. 94class AddRemoveThread : public PlatformThread::Delegate, 95 public Foo { 96 public: 97 AddRemoveThread(ObserverListThreadSafe<Foo>* list, bool notify) 98 : list_(list), 99 in_list_(false), 100 start_(Time::Now()), 101 count_observes_(0), 102 count_addtask_(0), 103 do_notifies_(notify) { 104 factory_ = new ScopedRunnableMethodFactory<AddRemoveThread>(this); 105 } 106 107 virtual ~AddRemoveThread() { 108 delete factory_; 109 } 110 111 void ThreadMain() { 112 loop_ = new MessageLoop(); // Fire up a message loop. 113 loop_->PostTask( 114 FROM_HERE, factory_->NewRunnableMethod(&AddRemoveThread::AddTask)); 115 loop_->Run(); 116 //LOG(ERROR) << "Loop 0x" << std::hex << loop_ << " done. " << 117 // count_observes_ << ", " << count_addtask_; 118 delete loop_; 119 loop_ = reinterpret_cast<MessageLoop*>(0xdeadbeef); 120 delete this; 121 } 122 123 // This task just keeps posting to itself in an attempt 124 // to race with the notifier. 125 void AddTask() { 126 count_addtask_++; 127 128 if ((Time::Now() - start_).InMilliseconds() > kThreadRunTime) { 129 LOG(INFO) << "DONE!"; 130 return; 131 } 132 133 if (!in_list_) { 134 list_->AddObserver(this); 135 in_list_ = true; 136 } 137 138 if (do_notifies_) { 139 list_->Notify(&Foo::Observe, 10); 140 } 141 142 loop_->PostDelayedTask(FROM_HERE, 143 factory_->NewRunnableMethod(&AddRemoveThread::AddTask), 0); 144 } 145 146 void Quit() { 147 loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); 148 } 149 150 virtual void Observe(int x) { 151 count_observes_++; 152 153 // If we're getting called after we removed ourselves from 154 // the list, that is very bad! 155 DCHECK(in_list_); 156 157 // This callback should fire on the appropriate thread 158 EXPECT_EQ(loop_, MessageLoop::current()); 159 160 list_->RemoveObserver(this); 161 in_list_ = false; 162 } 163 164 private: 165 ObserverListThreadSafe<Foo>* list_; 166 MessageLoop* loop_; 167 bool in_list_; // Are we currently registered for notifications. 168 // in_list_ is only used on |this| thread. 169 Time start_; // The time we started the test. 170 171 int count_observes_; // Number of times we observed. 172 int count_addtask_; // Number of times thread AddTask was called 173 bool do_notifies_; // Whether these threads should do notifications. 174 175 ScopedRunnableMethodFactory<AddRemoveThread>* factory_; 176}; 177 178TEST(ObserverListTest, BasicTest) { 179 ObserverList<Foo> observer_list; 180 Adder a(1), b(-1), c(1), d(-1); 181 Disrupter evil(&observer_list, &c); 182 183 observer_list.AddObserver(&a); 184 observer_list.AddObserver(&b); 185 186 FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); 187 188 observer_list.AddObserver(&evil); 189 observer_list.AddObserver(&c); 190 observer_list.AddObserver(&d); 191 192 FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); 193 194 EXPECT_EQ(a.total, 20); 195 EXPECT_EQ(b.total, -20); 196 EXPECT_EQ(c.total, 0); 197 EXPECT_EQ(d.total, -10); 198} 199 200TEST(ObserverListThreadSafeTest, BasicTest) { 201 MessageLoop loop; 202 203 scoped_refptr<ObserverListThreadSafe<Foo> > observer_list( 204 new ObserverListThreadSafe<Foo>); 205 Adder a(1); 206 Adder b(-1); 207 Adder c(1); 208 Adder d(-1); 209 ThreadSafeDisrupter evil(observer_list.get(), &c); 210 211 observer_list->AddObserver(&a); 212 observer_list->AddObserver(&b); 213 214 observer_list->Notify(&Foo::Observe, 10); 215 loop.RunAllPending(); 216 217 observer_list->AddObserver(&evil); 218 observer_list->AddObserver(&c); 219 observer_list->AddObserver(&d); 220 221 observer_list->Notify(&Foo::Observe, 10); 222 loop.RunAllPending(); 223 224 EXPECT_EQ(a.total, 20); 225 EXPECT_EQ(b.total, -20); 226 EXPECT_EQ(c.total, 0); 227 EXPECT_EQ(d.total, -10); 228} 229 230class FooRemover : public Foo { 231 public: 232 explicit FooRemover(ObserverListThreadSafe<Foo>* list) : list_(list) {} 233 virtual ~FooRemover() {} 234 235 void AddFooToRemove(Foo* foo) { 236 foos_.push_back(foo); 237 } 238 239 virtual void Observe(int x) { 240 std::vector<Foo*> tmp; 241 tmp.swap(foos_); 242 for (std::vector<Foo*>::iterator it = tmp.begin(); 243 it != tmp.end(); ++it) { 244 list_->RemoveObserver(*it); 245 } 246 } 247 248 private: 249 const scoped_refptr<ObserverListThreadSafe<Foo> > list_; 250 std::vector<Foo*> foos_; 251}; 252 253TEST(ObserverListThreadSafeTest, RemoveMultipleObservers) { 254 MessageLoop loop; 255 scoped_refptr<ObserverListThreadSafe<Foo> > observer_list( 256 new ObserverListThreadSafe<Foo>); 257 258 FooRemover a(observer_list); 259 Adder b(1); 260 261 observer_list->AddObserver(&a); 262 observer_list->AddObserver(&b); 263 264 a.AddFooToRemove(&a); 265 a.AddFooToRemove(&b); 266 267 observer_list->Notify(&Foo::Observe, 1); 268 loop.RunAllPending(); 269} 270 271// A test driver for a multi-threaded notification loop. Runs a number 272// of observer threads, each of which constantly adds/removes itself 273// from the observer list. Optionally, if cross_thread_notifies is set 274// to true, the observer threads will also trigger notifications to 275// all observers. 276static void ThreadSafeObserverHarness(int num_threads, 277 bool cross_thread_notifies) { 278 MessageLoop loop; 279 280 const int kMaxThreads = 15; 281 num_threads = num_threads > kMaxThreads ? kMaxThreads : num_threads; 282 283 scoped_refptr<ObserverListThreadSafe<Foo> > observer_list( 284 new ObserverListThreadSafe<Foo>); 285 Adder a(1); 286 Adder b(-1); 287 Adder c(1); 288 Adder d(-1); 289 290 observer_list->AddObserver(&a); 291 observer_list->AddObserver(&b); 292 293 AddRemoveThread* threaded_observer[kMaxThreads]; 294 PlatformThreadHandle threads[kMaxThreads]; 295 for (int index = 0; index < num_threads; index++) { 296 threaded_observer[index] = new AddRemoveThread(observer_list.get(), false); 297 EXPECT_TRUE(PlatformThread::Create(0, 298 threaded_observer[index], &threads[index])); 299 } 300 301 Time start = Time::Now(); 302 while (true) { 303 if ((Time::Now() - start).InMilliseconds() > kThreadRunTime) 304 break; 305 306 observer_list->Notify(&Foo::Observe, 10); 307 308 loop.RunAllPending(); 309 } 310 311 for (int index = 0; index < num_threads; index++) { 312 threaded_observer[index]->Quit(); 313 PlatformThread::Join(threads[index]); 314 } 315} 316 317TEST(ObserverListThreadSafeTest, CrossThreadObserver) { 318 // Use 7 observer threads. Notifications only come from 319 // the main thread. 320 ThreadSafeObserverHarness(7, false); 321} 322 323TEST(ObserverListThreadSafeTest, CrossThreadNotifications) { 324 // Use 3 observer threads. Notifications will fire from 325 // the main thread and all 3 observer threads. 326 ThreadSafeObserverHarness(3, true); 327} 328 329TEST(ObserverListTest, Existing) { 330 ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY); 331 Adder a(1); 332 AddInObserve b(&observer_list); 333 334 observer_list.AddObserver(&a); 335 observer_list.AddObserver(&b); 336 337 FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); 338 339 EXPECT_TRUE(b.added); 340 // B's adder should not have been notified because it was added during 341 // notificaiton. 342 EXPECT_EQ(0, b.adder.total); 343 344 // Notify again to make sure b's adder is notified. 345 FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); 346 EXPECT_EQ(1, b.adder.total); 347} 348 349class AddInClearObserve : public Foo { 350 public: 351 explicit AddInClearObserve(ObserverList<Foo>* list) 352 : list_(list), added_(false), adder_(1) {} 353 354 virtual void Observe(int /* x */) { 355 list_->Clear(); 356 list_->AddObserver(&adder_); 357 added_ = true; 358 } 359 360 bool added() const { return added_; } 361 const Adder& adder() const { return adder_; } 362 363 private: 364 ObserverList<Foo>* const list_; 365 366 bool added_; 367 Adder adder_; 368}; 369 370TEST(ObserverListTest, ClearNotifyAll) { 371 ObserverList<Foo> observer_list; 372 AddInClearObserve a(&observer_list); 373 374 observer_list.AddObserver(&a); 375 376 FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); 377 EXPECT_TRUE(a.added()); 378 EXPECT_EQ(1, a.adder().total) 379 << "Adder should observe once and have sum of 1."; 380} 381 382TEST(ObserverListTest, ClearNotifyExistingOnly) { 383 ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY); 384 AddInClearObserve a(&observer_list); 385 386 observer_list.AddObserver(&a); 387 388 FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); 389 EXPECT_TRUE(a.added()); 390 EXPECT_EQ(0, a.adder().total) 391 << "Adder should not observe, so sum should still be 0."; 392} 393 394} // namespace 395