Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(29)

Side by Side Diff: chrome/browser/sync/util/event_sys_unittest.cc

Issue 194065: Initial commit of sync engine code to browser/sync.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Fixes to gtest include path, reverted syncapi. Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2009 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/logging.h"
12 #include "base/port.h"
13 #include "chrome/browser/sync/util/event_sys-inl.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 using std::endl;
17 using std::ostream;
18 using std::string;
19 using std::stringstream;
20 using std::vector;
21
22 namespace {
23
24 class Pair;
25
26 struct TestEvent {
27 Pair* source;
28 enum {
29 A_CHANGED, B_CHANGED, PAIR_BEING_DELETED,
30 } what_happened;
31 int old_value;
32 };
33
34 struct TestEventTraits {
35 typedef TestEvent EventType;
36 static bool IsChannelShutdownEvent(const TestEvent& event) {
37 return TestEvent::PAIR_BEING_DELETED == event.what_happened;
38 }
39 };
40
41 class Pair {
42 public:
43 typedef EventChannel<TestEventTraits> Channel;
44 explicit Pair(const string& name) : name_(name), a_(0), b_(0) {
45 TestEvent shutdown = { this, TestEvent::PAIR_BEING_DELETED, 0 };
46 event_channel_ = new Channel(shutdown);
47 }
48 ~Pair() {
49 delete event_channel_;
50 }
51 void set_a(int n) {
52 TestEvent event = { this, TestEvent::A_CHANGED, a_ };
53 a_ = n;
54 event_channel_->NotifyListeners(event);
55 }
56 void set_b(int n) {
57 TestEvent event = { this, TestEvent::B_CHANGED, b_ };
58 b_ = n;
59 event_channel_->NotifyListeners(event);
60 }
61 int a() const { return a_; }
62 int b() const { return b_; }
63 const string& name() { return name_; }
64 Channel* event_channel() const { return event_channel_; }
65
66 protected:
67 const string name_;
68 int a_;
69 int b_;
70 Channel* event_channel_;
71 };
72
73 class EventLogger {
74 public:
75 explicit EventLogger(ostream& out) : out_(out) { }
76 ~EventLogger() {
77 for (Hookups::iterator i = hookups_.begin(); i != hookups_.end(); ++i)
78 delete *i;
79 }
80
81 void Hookup(const string name, Pair::Channel* channel) {
82 hookups_.push_back(NewEventListenerHookup(channel, this,
83 &EventLogger::HandlePairEvent,
84 name));
85 }
86
87 void HandlePairEvent(const string& name, const TestEvent& event) {
88 const char* what_changed;
89 int new_value;
90 Hookups::iterator dead;
91 switch (event.what_happened) {
92 case TestEvent::A_CHANGED:
93 what_changed = "A";
94 new_value = event.source->a();
95 break;
96 case TestEvent::B_CHANGED:
97 what_changed = "B";
98 new_value = event.source->b();
99 break;
100 case TestEvent::PAIR_BEING_DELETED:
101 out_ << name << " heard " << event.source->name() << " being deleted."
102 << endl;
103 return;
104 default:
105 LOG(FATAL) << "Bad event.what_happened: " << event.what_happened;
106 break;
107 }
108 out_ << name << " heard " << event.source->name() << "'s " << what_changed
109 << " change from "
110 << event.old_value << " to " << new_value << endl;
111 }
112
113 typedef vector<EventListenerHookup*> Hookups;
114 Hookups hookups_;
115 ostream& out_;
116 };
117
118 const char golden_result[] = "Larry heard Sally's B change from 0 to 2\n"
119 "Larry heard Sally's A change from 1 to 3\n"
120 "Lewis heard Sam's B change from 0 to 5\n"
121 "Larry heard Sally's A change from 3 to 6\n"
122 "Larry heard Sally being deleted.\n";
123
124 TEST(EventSys, Basic) {
125 Pair sally("Sally"), sam("Sam");
126 sally.set_a(1);
127 stringstream log;
128 EventLogger logger(log);
129 logger.Hookup("Larry", sally.event_channel());
130 sally.set_b(2);
131 sally.set_a(3);
132 sam.set_a(4);
133 logger.Hookup("Lewis", sam.event_channel());
134 sam.set_b(5);
135 sally.set_a(6);
136 // Test that disconnect within callback doesn't deadlock.
137 TestEvent event = {&sally, TestEvent::PAIR_BEING_DELETED, 0 };
138 sally.event_channel()->NotifyListeners(event);
139 sally.set_a(7);
140 ASSERT_EQ(log.str(), golden_result);
141 }
142
143
144 // This goes pretty far beyond the normal use pattern, so don't use
145 // ThreadTester as an example of what to do.
146 class ThreadTester : public EventListener<TestEvent> {
147 public:
148 explicit ThreadTester(Pair* pair)
149 : pair_(pair), remove_event_bool_(false) {
150 pair_->event_channel()->AddListener(this);
151 }
152 ~ThreadTester() {
153 pair_->event_channel()->RemoveListener(this);
154 for (int i = 0; i < threads_.size(); i++) {
155 CHECK(pthread_join(threads_[i].thread, NULL) == 0);
156 delete threads_[i].completed;
157 }
158 }
159
160 struct ThreadInfo {
161 pthread_t thread;
162 bool *completed;
163 };
164
165 struct ThreadArgs {
166 ThreadTester* self;
167 pthread_cond_t *thread_running_cond;
168 pthread_mutex_t *thread_running_mutex;
169 bool *thread_running;
170 bool *completed;
171 };
172
173 pthread_t Go() {
174 PThreadCondVar thread_running_cond;
175 PThreadMutex thread_running_mutex;
176 ThreadArgs args;
177 ThreadInfo info;
178 info.completed = new bool(false);
179 args.self = this;
180 args.completed = info.completed;
181 args.thread_running_cond = &(thread_running_cond.condvar_);
182 args.thread_running_mutex = &(thread_running_mutex.mutex_);
183 args.thread_running = new bool(false);
184 CHECK(0 ==
185 pthread_create(&info.thread, NULL, ThreadTester::ThreadMain, &args));
186 thread_running_mutex.Lock();
187 while ((*args.thread_running) == false) {
188 pthread_cond_wait(&(thread_running_cond.condvar_),
189 &(thread_running_mutex.mutex_));
190 }
191 thread_running_mutex.Unlock();
192 delete args.thread_running;
193 threads_.push_back(info);
194 return info.thread;
195 }
196
197 static void* ThreadMain(void* arg) {
198 ThreadArgs args = *reinterpret_cast<ThreadArgs*>(arg);
199 pthread_mutex_lock(args.thread_running_mutex);
200 *args.thread_running = true;
201 pthread_cond_signal(args.thread_running_cond);
202 pthread_mutex_unlock(args.thread_running_mutex);
203
204 args.self->remove_event_mutex_.Lock();
205 while (args.self->remove_event_bool_ == false) {
206 pthread_cond_wait(&args.self->remove_event_.condvar_,
207 &args.self->remove_event_mutex_.mutex_);
208 }
209 args.self->remove_event_mutex_.Unlock();
210
211 // Normally, you'd just delete the hookup. This is very bad style, but
212 // necessary for the test.
213 args.self->pair_->event_channel()->RemoveListener(args.self);
214 *args.completed = true;
215 return 0;
216 }
217
218 void HandleEvent(const TestEvent& event) {
219 remove_event_mutex_.Lock();
220 remove_event_bool_ = true;
221 pthread_cond_broadcast(&remove_event_.condvar_);
222 remove_event_mutex_.Unlock();
223
224 // Windows and posix use different functions to sleep.
225 #ifdef OS_WINDOWS
226 Sleep(1);
227 #else
228 sleep(1);
229 #endif
230
231 for (int i = 0; i < threads_.size(); i++) {
232 if (*(threads_[i].completed))
233 LOG(FATAL) << "A test thread exited too early.";
234 }
235 }
236
237 Pair* pair_;
238 PThreadCondVar remove_event_;
239 PThreadMutex remove_event_mutex_;
240 bool remove_event_bool_;
241 vector<ThreadInfo> threads_;
242 };
243
244 TEST(EventSys, Multithreaded) {
245 Pair sally("Sally");
246 ThreadTester a(&sally);
247 for (int i = 0; i < 3; ++i)
248 a.Go();
249 sally.set_b(99);
250 }
251
252 class HookupDeleter {
253 public:
254 void HandleEvent(const TestEvent& event) {
255 delete hookup_;
256 hookup_ = NULL;
257 }
258 EventListenerHookup* hookup_;
259 };
260
261 TEST(EventSys, InHandlerDeletion) {
262 Pair sally("Sally");
263 HookupDeleter deleter;
264 deleter.hookup_ = NewEventListenerHookup(sally.event_channel(),
265 &deleter,
266 &HookupDeleter::HandleEvent);
267 sally.set_a(1);
268 ASSERT_TRUE(NULL == deleter.hookup_);
269 }
270
271 } // namespace
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698