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