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