Remove code duplication in test fixtures
[libt2n] / test / timeout.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
28 #include <iostream>
29 #include <string>
30 #include <sstream>
31 #include <stdexcept>
32
33 #include <boost/bind.hpp>
34
35 #define BOOST_TEST_DYN_LINK
36 #include <boost/test/unit_test.hpp>
37
38 #include <boost/archive/binary_oarchive.hpp>
39 #include <boost/archive/binary_iarchive.hpp>
40 #include <boost/archive/xml_oarchive.hpp>
41 #include <boost/archive/xml_iarchive.hpp>
42 #include <boost/serialization/serialization.hpp>
43
44 #include <container.hxx>
45 #include <socket_client.hxx>
46 #include <socket_server.hxx>
47 #include <command_client.hxx>
48 #include <command_server.hxx>
49
50 #include "test_fixtures.hxx"
51
52 #ifdef HAVE_CONFIG_H
53 #include <config.h>
54 #endif
55
56 using namespace std;
57 using namespace libt2n;
58
59 string testfunc2(const string& str)
60 {
61     if (str=="throw")
62         throw libt2n::t2n_runtime_error("throw me around");
63     string ret(str);
64     ret+=", testfunc() was here";
65     return ret;
66 }
67
68 class testfunc2_res : public libt2n::result
69 {
70     private:
71         string res;
72
73         friend class boost::serialization::access;
74         template<class Archive>
75         void serialize(Archive & ar, const unsigned int version)
76         {
77             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result);
78             ar & BOOST_SERIALIZATION_NVP(res);
79         }
80
81     public:
82         testfunc2_res()
83             { }
84
85         testfunc2_res(const string& str)
86         {
87             res=str;
88         }
89
90         string get_data()
91         {
92             return res;
93         }
94 };
95
96
97 class testfunc2_cmd : public libt2n::command
98 {
99     private:
100         string param;
101
102         friend class boost::serialization::access;
103         template<class Archive>
104         void serialize(Archive & ar, const unsigned int version)
105         {
106             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::command);
107             ar & BOOST_SERIALIZATION_NVP(param);
108         }
109
110     public:
111         testfunc2_cmd()
112             { }
113
114         testfunc2_cmd(const string& str)
115         {
116             param=str;
117         }
118
119         libt2n::result* operator()()
120         {
121             return new testfunc2_res(testfunc2(param));
122         }
123 };
124
125 #include <boost/serialization/export.hpp>
126
127 BOOST_CLASS_EXPORT(testfunc2_cmd)
128 BOOST_CLASS_EXPORT(testfunc2_res)
129
130 class test_timeoutFixture : public KillChildOnShutdownFixture
131 {
132 protected:
133     typedef uint32_t packet_size_indicator;
134
135     // this is an evil hack to get access to real_write, don't ever do this in an app!!!
136     class real_write_client_connection: public socket_client_connection
137     {
138         public:
139             void real_write(const std::string& data)
140                 { socket_write(data); }
141     };
142
143     void send_slow_raw_socket(string data, socket_server* ss, unsigned int conn_id)
144     {
145         socket_server_connection *ssc=dynamic_cast<socket_server_connection*>(ss->get_connection(conn_id));
146
147         // this is an evil hack to get access to real_write, don't ever do this in an app!!!
148         real_write_connection *rwc=(real_write_connection*)ssc;
149
150         // we write one char each 0.2 sec
151         for (int pos=0; pos < data.size(); pos++)
152         {
153             string onebyte;
154             onebyte+=data[pos];
155             rwc->real_write(onebyte);
156             usleep(200000);
157         }
158     }
159 };
160
161 BOOST_FIXTURE_TEST_SUITE(test_timeout, test_timeoutFixture)
162
163 BOOST_AUTO_TEST_CASE(ConnectTimeout)
164 {
165     switch(child_pid=fork())
166     {
167         case -1:
168         {
169             BOOST_FAIL("fork error");
170             break;
171         }
172         case 0:
173         // child
174         {
175             try
176             {
177                 socket_server ss("./socket");
178             } catch(...)
179             {
180                 std::cerr << "exception in child. ignoring\n";
181             }
182
183             // don't call atexit and stuff
184             _exit(0);
185         }
186
187         default:
188         // parent
189         {
190             string data;
191
192             // wait till server is up
193             sleep(1);
194
195             string errormsg;
196
197             socket_client_connection sc("./socket");
198
199             BOOST_CHECK_MESSAGE(sc.connection::is_closed() == true, "connection not closed");
200
201             BOOST_CHECK_MESSAGE(sc.get_last_error_msg() == string("no more retries left after connect error"), "wrong errormessage");
202         }
203     }
204 }
205
206 BOOST_AUTO_TEST_CASE(HelloTimeoutNothing)
207 {
208     switch(child_pid=fork())
209     {
210         case -1:
211         {
212             BOOST_FAIL("fork error");
213             break;
214         }
215         case 0:
216         // child
217         {
218             try
219             {
220                 socket_server ss("./socket");
221
222                 // max 10 sec
223                 for (int i=0; i < 10; i++)
224                     ss.fill_buffer(1000000);
225             } catch(...)
226             {
227                 std::cerr << "exception in child. ignoring\n";
228             }
229
230             // don't call atexit and stuff
231             _exit(0);
232         }
233
234         default:
235         // parent
236         {
237             string data;
238
239             // wait till server is up
240             sleep(1);
241             socket_client_connection sc("./socket");
242             command_client cc(&sc,1000000,1000000);
243
244             t2n_exception* ep=cc.get_constuctor_exception();
245
246             string errormsg;
247             if (ep)
248                 errormsg=ep->what();
249
250             BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
251         }
252     }
253 }
254
255 BOOST_AUTO_TEST_CASE(HelloTimeoutSlowData)
256 {
257     switch(child_pid=fork())
258     {
259         case -1:
260         {
261             BOOST_FAIL("fork error");
262             break;
263         }
264         case 0:
265         // child
266         {
267             try
268             {
269                 socket_server ss("./socket");
270
271                 // create a valid packet
272                 ostringstream hello;
273                 hello << "T2Nv" << PROTOCOL_VERSION << ';';
274                 int byteordercheck=1;
275                 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
276                 hello << ';';
277
278                 packet_size_indicator psize=htonl(hello.str().size());
279                 std::string send_data(hello.str());
280                 send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
281
282                 ss.add_callback(new_connection,bind(&test_timeout::HelloTimeoutSlowData::send_slow_raw_socket, boost::ref(*this), send_data,&ss, _1));
283
284                 // max 10 sec
285                 for (int i=0; i < 10; i++)
286                     ss.fill_buffer(1000000);
287             } catch(...)
288             {
289                 std::cerr << "exception in child. ignoring\n";
290             }
291
292             // don't call atexit and stuff
293             _exit(0);
294         }
295
296         default:
297         // parent
298         {
299             string data;
300
301             // wait till server is up
302             sleep(1);
303             socket_client_connection sc("./socket");
304             command_client cc(&sc,1000000,1000000);
305
306             t2n_exception* ep=cc.get_constuctor_exception();
307
308             string errormsg;
309             if (ep)
310                 errormsg=ep->what();
311
312             BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
313         }
314     }
315 }
316
317 BOOST_AUTO_TEST_CASE(CommandTimeout)
318 {
319     switch(child_pid=fork())
320     {
321         case -1:
322         {
323             BOOST_FAIL("fork error");
324             break;
325         }
326         case 0:
327         // child
328         {
329             try
330             {
331                 socket_server ss("./socket");
332
333                 ostringstream hello;
334                 hello << "T2Nv" << PROTOCOL_VERSION << ';';
335                 int byteordercheck=1;
336                 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
337                 hello << ';';
338
339                 ss.add_callback(new_connection,bind(&test_timeout::CommandTimeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
340
341                 // max 10 sec
342                 for (int i=0; i < 10; i++)
343                     ss.fill_buffer(1000000);
344             } catch(...)
345             {
346                 std::cerr << "exception in child. ignoring\n";
347             }
348
349             // don't call atexit and stuff
350             _exit(0);
351         }
352
353         default:
354         // parent
355         {
356             string data;
357
358             // wait till server is up
359             sleep(1);
360             socket_client_connection sc("./socket");
361
362             command_client cc(&sc,1000000,1000000);
363             result_container rc;
364
365             string errormsg;
366
367             try
368             {
369                 cc.send_command(new testfunc2_cmd("hello"),rc);
370             }
371             catch(t2n_transfer_error &e)
372             { errormsg=e.what(); }
373             catch(...)
374             { throw; }
375
376             BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
377         }
378     }
379 }
380
381 BOOST_AUTO_TEST_CASE(CommandSlowResponse)
382 {
383     switch(child_pid=fork())
384     {
385         case -1:
386         {
387             BOOST_FAIL("fork error");
388             break;
389         }
390         case 0:
391         // child
392         {
393             try
394             {
395                 socket_server ss("./socket");
396
397                 ostringstream hello;
398                 hello << "T2Nv" << PROTOCOL_VERSION << ';';
399                 int byteordercheck=1;
400                 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
401                 hello << ';';
402
403                 ss.add_callback(new_connection,bind(&test_timeout::CommandSlowResponse::send_hello, boost::ref(*this), hello.str(),&ss, _1));
404
405                 // max 10 sec
406                 for (int i=0; i < 10; i++)
407                 {
408                     ss.fill_buffer(1000000);
409
410                     string data;
411                     unsigned int cid;
412
413                     if(ss.get_packet(data,cid))
414                     {
415                         // create a valid packet & send
416                         string response="abcdefghijklmnopqrstuvwxyz";
417                         packet_size_indicator psize=htonl(response.size());
418                         std::string send_data(response);
419                         send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
420                         send_slow_raw_socket(send_data,&ss,cid);
421                     }
422                 }
423             } catch(...)
424             {
425                 std::cerr << "exception in child. ignoring\n";
426             }
427
428             // don't call atexit and stuff
429             _exit(0);
430         }
431
432         default:
433         // parent
434         {
435             string data;
436
437             // wait till server is up
438             sleep(1);
439             socket_client_connection sc("./socket");
440
441             command_client cc(&sc,1000000,1000000);
442             result_container rc;
443
444             string errormsg;
445
446             try
447             {
448                 cc.send_command(new testfunc2_cmd("hello"),rc);
449             }
450             catch(t2n_transfer_error &e)
451             { errormsg=e.what(); }
452             catch(...)
453             { throw; }
454
455             BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
456         }
457     }
458 }
459
460 BOOST_AUTO_TEST_CASE(DisconnectOnWrite)
461 {
462     switch(child_pid=fork())
463     {
464         case -1:
465         {
466             BOOST_FAIL("fork error");
467             break;
468         }
469         case 0:
470         // child
471         {
472             try
473             {
474                 socket_server ss("./socket");
475
476                 // bail out as soon as we get something
477                 ss.fill_buffer(-1);
478             } catch(...)
479             {
480                 std::cerr << "exception in child. ignoring\n";
481             }
482
483             // don't call atexit and stuff
484             _exit(0);
485         }
486
487         default:
488         // parent
489         {
490             string data;
491
492             // don't kill us on broken pipe
493             signal(SIGPIPE, SIG_IGN);
494
495             // wait till server is up
496             sleep(1);
497             socket_client_connection sc("./socket");
498
499             string errormsg;
500
501             string huge(5000000,'x');
502
503             try
504             {
505                 sc.write(huge);
506             }
507             catch(t2n_transfer_error &e)
508             { errormsg=e.what(); }
509             catch(...)
510             { throw; }
511
512             BOOST_CHECK_EQUAL(string("write() returned Broken pipe"),errormsg);
513         }
514     }
515 }
516
517 BOOST_AUTO_TEST_CASE(WriteTwice)
518 {
519     switch(child_pid=fork())
520     {
521         case -1:
522         {
523             BOOST_FAIL("fork error");
524             break;
525         }
526         case 0:
527         // child
528         {
529             try
530             {
531                 socket_server ss("./socket");
532
533                 // bail out as soon as we get something
534                 ss.fill_buffer(-1);
535                 ss.fill_buffer(-1);
536             } catch(...)
537             {
538                 std::cerr << "exception in child. ignoring\n";
539             }
540
541             // don't call atexit and stuff
542             _exit(0);
543         }
544
545         default:
546         // parent
547         {
548             string data;
549
550             // don't kill us on broken pipe
551             signal(SIGPIPE, SIG_IGN);
552
553             // wait till server is up
554             sleep(1);
555             socket_client_connection sc("./socket");
556
557             string errormsg;
558
559             sc.write("somedata");
560
561             sleep(1);
562
563             // server should disconnect now
564             try
565             {
566                 sc.write("other data(2)");
567                 sleep(1);
568                 sc.write("other data(3)");
569             }
570             catch(t2n_transfer_error &e)
571             { errormsg=e.what(); }
572             catch(...)
573             { throw; }
574
575             BOOST_CHECK_EQUAL(string("write() returned Broken pipe"),errormsg);
576         }
577     }
578 }
579
580 BOOST_AUTO_TEST_CASE(DisconnectOnRead)
581 {
582     pid_t pid2;
583
584     switch(child_pid=fork())
585     {
586         case -1:
587         {
588             BOOST_FAIL("fork error");
589             break;
590         }
591         case 0:
592         // child
593         {
594             try
595             {
596                 // wait till server is up
597                 sleep(1);
598
599                 socket_client_connection sc("./socket");
600
601                 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
602                 real_write_client_connection *rwc=(real_write_client_connection*)&sc;
603                 rwc->real_write(string(10000,'x'));
604             } catch(...)
605             {
606                 std::cerr << "exception in child. ignoring\n";
607             }
608
609             // don't call atexit and stuff
610             _exit(0);
611         }
612
613         default:
614         // parent
615         {
616             // don't kill us on broken pipe
617             signal(SIGPIPE, SIG_IGN);
618
619             socket_server ss("./socket");
620
621             time_t t0 = time(NULL);
622
623             // max 5 sec
624             while (time(NULL) < t0 + 5 )
625             {
626                 ss.fill_buffer(1000000);
627
628                 string data;
629                 ss.get_packet(data);
630             }
631
632             // are we still alive and able to process data?
633
634             switch(pid2=fork())
635             {
636                 case -1:
637                 {
638                     BOOST_FAIL("fork error");
639                     break;
640                 }
641                 case 0:
642                 // child
643                 {
644                     try
645                     {
646                         socket_client_connection *sc=new socket_client_connection("./socket");
647                         sc->write(string(10000,'x'));
648                         // socket is closed regularly
649                         delete sc;
650                     } catch(...)
651                     {
652                         std::cerr << "exception in child. ignoring\n";
653                     }
654
655                     // don't run regular cleanup, otherwise cppunit stuff gets called
656                     _exit(0);
657                 }
658
659                 default:
660                 // parent
661                 {
662                     string received;
663
664                     t0 = time(NULL);
665
666                     // max 10 sec
667                     while (time(NULL) < t0 + 10 )
668                     {
669                         ss.fill_buffer(1000000);
670
671                         if (ss.get_packet(received))
672                             break;
673                     }
674
675                     BOOST_CHECK_EQUAL(string(10000,'x'),received);
676                 }
677             }
678         }
679     }
680     kill(pid2,SIGKILL);
681 }
682
683 BOOST_AUTO_TEST_CASE(BreakAccept)
684 {
685     pid_t pid2;
686
687     switch(child_pid=fork())
688     {
689         case -1:
690         {
691             BOOST_FAIL("fork error");
692             break;
693         }
694         case 0:
695         // child
696         {
697             try
698             {
699                 // wait till server is really up and waiting
700                 sleep(2);
701
702                 // connect with very tight timeout and only 1 retry
703                 socket_client_connection sc("./socket",50,1);
704             } catch(...)
705             {
706                 std::cerr << "exception in child. ignoring\n";
707             }
708
709             // don't call atexit and stuff
710             _exit(0);
711         }
712
713         default:
714         // parent
715         {
716             // don't kill us on broken pipe
717             signal(SIGPIPE, SIG_IGN);
718
719             socket_server ss("./socket");
720
721             // server is "working" while client wants to connect
722             sleep(5);
723
724             time_t t0 = time(NULL);
725
726             // max 5 sec
727             while (time(NULL) < t0 + 5 )
728             {
729                 ss.fill_buffer(1000000);
730
731                 string data;
732                 ss.get_packet(data);
733             }
734
735             // are we still alive and able to process data?
736
737             switch(pid2=fork())
738             {
739                 case -1:
740                 {
741                     BOOST_FAIL("fork error");
742                     break;
743                 }
744                 case 0:
745                 // child
746                 {
747                     try
748                     {
749                         socket_client_connection *sc=new socket_client_connection("./socket");
750                         sc->write(string(10000,'x'));
751                         delete sc;
752                         // socket is closed regularly
753                     } catch(...)
754                     {
755                         std::cerr << "exception in child. ignoring\n";
756                     }
757
758                     // don't run regular cleanup, otherwise cppunit stuff gets called
759                     _exit(0);
760                 }
761
762                 default:
763                 // parent
764                 {
765                     string received;
766
767                     t0 = time(NULL);
768
769                     // max 10 sec
770                     while (time(NULL) < t0 + 10 )
771                     {
772                         ss.fill_buffer(1000000);
773
774                         if (ss.get_packet(received))
775                             break;
776                     }
777
778                     BOOST_CHECK_EQUAL(string(10000,'x'),received);
779                 }
780             }
781         }
782     }
783     kill(pid2,SIGKILL);
784 }
785
786 BOOST_AUTO_TEST_SUITE_END()