Code

Include config.h in source files.
[sysdb.git] / t / utils / unixsock_test.c
1 /*
2  * SysDB - t/utils/unixsock_test.c
3  * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 /* required for fopencookie support */
33 #define _GNU_SOURCE
35 #include "utils/unixsock.h"
36 #include "libsysdb_test.h"
38 #include <check.h>
40 #include <dlfcn.h>
42 #include <errno.h>
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <sys/un.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
52 #include <unistd.h>
54 /*
55  * I/O "hook" functions
56  */
58 typedef struct {
59         int fd;
60         size_t pos;
61 } io_cookie_t;
63 static struct {
64         const char *data;
65         size_t len;
66 } golden_data[] = {
67         { "a", 1 },
68         { "abc", 3 },
69         { "12345", 5 },
70         { "", 0 },
71 };
73 static char *last_write = NULL;
75 static unsigned long long mock_read_called = 0;
76 static ssize_t
77 mock_read(void *cookie, char *buf, size_t size)
78 {
79         io_cookie_t *c = cookie;
80         ssize_t ret;
82         ++mock_read_called;
84         if (c->pos >= SDB_STATIC_ARRAY_LEN(golden_data))
85                 return 0;
87         ret = snprintf(buf, size, "%s\n", golden_data[c->pos].data);
88         ++c->pos;
89         return ret;
90 } /* mock_read */
92 static unsigned long long mock_write_called = 0;
93 static ssize_t
94 mock_write(void *cookie, const char *buf, size_t size)
95 {
96         io_cookie_t *c = cookie;
98         ++mock_write_called;
100         if (c->pos >= SDB_STATIC_ARRAY_LEN(golden_data))
101                 return 0;
103         if (last_write)
104                 free(last_write);
105         last_write = strdup(buf);
106         ++c->pos;
107         return (ssize_t)size;
108 } /* mock_write */
110 /* unsupported: int seek(void *cookie, off64_t *offset, int whence) */
112 static int
113 mock_close(void *cookie)
115         io_cookie_t *c = cookie;
117         if (! c)
118                 return EBADF;
120         close(c->fd);
121         free(c);
122         return 0;
123 } /* mock_close */
125 static cookie_io_functions_t mock_io = {
126         /* read = */  mock_read,
127         /* write = */ mock_write,
128         /* seek = */  NULL,
129         /* close = */ mock_close,
130 };
132 /*
133  * mocked functions
134  */
136 static int myfd = -1;
138 static void *
139 dlopen_libc(void)
141         static void *libc = NULL;
143         if (libc)
144                 return libc;
146         libc = dlopen("libc.so.6", RTLD_LAZY);
147         if (! libc)
148                 fail("INTERNAL ERROR: failed to load libc");
149         return libc;
150 } /* dlopen_libc */
152 int
153 socket(int domain, int __attribute__((unused)) type,
154                 int __attribute__((unused)) protocol)
156         char tmp_file[] = "unixsock_test_socket.XXXXXX";
157         int tmp_fd;
159         /* we only want to mock UNIX sockets; return an error else */
160         if (domain != AF_UNIX) {
161                 errno = EAFNOSUPPORT;
162                 return -1;
163         }
165         /* create an 'anonymous' file to have a valid file-descriptor
166          * which can be close()ed by the caller */
167         tmp_fd = mkstemp(tmp_file);
168         if (tmp_fd < 0)
169                 return -1;
171         unlink(tmp_file);
172         myfd = tmp_fd;
173         return tmp_fd;
174 } /* socket */
176 int
177 connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
179         if (sockfd < 0) {
180                 errno = EBADF;
181                 return -1;
182         }
184         /* we only want to mock UNIX sockets; return an error else */
185         if ((addrlen != sizeof(struct sockaddr_un)) || (! addr)
186                         || (! ((const struct sockaddr_un *)addr)->sun_path)) {
187                 errno = EAFNOSUPPORT;
188                 return -1;
189         }
190         return 0;
191 } /* connect */
193 FILE *
194 fdopen(int fd, const char *mode)
196         io_cookie_t *cookie;
198         if (fd < 0) {
199                 errno = EBADF;
200                 return NULL;
201         }
203         /* check also uses fdopen; in that case we need
204          * to use the original implementation */
205         if (fd != myfd) {
206                 void *libc = dlopen_libc();
207                 FILE *(*orig_fdopen)(int, const char *) = dlsym(libc, "fdopen");
209                 if (! orig_fdopen)
210                         fail("INTERNAL ERROR: failed to load fdopen() from libc");
212                 return orig_fdopen(fd, mode);
213         }
215         cookie = calloc(sizeof(*cookie), 1);
216         if (! cookie)
217                 return NULL;
219         cookie->fd = fd;
220         cookie->pos = 0;
221         return fopencookie(cookie, mode, mock_io);
222 } /* fdopen */
224 /*
225  * private variables
226  */
228 static sdb_unixsock_client_t *client;
230 static void
231 setup(void)
233         client = sdb_unixsock_client_create("unixsock_test_path");
234         fail_unless(client != NULL,
235                         "sdb_unixsock_client_create() = NULL; "
236                         "expected unixsock client object");
237 } /* setup */
239 static void
240 teardown(void)
242         sdb_unixsock_client_destroy(client);
243         client = NULL;
244 } /* teardown */
246 static void
247 conn(void)
249         int check;
251         check = sdb_unixsock_client_connect(client);
252         fail_unless(check == 0,
253                         "sdb_unixsock_client_connect() = %i; expected: 0", check);
254 } /* conn */
256 /*
257  * tests
258  */
260 START_TEST(test_sdb_unixsock_client_create)
262         sdb_unixsock_client_t *c;
263         const char *check;
265         c = sdb_unixsock_client_create(NULL);
266         fail_unless(c == NULL,
267                         "sdb_unixsock_client_create() = %p; expected: NULL", c);
269         c = sdb_unixsock_client_create("unixsock_test_path");
270         fail_unless(c != NULL,
271                         "sdb_unixsock_client_create() = NULL; "
272                         "expected unixsock client object");
274         check = sdb_unixsock_client_path(c);
275         fail_unless(check != NULL,
276                         "sdb_unixsock_client_create() did not store path name");
277         fail_unless(!strcmp(check, "unixsock_test_path"),
278                         "sdb_unixsock_client_create() did not store correct path name; "
279                         "got: '%s'; expected: 'unixsock_test_path'", check);
280         sdb_unixsock_client_destroy(c);
282 END_TEST
284 START_TEST(test_sdb_unixsock_client_connect)
286         int check;
288         check = sdb_unixsock_client_connect(client);
289         fail_unless(check == 0,
290                         "sdb_unixsock_client_connect() = %i; expected: 0", check);
292 END_TEST
294 START_TEST(test_sdb_unixsock_client_send)
296         size_t i;
298         conn();
300         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
301                 int check;
303                 mock_write_called = 0;
304                 check = sdb_unixsock_client_send(client, golden_data[i].data);
305                 /* client_send appends \r\n */
306                 fail_unless((size_t)check == golden_data[i].len + 2,
307                                 "sdb_unixsock_client_send() = %i; expected: %zu",
308                                 check, golden_data[i].len + 2);
309                 fail_unless(mock_write_called == 1,
310                                 "sdb_unixsock_client_send() called mock_write %llu times; "
311                                 "expected: 1", mock_write_called);
312                 fail_unless(last_write != NULL,
313                                 "INTERNAL ERROR: mock_write did not record last write");
314                 fail_unless((last_write[check - 1] == '\n')
315                                         && (last_write[check - 2] == '\r'),
316                                 "sdb_unixsock_client_send() did not append \\r\\n "
317                                 "before sending; got: '%s'", last_write);
318                 fail_unless(!strncmp(last_write, golden_data[i].data,
319                                         (size_t)check - 2),
320                                 "sdb_unixsock_client_send() sent unexpected string '%s'; "
321                                 "expected: '%s'", last_write, golden_data[i].data);
322                 free(last_write);
323                 last_write = NULL;
324         }
326 END_TEST
328 START_TEST(test_sdb_unixsock_client_recv)
330         size_t i;
332         conn();
334         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
335                 char *check;
336                 char buf[64];
338                 mock_read_called = 0;
339                 check = sdb_unixsock_client_recv(client, buf, sizeof(buf));
340                 fail_unless(check != NULL,
341                                 "sdb_unixsock_client_recv() = NULL; expected: a string");
342                 fail_unless(check == buf,
343                                 "sdb_unixsock_client_recv() did not return a pointer "
344                                 "to the user-provided buffer");
345                 fail_unless(mock_read_called == 1,
346                                 "sdb_unixsock_client_recv() called mock_read %llu times; "
347                                 "expected: 1", mock_read_called);
348                 fail_unless(strlen(check) == golden_data[i].len,
349                                 "sdb_unixsock_client_recv() returned string of length "
350                                 "%zu ('%s'); expected: %zu",
351                                 strlen(check), check, golden_data[i].len);
352                 fail_unless(check[golden_data[i].len] != '\n',
353                                 "sdb_unixsock_client_recv() did not strip newline");
354                 fail_unless(!strcmp(check, golden_data[i].data),
355                                 "sdb_unixsock_client_recv() = '%s'; expected: '%s'",
356                                 check, golden_data[i].data);
357         }
359 END_TEST
361 Suite *
362 util_unixsock_suite(void)
364         Suite *s = suite_create("utils::unixsock");
365         TCase *tc;
367         tc = tcase_create("core");
368         tcase_add_checked_fixture(tc, setup, teardown);
369         tcase_add_test(tc, test_sdb_unixsock_client_create);
370         tcase_add_test(tc, test_sdb_unixsock_client_connect);
371         tcase_add_test(tc, test_sdb_unixsock_client_send);
372         tcase_add_test(tc, test_sdb_unixsock_client_recv);
373         suite_add_tcase(s, tc);
375         return s;
376 } /* util_unixsock_suite */
378 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */