1/*
2 * Copyright (c) 2009 Mark Heily <mark@heily.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <sys/types.h>
18#include <poll.h>
19
20#include "common.h"
21
22struct unit_test {
23    const char *ut_name;
24    int         ut_enabled;
25    void      (*ut_func)(int);
26};
27
28/*
29 * Test the method for detecting when one end of a socketpair
30 * has been closed. This technique is used in kqueue_validate()
31 */
32static void
33test_peer_close_detection(void)
34{
35    int sockfd[2];
36    char buf[1];
37    struct pollfd pfd;
38
39    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0)
40        die("socketpair");
41
42    pfd.fd = sockfd[0];
43    pfd.events = POLLIN | POLLHUP;
44    pfd.revents = 0;
45
46    if (poll(&pfd, 1, 0) > 0)
47        die("unexpected data");
48
49    if (close(sockfd[1]) < 0)
50        die("close");
51
52    if (poll(&pfd, 1, 0) > 0) {
53        if (recv(sockfd[0], buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT) != 0)
54            die("failed to detect peer shutdown");
55    }
56}
57
58void
59test_kqueue(void)
60{
61    int kqfd;
62
63    if ((kqfd = kqueue()) < 0)
64        die("kqueue()");
65    test_no_kevents(kqfd);
66    if (close(kqfd) < 0)
67        die("close()");
68}
69
70void
71test_kevent(void)
72{
73    struct kevent kev;
74
75    memset(&kev, 0, sizeof(kev));
76
77    /* Provide an invalid kqueue descriptor */
78    if (kevent(-1, &kev, 1, NULL, 0, NULL) == 0)
79        die("invalid kq parameter");
80}
81
82
83void
84test_ev_receipt(void)
85{
86    int kq;
87    struct kevent kev;
88
89    if ((kq = kqueue()) < 0)
90        die("kqueue()");
91#if HAVE_EV_RECEIPT
92
93    EV_SET(&kev, SIGUSR2, EVFILT_SIGNAL, EV_ADD | EV_RECEIPT, 0, 0, NULL);
94    if (kevent(kq, &kev, 1, &kev, 1, NULL) < 0)
95        die("kevent");
96
97    /* TODO: check the receipt */
98
99    close(kq);
100#else
101    memset(&kev, 0, sizeof(kev));
102    puts("Skipped -- EV_RECEIPT is not available");
103#endif
104}
105
106int
107main(int argc, char **argv)
108{
109    struct unit_test tests[] = {
110        { "socket", 1, test_evfilt_read },
111        { "signal", 1, test_evfilt_signal },
112#if FIXME
113        { "proc", 1, test_evfilt_proc },
114#endif
115        { "vnode", 1, test_evfilt_vnode },
116        { "timer", 1, test_evfilt_timer },
117#if HAVE_EVFILT_USER
118        { "user", 1, test_evfilt_user },
119#endif
120        { NULL, 0, NULL },
121    };
122    struct unit_test *test;
123    char *arg;
124    int match, kqfd;
125
126    /* If specific tests are requested, disable all tests by default */
127    if (argc > 1) {
128        for (test = &tests[0]; test->ut_name != NULL; test++) {
129            test->ut_enabled = 0;
130        }
131    }
132
133    while (argc > 1) {
134        match = 0;
135        arg = argv[1];
136        for (test = &tests[0]; test->ut_name != NULL; test++) {
137            if (strcmp(arg, test->ut_name) == 0) {
138                test->ut_enabled = 1;
139                match = 1;
140                break;
141            }
142        }
143        if (!match) {
144            printf("ERROR: invalid option: %s\n", arg);
145            exit(1);
146        } else {
147            printf("enabled test: %s\n", arg);
148        }
149        argv++;
150        argc--;
151    }
152
153    testing_begin();
154
155    test(peer_close_detection);
156
157    test(kqueue);
158    test(kevent);
159
160    if ((kqfd = kqueue()) < 0)
161        die("kqueue()");
162
163    for (test = &tests[0]; test->ut_name != NULL; test++) {
164        if (test->ut_enabled)
165            test->ut_func(kqfd);
166    }
167
168    test(ev_receipt);
169
170    testing_end();
171
172    return (0);
173}
174