Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : testing of the tevent glib glue subsystem
5 :
6 : Copyright (C) Ralph Boehme 2016
7 :
8 : glib tests adapted from glib2 glib/tests/mainloop.c
9 : Copyright (C) 2011 Red Hat Inc., Matthias Clasen
10 :
11 : ** NOTE! The following LGPL license applies to the tevent
12 : ** library. This does NOT imply that all of Samba is released
13 : ** under the LGPL
14 :
15 : This library is free software; you can redistribute it and/or
16 : modify it under the terms of the GNU Lesser General Public
17 : License as published by the Free Software Foundation; either
18 : version 3 of the License, or (at your option) any later version.
19 :
20 : This library is distributed in the hope that it will be useful,
21 : but WITHOUT ANY WARRANTY; without even the implied warranty of
22 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 : Lesser General Public License for more details.
24 :
25 : You should have received a copy of the GNU Lesser General Public
26 : License along with this library; if not, see <http://www.gnu.org/licenses/>.
27 : */
28 :
29 : #include "replace.h"
30 :
31 : /*
32 : * glib uses TRUE and FALSE which may have redefined by "includes.h" to be
33 : * unusable. Unndefine so glib can establish its own working replacement.
34 : */
35 : #undef TRUE
36 : #undef FALSE
37 : #include <glib.h>
38 : #include <glib-unix.h>
39 : #include "lib/tevent_glib_glue.h"
40 :
41 : /*
42 : * Unfortunately the glib test suite runner doesn't pass args to tests
43 : * so we must keep a few globals here.
44 : */
45 : static struct tevent_context *ev;
46 :
47 17 : static gboolean count_calls(gpointer data)
48 : {
49 17 : gint *i = (gint *)data;
50 :
51 17 : (*i)++;
52 :
53 17 : return TRUE;
54 : }
55 :
56 1 : static gboolean quit_loop(gpointer data)
57 : {
58 1 : struct tevent_glib_glue *glue = talloc_get_type_abort(
59 : data, struct tevent_glib_glue);
60 :
61 1 : samba_tevent_glib_glue_quit(glue);
62 :
63 1 : return G_SOURCE_REMOVE;
64 : }
65 :
66 1 : static void test_timeouts(void)
67 : {
68 1 : GMainContext *ctx = NULL;
69 1 : struct tevent_glib_glue *glue = NULL;
70 1 : GSource *source = NULL;
71 1 : gint a;
72 1 : gint b;
73 1 : gint c;
74 :
75 1 : a = b = c = 0;
76 :
77 1 : ctx = g_main_context_new();
78 1 : glue = samba_tevent_glib_glue_create(ev, ev, ctx);
79 1 : g_assert(glue != NULL);
80 :
81 1 : source = g_timeout_source_new(100);
82 1 : g_source_set_callback(source, count_calls, &a, NULL);
83 1 : g_source_attach(source, ctx);
84 1 : g_source_unref(source);
85 :
86 1 : source = g_timeout_source_new(250);
87 1 : g_source_set_callback(source, count_calls, &b, NULL);
88 1 : g_source_attach(source, ctx);
89 1 : g_source_unref(source);
90 :
91 1 : source = g_timeout_source_new(330);
92 1 : g_source_set_callback(source, count_calls, &c, NULL);
93 1 : g_source_attach(source, ctx);
94 1 : g_source_unref(source);
95 :
96 1 : source = g_timeout_source_new(1050);
97 1 : g_source_set_callback(source, quit_loop, glue, NULL);
98 1 : g_source_attach(source, ctx);
99 1 : g_source_unref(source);
100 :
101 1 : g_assert(tevent_loop_wait(ev) == 0);
102 :
103 : /* We may be delayed for an arbitrary amount of time - for example,
104 : * it's possible for all timeouts to fire exactly once.
105 : */
106 1 : g_assert_cmpint(a, >, 0);
107 1 : g_assert_cmpint(a, >=, b);
108 1 : g_assert_cmpint(b, >=, c);
109 :
110 1 : g_assert_cmpint(a, <=, 10);
111 1 : g_assert_cmpint(b, <=, 4);
112 1 : g_assert_cmpint(c, <=, 3);
113 :
114 1 : samba_tevent_glib_glue_quit(glue);
115 1 : TALLOC_FREE(glue);
116 1 : g_main_context_unref(ctx);
117 1 : }
118 :
119 : struct test_glib_ev_source_data {
120 : GMainContext *ctx;
121 : struct tevent_glib_glue *glue;
122 : };
123 :
124 : static gboolean test_glib_ev_source_quit_loop(gpointer data);
125 :
126 2 : static gboolean test_glib_ev_source_timeout_cb(gpointer data)
127 : {
128 2 : struct test_glib_ev_source_data *state = talloc_get_type_abort(
129 : data, struct test_glib_ev_source_data);
130 2 : GSource *source = NULL;
131 :
132 2 : source = g_timeout_source_new(100);
133 2 : g_source_set_callback(source,
134 : test_glib_ev_source_quit_loop,
135 : state,
136 : NULL);
137 2 : g_source_attach(source, state->ctx);
138 2 : g_source_unref(source);
139 :
140 2 : return TRUE;
141 : }
142 :
143 1 : static gboolean test_glib_ev_source_quit_loop(gpointer data)
144 : {
145 1 : struct test_glib_ev_source_data *state = talloc_get_type_abort(
146 : data, struct test_glib_ev_source_data);
147 :
148 1 : samba_tevent_glib_glue_quit(state->glue);
149 :
150 1 : return G_SOURCE_REMOVE;
151 : }
152 :
153 1 : static void test_glib_ev_source(void)
154 : {
155 1 : GMainContext *ctx = NULL;
156 1 : struct tevent_glib_glue *glue = NULL;
157 1 : struct test_glib_ev_source_data *state = NULL;
158 1 : GSource *source = NULL;
159 :
160 1 : ctx = g_main_context_new();
161 1 : g_assert(ctx != NULL);
162 :
163 1 : glue = samba_tevent_glib_glue_create(ev, ev, ctx);
164 1 : g_assert(glue != NULL);
165 :
166 1 : state = talloc_zero(glue, struct test_glib_ev_source_data);
167 1 : g_assert(state != NULL);
168 :
169 1 : state->ctx = ctx;
170 1 : state->glue = glue;
171 :
172 1 : source = g_timeout_source_new(100);
173 1 : g_source_set_callback(source,
174 : test_glib_ev_source_timeout_cb,
175 : state,
176 : NULL);
177 1 : g_source_attach(source, ctx);
178 1 : g_source_unref(source);
179 :
180 1 : g_assert(tevent_loop_wait(ev) == 0);
181 :
182 1 : TALLOC_FREE(glue);
183 1 : g_main_context_unref(ctx);
184 1 : }
185 :
186 : struct test_tevent_ev_source_data {
187 : GMainContext *ctx;
188 : struct tevent_glib_glue *glue;
189 : };
190 :
191 : static gboolean test_tevent_ev_source_quit_loop(gpointer data);
192 :
193 1 : static void test_tevent_ev_source_timeout_cb(struct tevent_context *_ev,
194 : struct tevent_timer *te,
195 : struct timeval current_time,
196 : void *data)
197 : {
198 1 : struct test_tevent_ev_source_data *state = talloc_get_type_abort(
199 : data, struct test_tevent_ev_source_data);
200 1 : GSource *source = NULL;
201 :
202 1 : source = g_timeout_source_new(100);
203 1 : g_source_set_callback(source,
204 : test_tevent_ev_source_quit_loop,
205 : state,
206 : NULL);
207 1 : g_source_attach(source, state->ctx);
208 1 : g_source_unref(source);
209 :
210 1 : return;
211 : }
212 :
213 1 : static gboolean test_tevent_ev_source_quit_loop(gpointer data)
214 : {
215 1 : struct test_tevent_ev_source_data *state = talloc_get_type_abort(
216 : data, struct test_tevent_ev_source_data);
217 :
218 1 : samba_tevent_glib_glue_quit(state->glue);
219 :
220 1 : return G_SOURCE_REMOVE;
221 : }
222 :
223 1 : static void test_tevent_ev_source(void)
224 : {
225 1 : GMainContext *ctx = NULL;
226 1 : struct tevent_glib_glue *glue = NULL;
227 1 : struct test_tevent_ev_source_data *state = NULL;
228 1 : struct tevent_timer *timer = NULL;
229 :
230 1 : ctx = g_main_context_new();
231 1 : g_assert(ctx != NULL);
232 :
233 1 : glue = samba_tevent_glib_glue_create(ev, ev, ctx);
234 1 : g_assert(glue != NULL);
235 :
236 1 : state = talloc_zero(glue, struct test_tevent_ev_source_data);
237 1 : g_assert(state != NULL);
238 :
239 1 : state->ctx = ctx;
240 1 : state->glue = glue;
241 :
242 1 : timer = tevent_add_timer(ev,
243 : state,
244 : tevent_timeval_current_ofs(0, 1000),
245 : test_tevent_ev_source_timeout_cb,
246 : state);
247 1 : g_assert(timer != NULL);
248 :
249 1 : g_assert(tevent_loop_wait(ev) == 0);
250 :
251 1 : TALLOC_FREE(glue);
252 1 : g_main_context_unref(ctx);
253 1 : }
254 :
255 : static gchar zeros[1024];
256 :
257 1 : static gsize fill_a_pipe(gint fd)
258 : {
259 1 : gsize written = 0;
260 1 : GPollFD pfd;
261 :
262 1 : pfd.fd = fd;
263 1 : pfd.events = G_IO_OUT;
264 62 : while (g_poll(&pfd, 1, 0) == 1)
265 : /* we should never see -1 here */
266 61 : written += write(fd, zeros, sizeof zeros);
267 :
268 1 : return written;
269 : }
270 :
271 131073 : static gboolean write_bytes(gint fd,
272 : GIOCondition condition,
273 : gpointer user_data)
274 : {
275 131073 : gssize *to_write = user_data;
276 131073 : gint limit;
277 :
278 131073 : if (*to_write == 0)
279 : return FALSE;
280 :
281 : /* Detect if we run before we should */
282 131072 : g_assert(*to_write >= 0);
283 :
284 131072 : limit = MIN(*to_write, sizeof zeros);
285 131072 : *to_write -= write(fd, zeros, limit);
286 :
287 131072 : return TRUE;
288 : }
289 :
290 131133 : static gboolean read_bytes(gint fd,
291 : GIOCondition condition,
292 : gpointer user_data)
293 : {
294 131133 : static gchar buffer[1024];
295 131133 : gssize *to_read = user_data;
296 :
297 131133 : *to_read -= read(fd, buffer, sizeof buffer);
298 :
299 : /* The loop will exit when there is nothing else to read, then we will
300 : * use g_source_remove() to destroy this source.
301 : */
302 131133 : return TRUE;
303 : }
304 :
305 1 : static void test_unix_fd(void)
306 : {
307 1 : gssize to_write = -1;
308 1 : gssize to_read;
309 1 : gint fds[2];
310 1 : gint a, b;
311 1 : gint s;
312 1 : GSource *source_a = NULL;
313 1 : GSource *source_b = NULL;
314 1 : struct tevent_glib_glue *glue = NULL;
315 :
316 1 : glue = samba_tevent_glib_glue_create(ev, ev, g_main_context_default());
317 1 : g_assert(glue != NULL);
318 :
319 1 : s = pipe(fds);
320 1 : g_assert(s == 0);
321 :
322 1 : to_read = fill_a_pipe(fds[1]);
323 : /* write at higher priority to keep the pipe full... */
324 1 : a = g_unix_fd_add_full(G_PRIORITY_HIGH,
325 : fds[1],
326 : G_IO_OUT,
327 : write_bytes,
328 : &to_write,
329 : NULL);
330 1 : source_a = g_source_ref(g_main_context_find_source_by_id(NULL, a));
331 : /* make sure no 'writes' get dispatched yet */
332 1 : while (tevent_loop_once(ev));
333 :
334 1 : to_read += 128 * 1024 * 1024;
335 1 : to_write = 128 * 1024 * 1024;
336 1 : b = g_unix_fd_add(fds[0], G_IO_IN, read_bytes, &to_read);
337 1 : source_b = g_source_ref(g_main_context_find_source_by_id(NULL, b));
338 :
339 : /* Assuming the kernel isn't internally 'laggy' then there will always
340 : * be either data to read or room in which to write. That will keep
341 : * the loop running until all data has been read and written.
342 : */
343 117 : while (to_write > 0 || to_read > 0)
344 : {
345 393338 : gssize to_write_was = to_write;
346 393338 : gssize to_read_was = to_read;
347 :
348 393338 : if (tevent_loop_once(ev) != 0)
349 : break;
350 :
351 : /* Since the sources are at different priority, only one of them
352 : * should possibly have run.
353 : */
354 393339 : g_assert(to_write == to_write_was || to_read == to_read_was);
355 : }
356 :
357 1 : g_assert(to_write == 0);
358 1 : g_assert(to_read == 0);
359 :
360 : /* 'a' is already removed by itself */
361 1 : g_assert(g_source_is_destroyed(source_a));
362 1 : g_source_unref(source_a);
363 1 : g_source_remove(b);
364 1 : g_assert(g_source_is_destroyed(source_b));
365 1 : g_source_unref(source_b);
366 :
367 1 : samba_tevent_glib_glue_quit(glue);
368 1 : TALLOC_FREE(glue);
369 :
370 1 : close(fds[1]);
371 1 : close(fds[0]);
372 1 : }
373 :
374 1 : int main(int argc, const char *argv[])
375 : {
376 1 : int test_argc = 3;
377 1 : char *test_argv[] = {
378 : discard_const("test_glib_glue"),
379 : discard_const("-m"),
380 : discard_const("no-undefined")
381 : };
382 1 : char **argvp = test_argv;
383 :
384 1 : g_test_init(&test_argc, &argvp, NULL);
385 :
386 1 : ev = tevent_context_init(NULL);
387 1 : if (ev == NULL) {
388 0 : exit(1);
389 : }
390 :
391 1 : g_test_add_func("/mainloop/timeouts", test_timeouts);
392 1 : g_test_add_func("/mainloop/glib_ev_source", test_glib_ev_source);
393 1 : g_test_add_func("/mainloop/tevent_ev_source", test_tevent_ev_source);
394 1 : g_test_add_func("/mainloop/unix-fd", test_unix_fd);
395 :
396 1 : return g_test_run();
397 : }
|