Home | History | Annotate | Download | only in deprecated
      1 // Copyright (c) 2010 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 <iosfwd>
      6 #include <sstream>
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/basictypes.h"
     11 #include "base/message_loop.h"
     12 #include "base/port.h"
     13 #include "base/threading/platform_thread.h"
     14 #include "build/build_config.h"
     15 #include "chrome/common/deprecated/event_sys-inl.h"
     16 #include "testing/gtest/include/gtest/gtest.h"
     17 
     18 namespace {
     19 
     20 class Pair;
     21 
     22 struct TestEvent {
     23   Pair* source;
     24   enum {
     25     A_CHANGED, B_CHANGED, PAIR_BEING_DELETED,
     26   } what_happened;
     27   int old_value;
     28 };
     29 
     30 struct TestEventTraits {
     31   typedef TestEvent EventType;
     32   static bool IsChannelShutdownEvent(const TestEvent& event) {
     33     return TestEvent::PAIR_BEING_DELETED == event.what_happened;
     34   }
     35 };
     36 
     37 class Pair {
     38  public:
     39   typedef EventChannel<TestEventTraits> Channel;
     40   explicit Pair(const std::string& name) : name_(name), a_(0), b_(0) {
     41     TestEvent shutdown = { this, TestEvent::PAIR_BEING_DELETED, 0 };
     42     event_channel_ = new Channel(shutdown);
     43   }
     44   ~Pair() {
     45     delete event_channel_;
     46   }
     47   void set_a(int n) {
     48     TestEvent event = { this, TestEvent::A_CHANGED, a_ };
     49     a_ = n;
     50     event_channel_->NotifyListeners(event);
     51   }
     52   void set_b(int n) {
     53     TestEvent event = { this, TestEvent::B_CHANGED, b_ };
     54     b_ = n;
     55     event_channel_->NotifyListeners(event);
     56   }
     57   int a() const { return a_; }
     58   int b() const { return b_; }
     59   const std::string& name() { return name_; }
     60   Channel* event_channel() const { return event_channel_; }
     61 
     62  protected:
     63   const std::string name_;
     64   int a_;
     65   int b_;
     66   Channel* event_channel_;
     67 };
     68 
     69 class EventLogger {
     70  public:
     71   explicit EventLogger(std::ostream* out) : out_(out) { }
     72   ~EventLogger() {
     73     for (Hookups::iterator i = hookups_.begin(); i != hookups_.end(); ++i)
     74       delete *i;
     75   }
     76 
     77   void Hookup(const std::string name, Pair::Channel* channel) {
     78     hookups_.push_back(NewEventListenerHookup(channel, this,
     79                                               &EventLogger::HandlePairEvent,
     80                                               name));
     81   }
     82 
     83   void HandlePairEvent(const std::string& name, const TestEvent& event) {
     84     const char* what_changed = NULL;
     85     int new_value = 0;
     86     Hookups::iterator dead;
     87     switch (event.what_happened) {
     88     case TestEvent::A_CHANGED:
     89       what_changed = "A";
     90       new_value = event.source->a();
     91       break;
     92     case TestEvent::B_CHANGED:
     93       what_changed = "B";
     94       new_value = event.source->b();
     95       break;
     96     case TestEvent::PAIR_BEING_DELETED:
     97       *out_ << name << " heard " << event.source->name() << " being deleted."
     98             << std::endl;
     99       return;
    100     default:
    101       FAIL() << "Bad event.what_happened: " << event.what_happened;
    102       break;
    103     }
    104     *out_ << name << " heard " << event.source->name() << "'s " << what_changed
    105           << " change from " << event.old_value
    106           << " to " << new_value << std::endl;
    107   }
    108 
    109   typedef std::vector<EventListenerHookup*> Hookups;
    110   Hookups hookups_;
    111   std::ostream* out_;
    112 };
    113 
    114 const char golden_result[] = "Larry heard Sally's B change from 0 to 2\n"
    115 "Larry heard Sally's A change from 1 to 3\n"
    116 "Lewis heard Sam's B change from 0 to 5\n"
    117 "Larry heard Sally's A change from 3 to 6\n"
    118 "Larry heard Sally being deleted.\n";
    119 
    120 TEST(EventSys, Basic) {
    121   Pair sally("Sally"), sam("Sam");
    122   sally.set_a(1);
    123   std::stringstream log;
    124   EventLogger logger(&log);
    125   logger.Hookup("Larry", sally.event_channel());
    126   sally.set_b(2);
    127   sally.set_a(3);
    128   sam.set_a(4);
    129   logger.Hookup("Lewis", sam.event_channel());
    130   sam.set_b(5);
    131   sally.set_a(6);
    132   // Test that disconnect within callback doesn't deadlock.
    133   TestEvent event = {&sally, TestEvent::PAIR_BEING_DELETED, 0 };
    134   sally.event_channel()->NotifyListeners(event);
    135   sally.set_a(7);
    136   ASSERT_EQ(log.str(), golden_result);
    137 }
    138 
    139 
    140 // This goes pretty far beyond the normal use pattern, so don't use
    141 // ThreadTester as an example of what to do.
    142 class ThreadTester : public EventListener<TestEvent>,
    143                      public base::PlatformThread::Delegate {
    144  public:
    145   explicit ThreadTester(Pair* pair)
    146     : pair_(pair), remove_event_(&remove_event_mutex_),
    147       remove_event_bool_(false), completed_(false) {
    148     pair_->event_channel()->AddListener(this);
    149   }
    150   ~ThreadTester() {
    151     pair_->event_channel()->RemoveListener(this);
    152     for (size_t i = 0; i < threads_.size(); i++) {
    153       base::PlatformThread::Join(threads_[i].thread);
    154     }
    155   }
    156 
    157   struct ThreadInfo {
    158     base::PlatformThreadHandle thread;
    159   };
    160 
    161   struct ThreadArgs {
    162     base::ConditionVariable* thread_running_cond;
    163     base::Lock* thread_running_mutex;
    164     bool thread_running;
    165   };
    166 
    167   void Go() {
    168     base::Lock thread_running_mutex;
    169     base::ConditionVariable thread_running_cond(&thread_running_mutex);
    170     ThreadArgs args;
    171     ThreadInfo info;
    172     args.thread_running_cond = &(thread_running_cond);
    173     args.thread_running_mutex = &(thread_running_mutex);
    174     args.thread_running = false;
    175     args_ = args;
    176     ASSERT_TRUE(base::PlatformThread::Create(0, this, &info.thread));
    177     thread_running_mutex.Acquire();
    178     while ((args_.thread_running) == false) {
    179       thread_running_cond.Wait();
    180     }
    181     thread_running_mutex.Release();
    182     threads_.push_back(info);
    183   }
    184 
    185   // PlatformThread::Delegate methods.
    186   virtual void ThreadMain() {
    187     // Make sure each thread gets a current MessageLoop in TLS.
    188     // This test should use chrome threads for testing, but I'll leave it like
    189     // this for the moment since it requires a big chunk of rewriting and I
    190     // want the test passing while I checkpoint my CL.  Technically speaking,
    191     // there should be no functional difference.
    192     MessageLoop message_loop;
    193     args_.thread_running_mutex->Acquire();
    194     args_.thread_running = true;
    195     args_.thread_running_cond->Signal();
    196     args_.thread_running_mutex->Release();
    197 
    198     remove_event_mutex_.Acquire();
    199     while (remove_event_bool_ == false) {
    200       remove_event_.Wait();
    201     }
    202     remove_event_mutex_.Release();
    203 
    204     // Normally, you'd just delete the hookup. This is very bad style, but
    205     // necessary for the test.
    206     pair_->event_channel()->RemoveListener(this);
    207 
    208     completed_mutex_.Acquire();
    209     completed_ = true;
    210     completed_mutex_.Release();
    211   }
    212 
    213   void HandleEvent(const TestEvent& event) {
    214     remove_event_mutex_.Acquire();
    215     remove_event_bool_ = true;
    216     remove_event_.Broadcast();
    217     remove_event_mutex_.Release();
    218 
    219     base::PlatformThread::YieldCurrentThread();
    220 
    221     completed_mutex_.Acquire();
    222     if (completed_)
    223       FAIL() << "A test thread exited too early.";
    224     completed_mutex_.Release();
    225   }
    226 
    227   Pair* pair_;
    228   base::ConditionVariable remove_event_;
    229   base::Lock remove_event_mutex_;
    230   bool remove_event_bool_;
    231   base::Lock completed_mutex_;
    232   bool completed_;
    233   std::vector<ThreadInfo> threads_;
    234   ThreadArgs args_;
    235 };
    236 
    237 TEST(EventSys, Multithreaded) {
    238   Pair sally("Sally");
    239   ThreadTester a(&sally);
    240   for (int i = 0; i < 3; ++i)
    241     a.Go();
    242   sally.set_b(99);
    243 }
    244 
    245 class HookupDeleter {
    246  public:
    247   void HandleEvent(const TestEvent& event) {
    248     delete hookup_;
    249     hookup_ = NULL;
    250   }
    251   EventListenerHookup* hookup_;
    252 };
    253 
    254 TEST(EventSys, InHandlerDeletion) {
    255   Pair sally("Sally");
    256   HookupDeleter deleter;
    257   deleter.hookup_ = NewEventListenerHookup(sally.event_channel(),
    258                                            &deleter,
    259                                            &HookupDeleter::HandleEvent);
    260   sally.set_a(1);
    261   ASSERT_TRUE(NULL == deleter.hookup_);
    262 }
    263 
    264 }  // namespace
    265