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