1 #include "../lib/libfilezilla/event_handler.hpp"
2 #include "../lib/libfilezilla/event_loop.hpp"
3
4 #include <cppunit/extensions/HelperMacros.h>
5
6 class EventloopTest final : public CppUnit::TestFixture
7 {
8 CPPUNIT_TEST_SUITE(EventloopTest);
9 CPPUNIT_TEST(testSimple);
10 CPPUNIT_TEST(testFilter);
11 CPPUNIT_TEST(testCondition);
12 CPPUNIT_TEST(testTimer);
13 CPPUNIT_TEST_SUITE_END();
14
15 public:
setUp()16 void setUp() {}
tearDown()17 void tearDown() {}
18
19 void testSimple();
20 void testFilter();
21 void testCondition();
22 void testTimer();
23 };
24
25 CPPUNIT_TEST_SUITE_REGISTRATION(EventloopTest);
26
27 namespace {
28 struct type1;
29 typedef fz::simple_event<type1> T1;
30
31 struct type2;
32 typedef fz::simple_event<type2, int> T2;
33
34 struct type3;
35 typedef fz::simple_event<type3> T3;
36
37 struct type4;
38 typedef fz::simple_event<type4> T4;
39
40 class target : public fz::event_handler
41 {
42 public:
target(fz::event_loop & l)43 target(fz::event_loop & l)
44 : fz::event_handler(l)
45 {}
46
~target()47 virtual ~target()
48 {
49 remove_handler();
50 }
51
a()52 void a()
53 {
54 ++a_;
55 send_event<T2>(5);
56 }
57
b(int v)58 void b(int v)
59 {
60 ++b_;
61
62 CPPUNIT_ASSERT_EQUAL(v, 5);
63 }
64
c()65 void c()
66 {
67 send_event<T4>();
68 }
69
d()70 void d()
71 {
72 fz::scoped_lock l(m_);
73 cond_.signal(l);
74 }
75
operator ()(fz::event_base const & ev)76 virtual void operator()(fz::event_base const& ev) override {
77 CPPUNIT_ASSERT((fz::dispatch<T1, T2, T3, T4>(ev, this, &target::a, &target::b, &target::c, &target::d)));
78 }
79
80 int a_{};
81 int b_{};
82
83
84 fz::mutex m_;
85 fz::condition cond_;
86 };
87 }
88
testSimple()89 void EventloopTest::testSimple()
90 {
91 fz::event_loop loop;
92
93 target t(loop);
94
95 for (int i = 0; i < 1000; ++i) {
96 t.send_event<T1>();
97 }
98
99 t.send_event<T3>();
100
101 fz::scoped_lock l(t.m_);
102 CPPUNIT_ASSERT(t.cond_.wait(l, fz::duration::from_seconds(1)));
103
104 CPPUNIT_ASSERT_EQUAL(t.a_, 1000);
105 CPPUNIT_ASSERT_EQUAL(t.b_, 1000);
106 }
107
108 namespace {
109 class target2 : public fz::event_handler
110 {
111 public:
target2(fz::event_loop & l)112 target2(fz::event_loop & l)
113 : fz::event_handler(l)
114 {}
115
~target2()116 virtual ~target2()
117 {
118 remove_handler();
119 }
120
a()121 void a()
122 {
123 {
124 fz::scoped_lock l(m_);
125 CPPUNIT_ASSERT(cond2_.wait(l, fz::duration::from_seconds(1)));
126 }
127
128 auto f = [&](fz::event_loop::Events::value_type& ev) -> bool {
129 if (ev.second->derived_type() == T1::type()) {
130 ++c_;
131 return true;
132 }
133
134 if (ev.second->derived_type() == T2::type()) {
135 ++d_;
136 std::get<0>(static_cast<T2&>(*ev.second).v_) += 4;
137 }
138 return false;
139
140 };
141 event_loop_.filter_events(f);
142 ++a_;
143 }
144
b(int v)145 void b(int v)
146 {
147 b_ += v;
148 }
149
c()150 void c()
151 {
152 fz::scoped_lock l(m_);
153 cond_.signal(l);
154 }
155
operator ()(fz::event_base const & ev)156 virtual void operator()(fz::event_base const& ev) override {
157 CPPUNIT_ASSERT((fz::dispatch<T1, T2, T3>(ev, this, &target2::a, &target2::b, &target2::c)));
158 }
159
160 int a_{};
161 int b_{};
162 int c_{};
163 int d_{};
164
165 fz::mutex m_;
166 fz::condition cond_;
167 fz::condition cond2_;
168 };
169 }
170
testFilter()171 void EventloopTest::testFilter()
172 {
173 fz::event_loop loop;
174
175 target2 t(loop);
176
177 for (int i = 0; i < 10; ++i) {
178 t.send_event<T1>();
179 }
180 t.send_event<T2>(3);
181 t.send_event<T2>(5);
182
183 t.send_event<T3>();
184
185 fz::scoped_lock l(t.m_);
186 t.cond2_.signal(l);
187
188 CPPUNIT_ASSERT(t.cond_.wait(l, fz::duration::from_seconds(1)));
189
190 CPPUNIT_ASSERT_EQUAL(t.a_, 1);
191 CPPUNIT_ASSERT_EQUAL(t.b_, 16);
192 CPPUNIT_ASSERT_EQUAL(t.c_, 9);
193 CPPUNIT_ASSERT_EQUAL(t.d_, 2);
194 }
195
testCondition()196 void EventloopTest::testCondition()
197 {
198 // Make sure condition::wait works correctly.
199
200 auto const t1 = fz::monotonic_clock::now();
201
202 fz::mutex m;
203 fz::condition c;
204
205 fz::scoped_lock l(m);
206 CPPUNIT_ASSERT(!c.wait(l, fz::duration::from_milliseconds(200)));
207
208 auto const t2 = fz::monotonic_clock::now();
209
210 // Due to rounding errors things can be off for one millisecond, allow it.
211 CPPUNIT_ASSERT((t2 - t1) >= fz::duration::from_milliseconds(199));
212 }
213
214 namespace {
215 class timer_handler final : public fz::event_handler
216 {
217 public:
timer_handler(fz::event_loop & l)218 timer_handler(fz::event_loop & l)
219 : fz::event_handler(l)
220 {}
221
~timer_handler()222 virtual ~timer_handler()
223 {
224 remove_handler();
225 }
226
operator ()(fz::event_base const & ev)227 virtual void operator()(fz::event_base const& ev) override {
228 CPPUNIT_ASSERT(fz::dispatch<fz::timer_event>(ev, this, &timer_handler::on_timer));
229 }
230
on_timer(fz::timer_id const & id)231 void on_timer(fz::timer_id const& id)
232 {
233 CPPUNIT_ASSERT_EQUAL(id, id_);
234
235 fz::scoped_lock l(m_);
236 cond_.signal(l);
237 }
238
239 fz::mutex m_;
240 fz::condition cond_;
241
242 fz::timer_id id_{};
243 };
244 }
245
testTimer()246 void EventloopTest::testTimer()
247 {
248 fz::event_loop loop;
249
250 timer_handler handler(loop);
251
252 fz::scoped_lock l(handler.m_);
253 handler.id_ = handler.add_timer(fz::duration::from_milliseconds(1), true);
254
255 CPPUNIT_ASSERT(handler.cond_.wait(l, fz::duration::from_seconds(1)));
256 }
257