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