Fix off-by-one in unit test
[libt2n] / test / reconnect.cpp
1 /*
2 Copyright (C) 2004 by Intra2net AG
3
4 The software in this package is distributed under the GNU General
5 Public License version 2 (with a special exception described below).
6
7 A copy of GNU General Public License (GPL) is included in this distribution,
8 in the file COPYING.GPL.
9
10 As a special exception, if other files instantiate templates or use macros
11 or inline functions from this file, or you compile this file and link it
12 with other works to produce a work based on this file, this file
13 does not by itself cause the resulting work to be covered
14 by the GNU General Public License.
15
16 However the source code for this file must still be made available
17 in accordance with section (3) of the GNU General Public License.
18
19 This exception does not invalidate any other reasons why a work based
20 on this file might be covered by the GNU General Public License.
21 */
22 #include <sys/types.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <signal.h>
26 #include <stdio.h>
27 #include <time.h>
28
29 #include <iostream>
30 #include <string>
31 #include <sstream>
32 #include <stdexcept>
33
34 #define BOOST_TEST_DYN_LINK
35 #include <boost/test/unit_test.hpp>
36
37 #include <socket_client.hxx>
38 #include <socket_server.hxx>
39
40 #include "test_fixtures.hxx"
41
42 #include <config.h>
43
44 using namespace std;
45 using namespace libt2n;
46
47 BOOST_FIXTURE_TEST_SUITE(test_reconnect, KillChildOnShutdownFixture)
48
49 BOOST_AUTO_TEST_CASE(simple_reconnect)
50 {
51     switch(child_pid=fork())
52     {
53         case -1:
54         {
55             BOOST_FAIL("fork error");
56             break;
57         }
58         case 0:
59         // child
60         {
61             try
62             {
63                 socket_server ss("./socket");
64
65                 time_t t0 = time(NULL);
66
67                 // max 10 sec
68                 while (time(NULL) < t0 + 10 )
69                 {
70                     ss.fill_buffer(1000000);
71
72                     string data;
73                     unsigned int cid;
74
75                     if(ss.get_packet(data,cid))
76                     {
77                         server_connection* con=ss.get_connection(cid);
78
79                         if (data=="QUIT")
80                             break;
81
82                         if (data=="x")
83                             con->write(string().insert(0,100,'X'));
84                         else
85                             con->write(string().insert(0,100,'Y'));
86                     }
87                 }
88             } catch(...)
89             {
90                 std::cerr << "exception in child. ignoring\n";
91             }
92
93             // don't call atexit and stuff
94             _exit(0);
95         }
96
97         default:
98         // parent
99         {
100             // don't kill us on broken pipe
101             signal(SIGPIPE, SIG_IGN);
102
103             // wait till server is up
104             sleep(1);
105             socket_client_connection sc("./socket");
106
107             sc.write("abc");
108
109             string data;
110
111             while (!sc.get_packet(data))
112                 sc.fill_buffer(1000000);
113
114             sc.reconnect();
115
116             sc.write("x");
117
118             while (!sc.get_packet(data))
119                 sc.fill_buffer(1000000);
120
121             BOOST_CHECK_EQUAL(string().insert(0,100,'X'),data);
122         }
123     }
124 }
125
126 BOOST_AUTO_TEST_CASE(reconnect_with_close)
127 {
128     switch(child_pid=fork())
129     {
130         case -1:
131         {
132             BOOST_FAIL("fork error");
133             break;
134         }
135         case 0:
136         // child
137         {
138             try
139             {
140                 socket_server ss("./socket");
141
142                 time_t t0 = time(NULL);
143
144                 // max 10 sec
145                 while (time(NULL) < t0 + 10 )
146                 {
147                     ss.fill_buffer(1000000);
148
149                     string data;
150                     unsigned int cid;
151
152                     if(ss.get_packet(data,cid))
153                     {
154                         server_connection* con=ss.get_connection(cid);
155
156                         if (data=="QUIT")
157                             break;
158
159                         if (data=="x")
160                             con->write(string().insert(0,100,'X'));
161                         else
162                             con->write(string().insert(0,100,'Y'));
163                     }
164                 }
165             } catch(...)
166             {
167                 std::cerr << "exception in child. ignoring\n";
168             }
169
170             // don't call atexit and stuff
171             _exit(0);
172         }
173
174         default:
175         // parent
176         {
177             // don't kill us on broken pipe
178             signal(SIGPIPE, SIG_IGN);
179
180             // wait till server is up
181             sleep(1);
182             socket_client_connection sc("./socket");
183
184             sc.write("abc");
185
186             string data;
187
188             while (!sc.get_packet(data))
189                 sc.fill_buffer(1000000);
190
191             sc.close();
192
193             // empty buffer
194             sc.get_packet(data);
195
196             sc.reconnect();
197
198             sc.write("x");
199
200             while (!sc.get_packet(data))
201                 sc.fill_buffer(1000000);
202
203             BOOST_CHECK_EQUAL(string().insert(0,100,'X'),data);
204         }
205     }
206 }
207
208 BOOST_AUTO_TEST_CASE(reconnect_buffer_complete)
209 {
210     switch(child_pid=fork())
211     {
212         case -1:
213         {
214             BOOST_FAIL("fork error");
215             break;
216         }
217         case 0:
218         // child
219         {
220             try
221             {
222                 socket_server ss("./socket");
223
224                 time_t t0 = time(NULL);
225
226                 // max 10 sec
227                 while (time(NULL) < t0 + 10 )
228                 {
229                     ss.fill_buffer(1000000);
230
231                     string data;
232                     unsigned int cid;
233
234                     if(ss.get_packet(data,cid))
235                     {
236                         server_connection* con=ss.get_connection(cid);
237
238                         if (data=="QUIT")
239                             break;
240
241                         if (data=="x")
242                             con->write(string().insert(0,100,'X'));
243                     }
244                 }
245             } catch(...)
246             {
247                 std::cerr << "exception in child. ignoring\n";
248             }
249
250             // don't call atexit and stuff
251             _exit(0);
252         }
253
254         default:
255         // parent
256         {
257             // don't kill us on broken pipe
258             signal(SIGPIPE, SIG_IGN);
259
260             // wait till server is up
261             sleep(1);
262             socket_client_connection sc("./socket");
263
264             sc.write("x");
265
266             string data;
267
268             while (!sc.packet_available())
269                 sc.fill_buffer(1000000);
270
271             sc.reconnect();
272
273             BOOST_CHECK_MESSAGE(sc.get_packet(data) == true, "packet not there");
274
275             BOOST_CHECK_MESSAGE(data == string().insert(0,100,'X'), "data incorrect");
276         }
277     }
278 }
279
280 BOOST_AUTO_TEST_CASE(reconnect_buffer_several_complete)
281 {
282     const int packets=3;
283
284     switch(child_pid=fork())
285     {
286         case -1:
287         {
288             BOOST_FAIL("fork error");
289             break;
290         }
291         case 0:
292         // child
293         {
294             try
295             {
296                 socket_server ss("./socket");
297
298                 time_t t0 = time(NULL);
299
300                 // max 10 sec
301                 while (time(NULL) < t0 + 10 )
302                 {
303                     ss.fill_buffer(1000000);
304
305                     string data;
306                     unsigned int cid;
307
308                     if(ss.get_packet(data,cid))
309                     {
310                         server_connection* con=ss.get_connection(cid);
311
312                         if (data=="QUIT")
313                             break;
314
315                         if (data=="x")
316                         {
317                             for (int i=0; i<packets; i++)
318                                 con->write(string().insert(0,100,'X'));
319                         }
320                     }
321                 }
322             } catch(...)
323             {
324                 std::cerr << "exception in child. ignoring\n";
325             }
326
327             // don't call atexit and stuff
328             _exit(0);
329         }
330
331         default:
332         // parent
333         {
334             // don't kill us on broken pipe
335             signal(SIGPIPE, SIG_IGN);
336
337             // wait till server is up
338             sleep(1);
339             socket_client_connection sc("./socket");
340
341             sc.write("x");
342
343             // retrieve packets for 3 seconds
344             time_t t0 = time(NULL);
345
346             // max 3 sec
347             while (time(NULL) < t0 + 3 )
348                 sc.fill_buffer(1000000);
349
350             // we now should have packets complete packets in the buffer
351
352             sc.reconnect();
353
354             // are these packets still there?
355
356             for (int i=0; i < packets; i++)
357             {
358                 string data;
359
360                 ostringstream os;
361                 os << "packet " << i << " not there";
362
363                 BOOST_CHECK_MESSAGE(sc.get_packet(data) == true, os.str());
364
365                 os.str("");
366                 os << "packet " << i << " incorrect";
367
368                 BOOST_CHECK_MESSAGE(string().insert(0,100,'X') == data, os.str());
369             }
370         }
371     }
372 }
373
374 BOOST_AUTO_TEST_CASE(reconnect_buffer_no_incomplete1)
375 {
376     switch(child_pid=fork())
377     {
378         case -1:
379         {
380             BOOST_FAIL("fork error");
381             break;
382         }
383         case 0:
384         // child
385         {
386             try
387             {
388                 socket_server ss("./socket");
389
390                 time_t t0 = time(NULL);
391
392                 // max 10 sec
393                 while (time(NULL) < t0 + 10 )
394                 {
395                     ss.fill_buffer(1000000);
396
397                     string data;
398                     unsigned int cid;
399
400                     if(ss.get_packet(data,cid))
401                     {
402                         server_connection* con=ss.get_connection(cid);
403
404                         if (data=="QUIT")
405                             break;
406
407                         if (data=="x")
408                         {
409                             con->write(string().insert(0,100,'X'));
410                             send_raw_socket("aaaab",&ss,cid);
411                         }
412                     }
413                 }
414             } catch(...)
415             {
416                 std::cerr << "exception in child. ignoring\n";
417             }
418
419             // don't call atexit and stuff
420             _exit(0);
421         }
422
423         default:
424         // parent
425         {
426             // don't kill us on broken pipe
427             signal(SIGPIPE, SIG_IGN);
428
429             // wait till server is up
430             sleep(1);
431             socket_client_connection sc("./socket");
432
433             sc.write("x");
434
435             // retrieve packets for 3 seconds
436             time_t t0 = time(NULL);
437
438             // max 3 sec
439             while (time(NULL) < t0 + 3 )
440                 sc.fill_buffer(1000000);
441
442             // we now should have one complete packet and some stuff in the buffer
443
444             string data;
445             sc.get_packet(data);
446
447             BOOST_CHECK_MESSAGE((sc.peek_packet(data))>0, "no incomplete packet");
448
449             sc.reconnect();
450
451             BOOST_CHECK_MESSAGE((int)sc.peek_packet(data) == 0, "incomplete packet not removed");
452         }
453     }
454 }
455
456 BOOST_AUTO_TEST_CASE(reconnect_buffer_no_incomplete2)
457 {
458     switch(child_pid=fork())
459     {
460         case -1:
461         {
462             BOOST_FAIL("fork error");
463             break;
464         }
465         case 0:
466         // child
467         {
468             try
469             {
470                 socket_server ss("./socket");
471
472                 time_t t0 = time(NULL);
473
474                 // max 10 sec
475                 while (time(NULL) < t0 + 10 )
476                 {
477                     ss.fill_buffer(1000000);
478
479                     string data;
480                     unsigned int cid;
481
482                     if(ss.get_packet(data,cid))
483                     {
484                         server_connection* con=ss.get_connection(cid);
485
486                         if (data=="QUIT")
487                             break;
488
489                         if (data=="x")
490                         {
491                             con->write(string().insert(0,100,'X'));
492
493                             string blob=string().insert(0,100,'Y');
494
495                             // one byte will be missing...
496                             int size=blob.size()+1;
497                             char sizetransfer[sizeof(int)+1];
498                             memcpy(sizetransfer,(void*)&size,sizeof(int));
499                             sizetransfer[sizeof(int)]=0;
500
501                             string packet=string(sizetransfer)+blob;
502
503                             send_raw_socket(packet,&ss,cid);
504                         }
505                     }
506                 }
507             } catch(...)
508             {
509                 std::cerr << "exception in child. ignoring\n";
510             }
511
512             // don't call atexit and stuff
513             _exit(0);
514         }
515
516         default:
517         // parent
518         {
519             // don't kill us on broken pipe
520             signal(SIGPIPE, SIG_IGN);
521
522             // wait till server is up
523             sleep(1);
524             socket_client_connection sc("./socket");
525
526             sc.write("x");
527
528             // retrieve packets for 3 seconds
529             time_t t0 = time(NULL);
530
531             // max 3 sec
532             while (time(NULL) < t0 + 3 )
533                 sc.fill_buffer(1000000);
534
535             // we now should have one complete packet and some stuff in the buffer
536
537             sc.reconnect();
538
539             string data;
540
541             BOOST_CHECK_MESSAGE(sc.get_packet(data) == true, "packet not there");
542             BOOST_CHECK_MESSAGE(string().insert(0,100,'X') == data, "data incorrect");
543
544             BOOST_CHECK_MESSAGE((int)sc.peek_packet(data) == 0, "incomplete packet not removed");
545         }
546     }
547 }
548
549 BOOST_AUTO_TEST_SUITE_END()