* rework stringbuffered reading on nonblocking files
[citadel.git] / libcitadel / tests / stringbuf_IO_test.c
1
2 /*
3  *  CUnit - A Unit testing framework library for C.
4  *  Copyright (C) 2001  Anil Kumar
5  *  
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Library General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Library General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Library General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23
24 #include <sys/select.h>
25
26 #include <ctype.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <signal.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <sys/socket.h>
33 #include <sys/time.h>
34 #include <sys/stat.h>
35 #include <limits.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <sys/un.h>
39 #include <netdb.h>
40 #include <sys/poll.h>
41 #include <string.h>
42 #include <pwd.h>
43 #include <errno.h>
44 #include <stdarg.h>
45 #include <pthread.h>
46 #include <signal.h>
47 #include <sys/utsname.h>
48 #include <string.h>
49
50
51 typedef void testfunc(int Sock);
52
53 #define LISTEN_QUEUE_LENGTH 100
54 #include <string.h>
55
56 #include "stringbuf_test.h"
57 #include "../lib/libcitadel.h"
58
59 int msock;                      /* master listening socket */
60 int BindPort;
61 int time_to_die=0;
62 int listen_port=6666;
63 int n_Lines_to_read = 0;
64 int blobsize = 0;
65 int timeout = 5;
66 int selres = 1;
67 char ip_addr[256]="0.0.0.0";
68
69
70 static void TestRevalidateStrBuf(StrBuf *Buf)
71 {
72         CU_ASSERT(strlen(ChrPtr(Buf)) == StrLength(Buf));
73 }
74
75 /* 
76  * This is a generic function to set up a master socket for listening on
77  * a TCP port.  The server shuts down if the bind fails.
78  *
79  * ip_addr      IP address to bind
80  * port_number  port number to bind
81  * queue_len    number of incoming connections to allow in the queue
82  */
83 static int ig_tcp_server(char *ip_addr, int port_number, int queue_len)
84 {
85         struct protoent *p;
86         struct sockaddr_in sin;
87         int s, i;
88
89         memset(&sin, 0, sizeof(sin));
90         sin.sin_family = AF_INET;
91         if (ip_addr == NULL) {
92                 sin.sin_addr.s_addr = INADDR_ANY;
93         } else {
94                 sin.sin_addr.s_addr = inet_addr(ip_addr);
95         }
96
97         if (sin.sin_addr.s_addr == INADDR_NONE) {
98                 sin.sin_addr.s_addr = INADDR_ANY;
99         }
100
101         if (port_number == 0) {
102                 printf("Cannot start: no port number specified.\n");
103                 return (-1);
104         }
105         sin.sin_port = htons((u_short) port_number);
106
107         p = getprotobyname("tcp");
108
109         s = socket(PF_INET, SOCK_STREAM, (p->p_proto));
110         if (s < 0) {
111                 printf("Can't create a socket: %s\n", strerror(errno));
112                 return (-2);
113         }
114         /* Set some socket options that make sense. */
115         i = 1;
116         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
117
118         #ifndef __APPLE__
119         fcntl(s, F_SETFL, O_NONBLOCK); /* maide: this statement is incorrect
120                                           there should be a preceding F_GETFL
121                                           and a bitwise OR with the previous
122                                           fd flags */
123         #endif
124         
125         if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
126                 printf("Can't bind: %s\n", strerror(errno));
127                 return (-3);
128         }
129         if (listen(s, queue_len) < 0) {
130                 printf("Can't listen: %s\n", strerror(errno));
131                 return (-4);
132         }
133         return (s);
134 }
135
136
137
138 /*
139  * Entry point for worker threads
140  */
141 static void worker_entry(testfunc F)
142 {
143         int ssock;
144         int i = 0;
145         int fail_this_transaction = 0;
146         int ret;
147         struct timeval tv;
148         fd_set readset, tempset;
149
150         
151         tv.tv_sec = 0;
152         tv.tv_usec = 10000;
153         FD_ZERO(&readset);
154         FD_SET(msock, &readset);
155
156         do {
157                 /* Only one thread can accept at a time */
158                 fail_this_transaction = 0;
159                 ssock = -1; 
160                 errno = EAGAIN;
161                 do {
162                         ret = -1; /* just one at once should select... */
163                         
164                         FD_ZERO(&tempset);
165                         if (msock > 0) FD_SET(msock, &tempset);
166                         tv.tv_sec = 0;
167                         tv.tv_usec = 10000;
168                         if (msock > 0)  
169                                 ret = select(msock+1, &tempset, NULL, NULL,  &tv);
170                         if ((ret < 0) && (errno != EINTR) && (errno != EAGAIN))
171                         {/* EINTR and EAGAIN are thrown but not of interest. */
172                                 printf("accept() failed:%d %s\n",
173                                         errno, strerror(errno));
174                         }
175                         else if ((ret > 0) && (msock > 0) && FD_ISSET(msock, &tempset))
176                         {/* Successfully selected, and still not shutting down? Accept! */
177                                 ssock = accept(msock, NULL, 0);
178                         }
179                         
180                 } while ((msock > 0) && (ssock < 0)  && (time_to_die == 0));
181
182                 if ((msock == -1)||(time_to_die))
183                 {/* ok, we're going down. */
184                         return;
185                 }
186                 if (ssock < 0 ) continue;
187
188                 if (msock < 0) {
189                         if (ssock > 0) close (ssock);
190                         printf( "inbetween.");
191                         return;
192                 } else { /* Got it? do some real work! */
193                         /* Set the SO_REUSEADDR socket option */
194                         int fdflags; 
195                         i = 1;
196                         setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR,
197                                    &i, sizeof(i));
198
199                         fdflags = fcntl(ssock, F_GETFL);
200                         if (fdflags < 0)
201                                 printf("unable to get server socket flags! %s \n",
202                                         strerror(errno));
203                         fdflags = fdflags | O_NONBLOCK;
204                         if (fcntl(ssock, F_SETFL, fdflags) < 0)
205                                 printf("unable to set server socket nonblocking flags! %s \n",
206                                         strerror(errno));
207
208
209                         F(ssock);
210                 }
211
212         } while (!time_to_die);
213         printf ("bye\n");
214 }
215
216
217 static void SimpleLineBufTestFunc(int sock)
218 {
219         StrBuf *ReadBuffer;
220         StrBuf *Line;
221         const char *Pos = NULL;
222         const char *err = NULL;
223         int i;
224
225         ReadBuffer = NewStrBuf();
226         Line = NewStrBuf();
227
228         for (i = 0; i < n_Lines_to_read; i++) {
229                 StrBufTCP_read_buffered_line_fast(Line, 
230                                                   ReadBuffer, 
231                                                   &Pos,
232                                                   &sock,
233                                                   timeout,
234                                                   selres,
235                                                   &err);
236                 TestRevalidateStrBuf(Line);
237                 if (err != NULL)
238                         printf("%s", err);
239                 CU_ASSERT_PTR_NULL(err);
240                 CU_ASSERT_NOT_EQUAL(sock, -1);
241                 if (sock == -1)
242                         break;
243                 printf("LINE: >%s<\n", ChrPtr(Line));
244         }
245         FreeStrBuf(&ReadBuffer);
246         FreeStrBuf(&Line);
247         time_to_die = 1;
248 }
249
250 static void SimpleLinebufferTest(void)
251 {
252         msock = ig_tcp_server(ip_addr, listen_port, LISTEN_QUEUE_LENGTH);
253
254         worker_entry(SimpleLineBufTestFunc);
255         close (msock);
256 }
257
258
259 static void SimpleBlobTestFunc(int sock)
260 {
261         StrBuf *ReadBuffer;
262         StrBuf *Blob;
263         const char *Pos = NULL;
264         const char *err = NULL;
265         
266         ReadBuffer = NewStrBuf();
267         Blob = NewStrBuf();
268
269         StrBufReadBLOBBuffered(Blob, 
270                                ReadBuffer, 
271                                &Pos,
272                                &sock,
273                                0,
274                                blobsize,
275                                0,
276                                &err);
277         TestRevalidateStrBuf(Blob);
278         if (err != NULL)
279                 printf("%s", err);
280         CU_ASSERT(blobsize == StrLength(Blob));
281         CU_ASSERT_PTR_NULL(err);
282         CU_ASSERT_NOT_EQUAL(sock, -1);
283         if (sock == -1)
284         printf("BLOB: >%s<\n", ChrPtr(Blob));
285         
286         FreeStrBuf(&ReadBuffer);
287         FreeStrBuf(&Blob);
288         time_to_die = 1;
289 }
290
291
292 static void SimpleHttpPostTestFunc(int sock)
293 {
294         StrBuf *ReadBuffer;
295         StrBuf *Blob;
296         StrBuf *Line;
297         const char *Pos = NULL;
298         const char *err = NULL;
299         int blobsize = 0;
300         int i;
301         const char *pch;
302         
303         ReadBuffer = NewStrBuf();
304         Blob = NewStrBuf();
305         Line = NewStrBuf();
306
307         for (i = 0; (i == 0) || (StrLength(Line) != 0); i++) {
308                 StrBufTCP_read_buffered_line_fast(Line, 
309                                                   ReadBuffer, 
310                                                   &Pos,
311                                                   &sock,
312                                                   timeout,
313                                                   selres,
314                                                   &err);
315                 TestRevalidateStrBuf(Line);
316                 if (err != NULL)
317                         printf("%s", err);
318                 CU_ASSERT_PTR_NULL(err);
319                 CU_ASSERT_NOT_EQUAL(sock, -1);
320                 if (sock == -1)
321                         break;
322                 printf("LINE: >%s<\n", ChrPtr(Line));
323                 pch = strstr(ChrPtr(Line), "Content-Length");
324                 if (pch != NULL) {
325                         blobsize = atol(ChrPtr(Line) + 
326                                         sizeof("Content-Length:"));
327
328                 }
329                 FlushStrBuf(Line);
330         }
331
332         StrBufReadBLOBBuffered(Blob, 
333                                ReadBuffer, 
334                                &Pos,
335                                &sock,
336                                0,
337                                blobsize,
338                                0,
339                                &err);
340         TestRevalidateStrBuf(Blob);
341         if (err != NULL)
342                 printf("%s", err);
343         CU_ASSERT(blobsize != 0);
344         CU_ASSERT(blobsize == StrLength(Blob));
345         CU_ASSERT_PTR_NULL(err);
346         CU_ASSERT_NOT_EQUAL(sock, -1);
347         if (sock == -1)
348         printf("BLOB: >%s<\n", ChrPtr(Blob));
349         
350         FreeStrBuf(&ReadBuffer);
351         FreeStrBuf(&Blob);
352         FreeStrBuf(&Line);
353         time_to_die = 1;
354 }
355
356
357 static void SimpleBLOBbufferTest(void)
358 {
359         msock = ig_tcp_server(ip_addr, listen_port, LISTEN_QUEUE_LENGTH);
360
361         worker_entry(SimpleBlobTestFunc);
362         close (msock);
363 }
364
365 static void SimpleMixedLineBlob(void)
366 {
367         msock = ig_tcp_server(ip_addr, listen_port, LISTEN_QUEUE_LENGTH);
368
369         worker_entry(SimpleHttpPostTestFunc);
370         close (msock);
371 }
372
373
374
375
376
377 /*
378 Some samples from the original...
379         CU_ASSERT_EQUAL(10, 10);
380         CU_ASSERT_EQUAL(0, -0);
381         CU_ASSERT_EQUAL(-12, -12);
382         CU_ASSERT_NOT_EQUAL(10, 11);
383         CU_ASSERT_NOT_EQUAL(0, -1);
384         CU_ASSERT_NOT_EQUAL(-12, -11);
385         CU_ASSERT_PTR_EQUAL((void*)0x100, (void*)0x100);
386         CU_ASSERT_PTR_NOT_EQUAL((void*)0x100, (void*)0x101);
387         CU_ASSERT_PTR_NULL(NULL);
388         CU_ASSERT_PTR_NULL(0x0);
389         CU_ASSERT_PTR_NOT_NULL((void*)0x23);
390         CU_ASSERT_STRING_EQUAL(str1, str2);
391         CU_ASSERT_STRING_NOT_EQUAL(str1, str2);
392         CU_ASSERT_NSTRING_EQUAL(str1, str2, strlen(str1));
393         CU_ASSERT_NSTRING_EQUAL(str1, str1, strlen(str1));
394         CU_ASSERT_NSTRING_EQUAL(str1, str1, strlen(str1) + 1);
395         CU_ASSERT_NSTRING_NOT_EQUAL(str1, str2, 3);
396         CU_ASSERT_NSTRING_NOT_EQUAL(str1, str3, strlen(str1) + 1);
397         CU_ASSERT_DOUBLE_EQUAL(10, 10.0001, 0.0001);
398         CU_ASSERT_DOUBLE_EQUAL(10, 10.0001, -0.0001);
399         CU_ASSERT_DOUBLE_EQUAL(-10, -10.0001, 0.0001);
400         CU_ASSERT_DOUBLE_EQUAL(-10, -10.0001, -0.0001);
401         CU_ASSERT_DOUBLE_NOT_EQUAL(10, 10.001, 0.0001);
402         CU_ASSERT_DOUBLE_NOT_EQUAL(10, 10.001, -0.0001);
403         CU_ASSERT_DOUBLE_NOT_EQUAL(-10, -10.001, 0.0001);
404         CU_ASSERT_DOUBLE_NOT_EQUAL(-10, -10.001, -0.0001);
405 */
406
407
408
409
410
411 static void AddStrBufSimlpeTests(void)
412 {
413         CU_pSuite pGroup = NULL;
414         CU_pTest pTest = NULL;
415
416         pGroup = CU_add_suite("TestStringBufSimpleAppenders", NULL, NULL);
417         if (n_Lines_to_read > 0)
418                 pTest = CU_add_test(pGroup, "testSimpleLinebufferTest", SimpleLinebufferTest);
419         else if (blobsize > 0)
420                 pTest = CU_add_test(pGroup, "testSimpleBLOBbufferTest", SimpleBLOBbufferTest);
421         else 
422                 pTest = CU_add_test(pGroup,"testSimpleMixedLineBlob", SimpleMixedLineBlob);
423
424 }
425
426
427 int main(int argc, char* argv[])
428 {
429         char a;
430         setvbuf(stdout, NULL, _IONBF, 0);
431
432
433         while ((a = getopt(argc, argv, "p:i:n:b:t:s")) != EOF)
434         {
435                 switch (a) {
436
437                 case 'p':
438                         listen_port = atoi(optarg);
439                         break;
440                 case 'i':
441                         safestrncpy(ip_addr, optarg, sizeof ip_addr);
442                         break;
443                 case 'n':
444                         // do linetest?
445                         n_Lines_to_read = atoi(optarg);
446                         break;
447                 case 'b':
448                         // or blobtest?
449                         blobsize = atoi(optarg);
450                         // else run the simple http test
451                         break;
452                 case 't':
453                         timeout = atoi(optarg);
454                         break;
455                 case 's':
456                         selres = atoi(optarg);
457                         break;
458                 }
459         }
460
461
462         StartLibCitadel(8);
463         CU_BOOL Run = CU_FALSE ;
464         
465         CU_set_output_filename("TestAutomated");
466         if (CU_initialize_registry()) {
467                 printf("\nInitialize of test Registry failed.");
468         }
469         
470         Run = CU_TRUE ;
471         AddStrBufSimlpeTests();
472         
473         if (CU_TRUE == Run) {
474                 //CU_console_run_tests();
475     printf("\nTests completed with return value %d.\n", CU_basic_run_tests());
476     
477     ///CU_automated_run_tests();
478         }
479         
480         CU_cleanup_registry();
481
482         return 0;
483 }