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 |