| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |