Change license from LGPL to GPL version 2 + linking exception. This fixes C++ templat...
[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 #include <cppunit/extensions/TestFactoryRegistry.h>
35 #include <cppunit/ui/text/TestRunner.h>
36 #include <cppunit/extensions/HelperMacros.h>
37
38 #include <socket_client.hxx>
39 #include <socket_server.hxx>
40
41 #ifdef HAVE_CONFIG_H
42 #include <config.h>
43 #endif
44
45 using namespace std;
46 using namespace libt2n;
47 using namespace CppUnit;
48
49 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
50 class real_write_connection: public socket_server_connection
51 {
52     public:
53         void real_write(const std::string& data)
54             { socket_write(data); }
55 };
56
57 class test_reconnect : public TestFixture
58 {
59     CPPUNIT_TEST_SUITE(test_reconnect);
60
61     CPPUNIT_TEST(simple_reconnect);
62     CPPUNIT_TEST(reconnect_with_close);
63     CPPUNIT_TEST(reconnect_buffer_complete);
64     CPPUNIT_TEST(reconnect_buffer_several_complete);
65     CPPUNIT_TEST(reconnect_buffer_no_incomplete1);
66     CPPUNIT_TEST(reconnect_buffer_no_incomplete2);
67
68     CPPUNIT_TEST_SUITE_END();
69
70     pid_t child_pid;
71
72     public:
73
74     void setUp()
75     { }
76
77     void tearDown()
78     {
79         // make sure the server-child is dead before the next test runs
80         kill(child_pid,SIGKILL);
81         sleep(1);
82     }
83
84     void send_raw_socket(string hello_string, socket_server* ss, int conn_id)
85     {
86         socket_server_connection *ssc=dynamic_cast<socket_server_connection*>(ss->get_connection(conn_id));
87
88         // this is an evil hack to get access to real_write, don't ever do this in an app!!!
89         real_write_connection *rwc=(real_write_connection*)ssc;
90         rwc->real_write(hello_string);
91     }
92
93     void simple_reconnect()
94     {
95         switch(child_pid=fork())
96         {
97             case -1:
98             {
99                 CPPUNIT_FAIL("fork error");
100                 break;
101             }
102             case 0:
103             // child
104             {
105                 try
106                 {
107                     socket_server ss("./socket");
108
109                     time_t t0 = time(NULL);
110
111                     // max 10 sec
112                     while (time(NULL) < t0 + 10 )
113                     {
114                         ss.fill_buffer(1000000);
115
116                         string data;
117                         unsigned int cid;
118
119                         if(ss.get_packet(data,cid))
120                         {
121                             server_connection* con=ss.get_connection(cid);
122
123                             if (data=="QUIT")
124                                 break;
125
126                             if (data=="x")
127                                 con->write(string().insert(0,100,'X'));
128                             else
129                                 con->write(string().insert(0,100,'Y'));
130                         }
131                     }
132                 } catch(...)
133                 {
134                     std::cerr << "exception in child. ignoring\n";
135                 }
136
137                 // don't call atexit and stuff
138                 _exit(0);
139             }
140
141             default:
142             // parent
143             {
144                 // don't kill us on broken pipe
145                 signal(SIGPIPE, SIG_IGN);
146
147                 // wait till server is up
148                 sleep(1);
149                 socket_client_connection sc("./socket");
150
151                 sc.write("abc");
152
153                 string data;
154
155                 while (!sc.get_packet(data))
156                     sc.fill_buffer(1000000);
157
158                 sc.reconnect();
159
160                 sc.write("x");
161
162                 while (!sc.get_packet(data))
163                     sc.fill_buffer(1000000);
164
165                 CPPUNIT_ASSERT_EQUAL(string().insert(0,100,'X'),data);
166             }
167         }
168     }
169
170     void reconnect_with_close()
171     {
172         switch(child_pid=fork())
173         {
174             case -1:
175             {
176                 CPPUNIT_FAIL("fork error");
177                 break;
178             }
179             case 0:
180             // child
181             {
182                 try
183                 {
184                     socket_server ss("./socket");
185
186                     time_t t0 = time(NULL);
187
188                     // max 10 sec
189                     while (time(NULL) < t0 + 10 )
190                     {
191                         ss.fill_buffer(1000000);
192
193                         string data;
194                         unsigned int cid;
195
196                         if(ss.get_packet(data,cid))
197                         {
198                             server_connection* con=ss.get_connection(cid);
199
200                             if (data=="QUIT")
201                                 break;
202
203                             if (data=="x")
204                                 con->write(string().insert(0,100,'X'));
205                             else
206                                 con->write(string().insert(0,100,'Y'));
207                         }
208                     }
209                 } catch(...)
210                 {
211                     std::cerr << "exception in child. ignoring\n";
212                 }
213
214                 // don't call atexit and stuff
215                 _exit(0);
216             }
217
218             default:
219             // parent
220             {
221                 // don't kill us on broken pipe
222                 signal(SIGPIPE, SIG_IGN);
223
224                 // wait till server is up
225                 sleep(1);
226                 socket_client_connection sc("./socket");
227
228                 sc.write("abc");
229
230                 string data;
231
232                 while (!sc.get_packet(data))
233                     sc.fill_buffer(1000000);
234
235                 sc.close();
236
237                 // empty buffer
238                 sc.get_packet(data);
239
240                 sc.reconnect();
241
242                 sc.write("x");
243
244                 while (!sc.get_packet(data))
245                     sc.fill_buffer(1000000);
246
247                 CPPUNIT_ASSERT_EQUAL(string().insert(0,100,'X'),data);
248             }
249         }
250     }
251
252     void reconnect_buffer_complete()
253     {
254         switch(child_pid=fork())
255         {
256             case -1:
257             {
258                 CPPUNIT_FAIL("fork error");
259                 break;
260             }
261             case 0:
262             // child
263             {
264                 try
265                 {
266                     socket_server ss("./socket");
267
268                     time_t t0 = time(NULL);
269
270                     // max 10 sec
271                     while (time(NULL) < t0 + 10 )
272                     {
273                         ss.fill_buffer(1000000);
274
275                         string data;
276                         unsigned int cid;
277
278                         if(ss.get_packet(data,cid))
279                         {
280                             server_connection* con=ss.get_connection(cid);
281
282                             if (data=="QUIT")
283                                 break;
284
285                             if (data=="x")
286                                 con->write(string().insert(0,100,'X'));
287                         }
288                     }
289                 } catch(...)
290                 {
291                     std::cerr << "exception in child. ignoring\n";
292                 }
293
294                 // don't call atexit and stuff
295                 _exit(0);
296             }
297
298             default:
299             // parent
300             {
301                 // don't kill us on broken pipe
302                 signal(SIGPIPE, SIG_IGN);
303
304                 // wait till server is up
305                 sleep(1);
306                 socket_client_connection sc("./socket");
307
308                 sc.write("x");
309
310                 string data;
311
312                 while (!sc.packet_available())
313                     sc.fill_buffer(1000000);
314
315                 sc.reconnect();
316
317                 CPPUNIT_ASSERT_EQUAL_MESSAGE("packet not there",true,sc.get_packet(data));
318
319                 CPPUNIT_ASSERT_EQUAL_MESSAGE("data incorrect",string().insert(0,100,'X'),data);
320             }
321         }
322     }
323
324     void reconnect_buffer_several_complete()
325     {
326         const int packets=3;
327
328         switch(child_pid=fork())
329         {
330             case -1:
331             {
332                 CPPUNIT_FAIL("fork error");
333                 break;
334             }
335             case 0:
336             // child
337             {
338                 try
339                 {
340                     socket_server ss("./socket");
341
342                     time_t t0 = time(NULL);
343
344                     // max 10 sec
345                     while (time(NULL) < t0 + 10 )
346                     {
347                         ss.fill_buffer(1000000);
348
349                         string data;
350                         unsigned int cid;
351
352                         if(ss.get_packet(data,cid))
353                         {
354                             server_connection* con=ss.get_connection(cid);
355
356                             if (data=="QUIT")
357                                 break;
358
359                             if (data=="x")
360                             {
361                                 for (int i=0; i<packets; i++)
362                                     con->write(string().insert(0,100,'X'));
363                             }
364                         }
365                     }
366                 } catch(...)
367                 {
368                     std::cerr << "exception in child. ignoring\n";
369                 }
370
371                 // don't call atexit and stuff
372                 _exit(0);
373             }
374
375             default:
376             // parent
377             {
378                 // don't kill us on broken pipe
379                 signal(SIGPIPE, SIG_IGN);
380
381                 // wait till server is up
382                 sleep(1);
383                 socket_client_connection sc("./socket");
384
385                 sc.write("x");
386
387                 // retrieve packets for 3 seconds
388                 time_t t0 = time(NULL);
389
390                 // max 3 sec
391                 while (time(NULL) < t0 + 3 )
392                     sc.fill_buffer(1000000);
393
394                 // we now should have packets complete packets in the buffer
395
396                 sc.reconnect();
397
398                 // are these packets still there?
399
400                 for (int i=0; i < packets; i++)
401                 {
402                     string data;
403
404                     ostringstream os;
405                     os << "packet " << i << " not there";
406
407                     CPPUNIT_ASSERT_EQUAL_MESSAGE(os.str(),true,sc.get_packet(data));
408
409                     os.str("");
410                     os << "packet " << i << " incorrect";
411
412                     CPPUNIT_ASSERT_EQUAL_MESSAGE(os.str(),string().insert(0,100,'X'),data);
413                 }
414             }
415         }
416     }
417
418     void reconnect_buffer_no_incomplete1()
419     {
420         switch(child_pid=fork())
421         {
422             case -1:
423             {
424                 CPPUNIT_FAIL("fork error");
425                 break;
426             }
427             case 0:
428             // child
429             {
430                 try
431                 {
432                     socket_server ss("./socket");
433
434                     time_t t0 = time(NULL);
435
436                     // max 10 sec
437                     while (time(NULL) < t0 + 10 )
438                     {
439                         ss.fill_buffer(1000000);
440
441                         string data;
442                         unsigned int cid;
443
444                         if(ss.get_packet(data,cid))
445                         {
446                             server_connection* con=ss.get_connection(cid);
447
448                             if (data=="QUIT")
449                                 break;
450
451                             if (data=="x")
452                             {
453                                 con->write(string().insert(0,100,'X'));
454                                 send_raw_socket("aaaab",&ss,cid);
455                             }
456                         }
457                     }
458                 } catch(...)
459                 {
460                     std::cerr << "exception in child. ignoring\n";
461                 }
462
463                 // don't call atexit and stuff
464                 _exit(0);
465             }
466
467             default:
468             // parent
469             {
470                 // don't kill us on broken pipe
471                 signal(SIGPIPE, SIG_IGN);
472
473                 // wait till server is up
474                 sleep(1);
475                 socket_client_connection sc("./socket");
476
477                 sc.write("x");
478
479                 // retrieve packets for 3 seconds
480                 time_t t0 = time(NULL);
481
482                 // max 3 sec
483                 while (time(NULL) < t0 + 3 )
484                     sc.fill_buffer(1000000);
485
486                 // we now should have one complete packet and some stuff in the buffer
487
488                 string data;
489                 sc.get_packet(data);
490
491                 CPPUNIT_ASSERT_EQUAL_MESSAGE("no incomplete packet",true,(sc.peek_packet(data))>0);
492
493                 sc.reconnect();
494
495                 CPPUNIT_ASSERT_EQUAL_MESSAGE("incomplete packet not removed",0,(int)sc.peek_packet(data));
496             }
497         }
498     }
499
500     void reconnect_buffer_no_incomplete2()
501     {
502         switch(child_pid=fork())
503         {
504             case -1:
505             {
506                 CPPUNIT_FAIL("fork error");
507                 break;
508             }
509             case 0:
510             // child
511             {
512                 try
513                 {
514                     socket_server ss("./socket");
515
516                     time_t t0 = time(NULL);
517
518                     // max 10 sec
519                     while (time(NULL) < t0 + 10 )
520                     {
521                         ss.fill_buffer(1000000);
522
523                         string data;
524                         unsigned int cid;
525
526                         if(ss.get_packet(data,cid))
527                         {
528                             server_connection* con=ss.get_connection(cid);
529
530                             if (data=="QUIT")
531                                 break;
532
533                             if (data=="x")
534                             {
535                                 con->write(string().insert(0,100,'X'));
536
537                                 string blob=string().insert(0,100,'Y');
538
539                                 // one byte will be missing...
540                                 int size=blob.size()+1;
541                                 char sizetransfer[sizeof(int)+1];
542                                 memcpy(sizetransfer,(void*)&size,sizeof(int));
543                                 sizetransfer[sizeof(int)+1]=0;
544
545                                 string packet=string(sizetransfer)+blob;
546
547                                 send_raw_socket(packet,&ss,cid);
548                             }
549                         }
550                     }
551                 } catch(...)
552                 {
553                     std::cerr << "exception in child. ignoring\n";
554                 }
555
556                 // don't call atexit and stuff
557                 _exit(0);
558             }
559
560             default:
561             // parent
562             {
563                 // don't kill us on broken pipe
564                 signal(SIGPIPE, SIG_IGN);
565
566                 // wait till server is up
567                 sleep(1);
568                 socket_client_connection sc("./socket");
569
570                 sc.write("x");
571
572                 // retrieve packets for 3 seconds
573                 time_t t0 = time(NULL);
574
575                 // max 3 sec
576                 while (time(NULL) < t0 + 3 )
577                     sc.fill_buffer(1000000);
578
579                 // we now should have one complete packet and some stuff in the buffer
580
581                 sc.reconnect();
582
583                 string data;
584
585                 CPPUNIT_ASSERT_EQUAL_MESSAGE("packet not there",true,sc.get_packet(data));
586                 CPPUNIT_ASSERT_EQUAL_MESSAGE("data incorrect",string().insert(0,100,'X'),data);
587
588                 CPPUNIT_ASSERT_EQUAL_MESSAGE("incomplete packet not removed",0,(int)sc.peek_packet(data));
589             }
590         }
591     }
592
593 };
594
595 CPPUNIT_TEST_SUITE_REGISTRATION(test_reconnect);