Imported Upstream version 3.3.3
[debian/amanda] / common-src / event-test.c
1 /*
2  * Copyright (c) 2008-2012 Zmanda, Inc.  All Rights Reserved.
3  * 
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * for more details.
13  * 
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17  * 
18  * Contact information: Zmanda Inc, 465 S. Mathilda Ave., Suite 300
19  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
20  *
21  * Author: Dustin J. Mitchell <dustin@zmanda.com>
22  */
23
24 #include "amanda.h"
25 #include "testutils.h"
26 #include "event.h"
27
28 /* a random global variable to flag that some function has been called */
29 static int global;
30
31 /* file descriptor under EV_READFD or EV_WRITEFD */
32 static int cb_fd;
33
34 /* and some easy access to the event handles for callbacks */
35 static event_handle_t *hdl[10];
36
37 /*
38  * Utils
39  */
40
41 /* A common event callback that just decrements 'global', and frees
42  * hdl[0] if global reaches zero.
43  */
44 static void
45 test_decrement_cb(void *up G_GNUC_UNUSED)
46 {
47     global--;
48     tu_dbg("Decrement global to %d\n", global);
49     if (global == 0) {
50         tu_dbg("Release event\n");
51         event_release(hdl[0]);
52     }
53 }
54
55 /*
56  * Tests
57  */
58
59 /****
60  * Test that EV_TIME events fire, repeatedly.
61  */
62 static gboolean
63 test_ev_time(void)
64 {
65     global = 2;
66     hdl[0] = event_register(1, EV_TIME, test_decrement_cb, NULL);
67
68     /* Block waiting for the event to fire.  The event itself eventually
69      * unregisters itself, causing the event_loop to finish */
70     event_loop(0);
71
72     return (global == 0);
73 }
74
75 /****
76  * Test that nonblocking waits don't block.
77  */
78 static gboolean
79 test_nonblock(void)
80 {
81     global = 1; /* the callback should not be triggered, so this should stay 1 */
82     hdl[0] = event_register(1, EV_TIME, test_decrement_cb, NULL);
83
84     event_loop(1); /* non-blocking */
85
86     return (global != 0);
87 }
88
89 /****
90  * Test that EV_WAIT events fire when event_wakeup is called, without waiting for
91  * another iteration of the event loop.  Security API depends on callbacks occuring
92  * immediately.
93  */
94 static gboolean
95 test_ev_wait(void)
96 {
97     global = 2;
98     hdl[0] = event_register(4422, EV_WAIT, test_decrement_cb, NULL);
99
100     if (global != 2) return FALSE;
101     event_wakeup(4422);
102     if (global != 1) return FALSE;
103     event_wakeup(4422);
104     if (global != 0) return FALSE;
105     event_wakeup(4422); /* the handler has been removed, but this is not an error */
106     if (global != 0) return FALSE;
107
108     /* queue should now be empty, so this won't block */
109     event_loop(0);
110
111     return TRUE;
112 }
113
114 /****
115  * Test that EV_WAIT events with the same ID added during an EV_WAIT callback are not
116  * called back immediately, but wait for a subsequent wakeup.  Security API depends on
117  * this behavior.  This is a pathological test :)
118  */
119 static void
120 test_ev_wait_2_cb(void *up G_GNUC_UNUSED)
121 {
122     global--;
123     tu_dbg("Decrement global to %d\n", global);
124
125     if (global >= 0) {
126         tu_dbg("release EV_WAIT event\n");
127         event_release(hdl[0]);
128     }
129     if (global > 0) {
130         tu_dbg("register new EV_WAIT event with same ID\n");
131         hdl[0] = event_register(84, EV_WAIT, test_ev_wait_2_cb, NULL);
132     }
133 }
134
135 static gboolean
136 test_ev_wait_2(void)
137 {
138     global = 2;
139     hdl[0] = event_register(84, EV_WAIT, test_ev_wait_2_cb, NULL);
140
141     /* Each wakeup should only invoke the callback *once* */
142     if (global != 2) return FALSE;
143     event_wakeup(84);
144     if (global != 1) return FALSE;
145     event_wakeup(84);
146     if (global != 0) return FALSE;
147     event_wakeup(84); /* the handler has been removed, but this is not an error */
148     if (global != 0) return FALSE;
149
150     return TRUE;
151 }
152
153 /****
154  * Test that event_wait correctly waits for a EV_TIME event to fire, even when
155  * other events are running.  */
156 static void
157 test_event_wait_cb(void *up G_GNUC_UNUSED)
158 {
159     int *cb_fired = (int *)up;
160     (*cb_fired) = 1;
161
162     /* immediately unregister ourselves */
163     tu_dbg("test_event_wait_cb called\n");
164     event_release(hdl[1]);
165 }
166
167 static gboolean
168 test_event_wait(void)
169 {
170     int cb_fired = 0;
171     global = 3;
172
173     /* this one serves as a "decoy", running in the background while we wait
174      * for test_event_wait_cb */
175     hdl[0] = event_register(1, EV_TIME, test_decrement_cb, NULL);
176
177     /* this is our own callback */
178     hdl[1] = event_register(2, EV_TIME, test_event_wait_cb, (void *)&cb_fired);
179
180     /* wait until our own callback fires */
181     event_wait(hdl[1]);
182
183     /* at this point, test_decrement_cb should have fired once or twice, but not
184      * three times */
185     if (global == 0) {
186         tu_dbg("global is already zero!\n");
187         return FALSE;
188     }
189
190     /* and our own callback should have fired */
191     if (!cb_fired) {
192         tu_dbg("test_event_wait_cb didn't fire\n");
193         return FALSE;
194     }
195
196     return TRUE;
197 }
198
199 /****
200  * Test that event_wait correctly waits for a EV_WAIT event to be released, not 
201  * fired, even when other events are running.  */
202 static void
203 test_event_wait_2_cb(void *up)
204 {
205     int *wakeups_remaining = (int *)up;
206     tu_dbg("test_event_wait_2_cb called\n");
207
208     if (--(*wakeups_remaining) == 0) {
209         /* unregister ourselves if we've awakened enough times */
210         event_release(hdl[2]);
211         hdl[2] = NULL;
212     }
213 }
214
215 static void
216 test_event_wait_2_wakeup_cb(void *up G_GNUC_UNUSED)
217 {
218     tu_dbg("test_event_wait_2_wakeup_cb called\n");
219
220     /* wake up the EV_WAIT event */
221     event_wakeup(9876);
222 }
223
224 static gboolean
225 test_event_wait_2(void)
226 {
227     int wakeups_remaining = 2;
228     global = 3;
229
230     /* this one serves as a "decoy", running in the background while we wait
231      * for test_event_wait_2_cb */
232     hdl[0] = event_register(1, EV_TIME, test_decrement_cb, NULL);
233
234     /* This one repeatedly calls event_wakeup for the EV_WAIT event */
235     hdl[1] = event_register(1, EV_TIME, test_event_wait_2_wakeup_cb, NULL);
236
237     /* this is our own callback */
238     hdl[2] = event_register(9876, EV_WAIT, test_event_wait_2_cb, (void *)&wakeups_remaining);
239
240     /* wait until the EV_WAIT is *released*, not just fired. */
241     event_wait(hdl[2]);
242
243     /* at this point, test_decrement_cb should have fired twice, but not
244      * three times */
245     if (global == 0) {
246         tu_dbg("global is already zero!\n");
247         return FALSE;
248     }
249
250     /* and our own callback should have fired twice, not just once */
251     if (wakeups_remaining != 0) {
252         tu_dbg("test_event_wait_2_cb didn't fire twice\n");
253         return FALSE;
254     }
255
256     return TRUE;
257 }
258
259 /****
260  * Test that EV_READFD is triggered correctly when there's data available
261  * for reading.  The source of read events is a spawned child which writes
262  * lots of data to a pipe, in hopes of overflowing the pipe buffer.
263  */
264 static void
265 test_ev_readfd_cb(void *up G_GNUC_UNUSED)
266 {
267     char buf[1024];
268     int len;
269
270     /* read from the fd until we're out of bytes */
271     tu_dbg("reader: callback executing\n");
272     len = read(cb_fd, buf, sizeof(buf));
273     if (len == 0) {
274         tu_dbg("reader: callback returning\n");
275     } else if (len < 0) {
276         tu_dbg("reader: read() returned %d: %s\n", len, strerror(errno));
277         /* do we need to handle e.g., EAGAIN here? */
278     } else {
279         tu_dbg("reader: read %d bytes\n", len);
280         global -= len;
281         /* release this event if we've read all of the available bytes */
282         if (global <= 0) {
283             close(cb_fd);
284             event_release(hdl[0]);
285         }
286     }
287 }
288
289 static void
290 test_ev_readfd_writer(int fd, size_t count)
291 {
292     char buf[256];
293     size_t i;
294
295     for (i = 0; i < sizeof(buf); i++) {
296         buf[i] = (char)i;
297     }
298
299     while (count > 0) {
300         int len;
301
302         len = write(fd, buf, min(sizeof(buf), count));
303         tu_dbg("writer wrote %d bytes\n", len);
304         count -= len;
305     }
306
307     close(fd);
308 }
309
310 #define TEST_EV_READFD_SIZE (1024*1024)
311
312 static gboolean
313 test_ev_readfd(void)
314 {
315     int writer_pid;
316     int p[2];
317
318     /* make a pipe */
319     if (pipe(p) == -1) {
320         exit(1);
321     }
322
323     /* fork off the writer */
324     switch (writer_pid = fork()) {
325         case 0: /* child */
326             close(p[0]);
327             test_ev_readfd_writer(p[1], TEST_EV_READFD_SIZE);
328             exit(0);
329             break;
330
331         case -1: /* error */
332             perror("fork");
333             return FALSE;
334
335         default: /* parent */
336             break;
337     }
338
339     /* set up a EV_READFD on the read end of the pipe */
340     cb_fd = p[0];
341     fcntl(cb_fd, F_SETFL, O_NONBLOCK);
342     close(p[1]);
343     global = TEST_EV_READFD_SIZE;
344     hdl[0] = event_register(p[0], EV_READFD, test_ev_readfd_cb, NULL);
345
346     /* let it run */
347     event_loop(0);
348
349     tu_dbg("waiting for writer to die..\n");
350     waitpid(writer_pid, NULL, 0);
351
352     if (global != 0) {
353         tu_dbg("%d bytes remain unread..\n", global);
354         return FALSE;
355     }
356
357     return TRUE;
358 }
359
360 /****
361  * Test the combination of an EV_TIME and an EV_READFD to peform a 
362  * timeout-protected read that times out.
363  */
364 static void
365 test_read_timeout_slow_writer(int fd)
366 {
367     char buf[] = "OH NO!";
368
369     /* this should exceed the timeout, which is 1s */
370     sleep(2);
371
372     if (write(fd, buf, strlen(buf)+1) == -1) {
373         exit(1);
374     }
375     close(fd);
376 }
377
378 static void
379 test_read_timeout_cb(void *up G_GNUC_UNUSED)
380 {
381     tu_dbg("read timed out (this is supposed to happen)\n");
382     global = 1234; /* sentinel value */
383
384     /* free up all of the events so that event_loop returns */
385     event_release(hdl[0]);
386     event_release(hdl[1]);
387 }
388
389 static gboolean
390 test_read_timeout(void)
391 {
392     int writer_pid;
393     int p[2];
394
395     /* make a pipe */
396     if (pipe(p) == -1) {
397         exit(1);
398     }
399
400     /* fork off the writer */
401     switch (writer_pid = fork()) {
402         case 0: /* child */
403             close(p[0]);
404             test_read_timeout_slow_writer(p[1]);
405             exit(0);
406             break;
407
408         case -1: /* error */
409             perror("fork");
410             return FALSE;
411
412         default: /* parent */
413             break;
414     }
415
416     /* set up a EV_READFD on the read end of the pipe */
417     cb_fd = p[0];
418     fcntl(cb_fd, F_SETFL, O_NONBLOCK);
419     close(p[1]);
420     hdl[0] = event_register(p[0], EV_READFD, test_ev_readfd_cb, NULL);
421
422     /* and set up a timeout */
423     global = 0; /* timeout_cb will set this to 1234 */
424     hdl[1] = event_register(1, EV_TIME, test_read_timeout_cb, NULL);
425
426     /* let it run */
427     event_loop(0);
428
429     /* see if we got the sentinel indicating the timeout fired */
430     if (global != 1234)
431         return FALSE;
432
433     return TRUE;
434 }
435
436 /****
437  * Test that EV_WRITEFD is triggered correctly when there's buffer space to
438  * support a write.  
439  */
440
441 static void
442 test_ev_writefd_cb(void *up G_GNUC_UNUSED)
443 {
444     char buf[1024];
445     int len;
446     unsigned int i;
447
448     /* initialize the buffer to something worthwhile */
449     for (i = 0; i < sizeof(buf); i++) {
450         buf[i] = (char)i;
451     }
452
453     /* write some bytes, but no more than global */
454     tu_dbg("test_ev_writefd_cb called\n");
455     while (1) {
456         len = write(cb_fd, buf, min((size_t)global, sizeof(buf)));
457         if (len < 0) {
458             tu_dbg("test_ev_writefd_cb: write() returned %d\n", len);
459             return;
460         } else if (len == 0) {
461             /* do we need to handle EAGAIN, etc. here? */
462             tu_dbg("test_ev_writefd_cb done\n");
463             return;
464         }
465         tu_dbg(" write() wrote %d bytes\n", len);
466         global -= len;
467         if (global <= 0) {
468             close(cb_fd);
469             event_release(hdl[0]);
470             return;
471         }
472     }
473 }
474
475 static void
476 test_ev_writefd_consumer(int fd, size_t count)
477 {
478     while (count > 0) {
479         char buf[1024];
480         int len;
481
482         tu_dbg("reader: calling read(%d)\n", (int)sizeof(buf));
483         len = read(fd, buf, sizeof(buf));
484
485         /* exit on a read error or EOF */
486         if (len < 1) return;
487
488         tu_dbg("reader: read() returned %d bytes\n", len);
489
490         count -= len;
491     }
492 }
493
494 #define TEST_EV_WRITEFD_SIZE (1024*1024)
495
496 static gboolean
497 test_ev_writefd(void)
498 {
499     int reader_pid;
500     int p[2];
501
502     /* make a pipe */
503     if (pipe(p) == -1) {
504         exit(1);
505     }
506
507     /* fork off the reader */
508     switch (reader_pid = fork()) {
509         case 0: /* child */
510             close(p[1]);
511             test_ev_writefd_consumer(p[0], TEST_EV_WRITEFD_SIZE);
512             exit(0);
513             break;
514
515         case -1: /* error */
516             perror("fork");
517             return FALSE;
518
519         default: /* parent */
520             break;
521     }
522
523     /* set up a EV_WRITEFD on the write end of the pipe */
524     cb_fd = p[1];
525     fcntl(cb_fd, F_SETFL, O_NONBLOCK);
526     global = TEST_EV_WRITEFD_SIZE;
527     close(p[0]);
528     hdl[0] = event_register(p[1], EV_WRITEFD, test_ev_writefd_cb, NULL);
529
530     /* let it run */
531     event_loop(0);
532
533     tu_dbg("waiting for reader to die..\n");
534     waitpid(reader_pid, NULL, 0);
535
536     /* and see what we got */
537     if (global != 0) {
538         tu_dbg("writes did not complete\n");
539         return FALSE;
540     }
541
542     return TRUE;
543 }
544
545 /****
546  * Test that a child_watch_source works correctly.
547  */
548
549 static gint test_child_watch_result = 0;
550 static GMainLoop *test_child_watch_main_loop = NULL;
551
552 static void
553 test_child_watch_callback(
554     pid_t pid,
555     gint status,
556     gpointer data)
557 {
558     static int count = 0;
559     gint expected_pid = GPOINTER_TO_INT(data);
560
561     if (pid != expected_pid
562             || !WIFEXITED(status)
563             || WEXITSTATUS(status) != 13)
564         test_child_watch_result = FALSE;
565     else
566         test_child_watch_result = TRUE;
567
568     count++;
569     if(count >= 2)
570         g_main_loop_quit(test_child_watch_main_loop);
571 }
572
573 static gboolean
574 test_child_watch_source(void)
575 {
576     int pid, pid2;
577     GSource *src, *src2;
578
579     /* fork off the child we want to watch die */
580     switch (pid = fork()) {
581         case 0: /* child */
582             exit(13);
583             break;
584
585         case -1: /* error */
586             perror("fork");
587             return FALSE;
588
589         default: /* parent */
590             break;
591     }
592
593     /* set up a child watch */
594     src = new_child_watch_source(pid);
595     g_source_set_callback(src, (GSourceFunc)test_child_watch_callback,
596              GINT_TO_POINTER(pid), NULL);
597     g_source_attach(src, NULL);
598     g_source_unref(src);
599
600     switch (pid2 = fork()) {
601         case 0: /* child */
602             exit(13);
603             break;
604
605         case -1: /* error */
606             perror("fork");
607             return FALSE;
608
609         default: /* parent */
610             break;
611     }
612
613     sleep(1);
614     /* set up a child watch */
615     src2 = new_child_watch_source(pid2);
616     g_source_set_callback(src2, (GSourceFunc)test_child_watch_callback,
617              GINT_TO_POINTER(pid2), NULL);
618     g_source_attach(src2, NULL);
619     g_source_unref(src2);
620
621     /* let it run */
622     test_child_watch_main_loop = g_main_loop_new(NULL, 1);
623     g_main_loop_run(test_child_watch_main_loop);
624
625     return test_child_watch_result;
626 }
627
628 /*
629  * Main driver
630  */
631
632 int
633 main(int argc, char **argv)
634 {
635     static TestUtilsTest tests[] = {
636         TU_TEST(test_ev_time, 90),
637         TU_TEST(test_ev_wait, 90),
638         TU_TEST(test_ev_wait_2, 90),
639         TU_TEST(test_ev_readfd, 120), /* runs slowly on old kernels */
640         TU_TEST(test_ev_writefd, 90),
641         TU_TEST(test_event_wait, 90),
642         TU_TEST(test_event_wait_2, 90),
643         TU_TEST(test_nonblock, 90),
644         TU_TEST(test_read_timeout, 90),
645         TU_TEST(test_child_watch_source, 90),
646         /* fdsource is used by ev_readfd/ev_writefd, and is sufficiently tested there */
647         TU_END()
648     };
649
650     return testutils_run_tests(argc, argv, tests);
651 }