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