Migrate from cppunit to Boost.test
[libt2n] / test / timeout.cpp
... / ...
CommitLineData
1/*
2Copyright (C) 2004 by Intra2net AG
3
4The software in this package is distributed under the GNU General
5Public License version 2 (with a special exception described below).
6
7A copy of GNU General Public License (GPL) is included in this distribution,
8in the file COPYING.GPL.
9
10As a special exception, if other files instantiate templates or use macros
11or inline functions from this file, or you compile this file and link it
12with other works to produce a work based on this file, this file
13does not by itself cause the resulting work to be covered
14by the GNU General Public License.
15
16However the source code for this file must still be made available
17in accordance with section (3) of the GNU General Public License.
18
19This exception does not invalidate any other reasons why a work based
20on 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
56using namespace std;
57using namespace libt2n;
58
59string 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
68class 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
97class 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
127BOOST_CLASS_EXPORT(testfunc2_cmd)
128BOOST_CLASS_EXPORT(testfunc2_res)
129
130// this is an evil hack to get access to real_write, don't ever do this in an app!!!
131class real_write_connection: public socket_server_connection
132{
133 public:
134 void real_write(const std::string& data)
135 { socket_write(data); }
136};
137
138// this is an evil hack to get access to real_write, don't ever do this in an app!!!
139class real_write_client_connection: public socket_client_connection
140{
141 public:
142 void real_write(const std::string& data)
143 { socket_write(data); }
144};
145
146
147class test_timeoutFixture : public KillChildOnShutdownFixture
148{
149protected:
150 typedef uint32_t packet_size_indicator;
151
152 void send_hello(string hello_string, socket_server* ss, unsigned int conn_id)
153 {
154 server_connection *sc=ss->get_connection(conn_id);
155 sc->write(hello_string);
156 }
157
158 void send_slow_raw_socket(string data, socket_server* ss, unsigned int conn_id)
159 {
160 socket_server_connection *ssc=dynamic_cast<socket_server_connection*>(ss->get_connection(conn_id));
161
162 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
163 real_write_connection *rwc=(real_write_connection*)ssc;
164
165 // we write one char each 0.2 sec
166 for (int pos=0; pos < data.size(); pos++)
167 {
168 string onebyte;
169 onebyte+=data[pos];
170 rwc->real_write(onebyte);
171 usleep(200000);
172 }
173 }
174
175public:
176 test_timeoutFixture()
177 {
178 }
179
180 ~test_timeoutFixture()
181 {
182 }
183};
184
185BOOST_FIXTURE_TEST_SUITE(test_timeout, test_timeoutFixture)
186
187BOOST_AUTO_TEST_CASE(ConnectTimeout)
188{
189 switch(child_pid=fork())
190 {
191 case -1:
192 {
193 BOOST_FAIL("fork error");
194 break;
195 }
196 case 0:
197 // child
198 {
199 try
200 {
201 socket_server ss("./socket");
202 } catch(...)
203 {
204 std::cerr << "exception in child. ignoring\n";
205 }
206
207 // don't call atexit and stuff
208 _exit(0);
209 }
210
211 default:
212 // parent
213 {
214 string data;
215
216 // wait till server is up
217 sleep(1);
218
219 string errormsg;
220
221 socket_client_connection sc("./socket");
222
223 BOOST_CHECK_MESSAGE(sc.connection::is_closed() == true, "connection not closed");
224
225 BOOST_CHECK_MESSAGE(sc.get_last_error_msg() == string("no more retries left after connect error"), "wrong errormessage");
226 }
227 }
228}
229
230BOOST_AUTO_TEST_CASE(HelloTimeoutNothing)
231{
232 switch(child_pid=fork())
233 {
234 case -1:
235 {
236 BOOST_FAIL("fork error");
237 break;
238 }
239 case 0:
240 // child
241 {
242 try
243 {
244 socket_server ss("./socket");
245
246 // max 10 sec
247 for (int i=0; i < 10; i++)
248 ss.fill_buffer(1000000);
249 } catch(...)
250 {
251 std::cerr << "exception in child. ignoring\n";
252 }
253
254 // don't call atexit and stuff
255 _exit(0);
256 }
257
258 default:
259 // parent
260 {
261 string data;
262
263 // wait till server is up
264 sleep(1);
265 socket_client_connection sc("./socket");
266 command_client cc(&sc,1000000,1000000);
267
268 t2n_exception* ep=cc.get_constuctor_exception();
269
270 string errormsg;
271 if (ep)
272 errormsg=ep->what();
273
274 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
275 }
276 }
277}
278
279BOOST_AUTO_TEST_CASE(HelloTimeoutSlowData)
280{
281 switch(child_pid=fork())
282 {
283 case -1:
284 {
285 BOOST_FAIL("fork error");
286 break;
287 }
288 case 0:
289 // child
290 {
291 try
292 {
293 socket_server ss("./socket");
294
295 // create a valid packet
296 ostringstream hello;
297 hello << "T2Nv" << PROTOCOL_VERSION << ';';
298 int byteordercheck=1;
299 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
300 hello << ';';
301
302 packet_size_indicator psize=htonl(hello.str().size());
303 std::string send_data(hello.str());
304 send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
305
306 ss.add_callback(new_connection,bind(&test_timeout::HelloTimeoutSlowData::send_slow_raw_socket, boost::ref(*this), send_data,&ss, _1));
307
308 // max 10 sec
309 for (int i=0; i < 10; i++)
310 ss.fill_buffer(1000000);
311 } catch(...)
312 {
313 std::cerr << "exception in child. ignoring\n";
314 }
315
316 // don't call atexit and stuff
317 _exit(0);
318 }
319
320 default:
321 // parent
322 {
323 string data;
324
325 // wait till server is up
326 sleep(1);
327 socket_client_connection sc("./socket");
328 command_client cc(&sc,1000000,1000000);
329
330 t2n_exception* ep=cc.get_constuctor_exception();
331
332 string errormsg;
333 if (ep)
334 errormsg=ep->what();
335
336 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
337 }
338 }
339}
340
341BOOST_AUTO_TEST_CASE(CommandTimeout)
342{
343 switch(child_pid=fork())
344 {
345 case -1:
346 {
347 BOOST_FAIL("fork error");
348 break;
349 }
350 case 0:
351 // child
352 {
353 try
354 {
355 socket_server ss("./socket");
356
357 ostringstream hello;
358 hello << "T2Nv" << PROTOCOL_VERSION << ';';
359 int byteordercheck=1;
360 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
361 hello << ';';
362
363 ss.add_callback(new_connection,bind(&test_timeout::CommandTimeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
364
365 // max 10 sec
366 for (int i=0; i < 10; i++)
367 ss.fill_buffer(1000000);
368 } catch(...)
369 {
370 std::cerr << "exception in child. ignoring\n";
371 }
372
373 // don't call atexit and stuff
374 _exit(0);
375 }
376
377 default:
378 // parent
379 {
380 string data;
381
382 // wait till server is up
383 sleep(1);
384 socket_client_connection sc("./socket");
385
386 command_client cc(&sc,1000000,1000000);
387 result_container rc;
388
389 string errormsg;
390
391 try
392 {
393 cc.send_command(new testfunc2_cmd("hello"),rc);
394 }
395 catch(t2n_transfer_error &e)
396 { errormsg=e.what(); }
397 catch(...)
398 { throw; }
399
400 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
401 }
402 }
403}
404
405BOOST_AUTO_TEST_CASE(CommandSlowResponse)
406{
407 switch(child_pid=fork())
408 {
409 case -1:
410 {
411 BOOST_FAIL("fork error");
412 break;
413 }
414 case 0:
415 // child
416 {
417 try
418 {
419 socket_server ss("./socket");
420
421 ostringstream hello;
422 hello << "T2Nv" << PROTOCOL_VERSION << ';';
423 int byteordercheck=1;
424 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
425 hello << ';';
426
427 ss.add_callback(new_connection,bind(&test_timeout::CommandSlowResponse::send_hello, boost::ref(*this), hello.str(),&ss, _1));
428
429 // max 10 sec
430 for (int i=0; i < 10; i++)
431 {
432 ss.fill_buffer(1000000);
433
434 string data;
435 unsigned int cid;
436
437 if(ss.get_packet(data,cid))
438 {
439 // create a valid packet & send
440 string response="abcdefghijklmnopqrstuvwxyz";
441 packet_size_indicator psize=htonl(response.size());
442 std::string send_data(response);
443 send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
444 send_slow_raw_socket(send_data,&ss,cid);
445 }
446 }
447 } catch(...)
448 {
449 std::cerr << "exception in child. ignoring\n";
450 }
451
452 // don't call atexit and stuff
453 _exit(0);
454 }
455
456 default:
457 // parent
458 {
459 string data;
460
461 // wait till server is up
462 sleep(1);
463 socket_client_connection sc("./socket");
464
465 command_client cc(&sc,1000000,1000000);
466 result_container rc;
467
468 string errormsg;
469
470 try
471 {
472 cc.send_command(new testfunc2_cmd("hello"),rc);
473 }
474 catch(t2n_transfer_error &e)
475 { errormsg=e.what(); }
476 catch(...)
477 { throw; }
478
479 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
480 }
481 }
482}
483
484BOOST_AUTO_TEST_CASE(DisconnectOnWrite)
485{
486 switch(child_pid=fork())
487 {
488 case -1:
489 {
490 BOOST_FAIL("fork error");
491 break;
492 }
493 case 0:
494 // child
495 {
496 try
497 {
498 socket_server ss("./socket");
499
500 // bail out as soon as we get something
501 ss.fill_buffer(-1);
502 } catch(...)
503 {
504 std::cerr << "exception in child. ignoring\n";
505 }
506
507 // don't call atexit and stuff
508 _exit(0);
509 }
510
511 default:
512 // parent
513 {
514 string data;
515
516 // don't kill us on broken pipe
517 signal(SIGPIPE, SIG_IGN);
518
519 // wait till server is up
520 sleep(1);
521 socket_client_connection sc("./socket");
522
523 string errormsg;
524
525 string huge(5000000,'x');
526
527 try
528 {
529 sc.write(huge);
530 }
531 catch(t2n_transfer_error &e)
532 { errormsg=e.what(); }
533 catch(...)
534 { throw; }
535
536 BOOST_CHECK_EQUAL(string("write() returned Broken pipe"),errormsg);
537 }
538 }
539}
540
541BOOST_AUTO_TEST_CASE(WriteTwice)
542{
543 switch(child_pid=fork())
544 {
545 case -1:
546 {
547 BOOST_FAIL("fork error");
548 break;
549 }
550 case 0:
551 // child
552 {
553 try
554 {
555 socket_server ss("./socket");
556
557 // bail out as soon as we get something
558 ss.fill_buffer(-1);
559 ss.fill_buffer(-1);
560 } catch(...)
561 {
562 std::cerr << "exception in child. ignoring\n";
563 }
564
565 // don't call atexit and stuff
566 _exit(0);
567 }
568
569 default:
570 // parent
571 {
572 string data;
573
574 // don't kill us on broken pipe
575 signal(SIGPIPE, SIG_IGN);
576
577 // wait till server is up
578 sleep(1);
579 socket_client_connection sc("./socket");
580
581 string errormsg;
582
583 sc.write("somedata");
584
585 sleep(1);
586
587 // server should disconnect now
588 try
589 {
590 sc.write("other data(2)");
591 sleep(1);
592 sc.write("other data(3)");
593 }
594 catch(t2n_transfer_error &e)
595 { errormsg=e.what(); }
596 catch(...)
597 { throw; }
598
599 BOOST_CHECK_EQUAL(string("write() returned Broken pipe"),errormsg);
600 }
601 }
602}
603
604BOOST_AUTO_TEST_CASE(DisconnectOnRead)
605{
606 pid_t pid2;
607
608 switch(child_pid=fork())
609 {
610 case -1:
611 {
612 BOOST_FAIL("fork error");
613 break;
614 }
615 case 0:
616 // child
617 {
618 try
619 {
620 // wait till server is up
621 sleep(1);
622
623 socket_client_connection sc("./socket");
624
625 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
626 real_write_client_connection *rwc=(real_write_client_connection*)&sc;
627 rwc->real_write(string(10000,'x'));
628 } catch(...)
629 {
630 std::cerr << "exception in child. ignoring\n";
631 }
632
633 // don't call atexit and stuff
634 _exit(0);
635 }
636
637 default:
638 // parent
639 {
640 // don't kill us on broken pipe
641 signal(SIGPIPE, SIG_IGN);
642
643 socket_server ss("./socket");
644
645 time_t t0 = time(NULL);
646
647 // max 5 sec
648 while (time(NULL) < t0 + 5 )
649 {
650 ss.fill_buffer(1000000);
651
652 string data;
653 ss.get_packet(data);
654 }
655
656 // are we still alive and able to process data?
657
658 switch(pid2=fork())
659 {
660 case -1:
661 {
662 BOOST_FAIL("fork error");
663 break;
664 }
665 case 0:
666 // child
667 {
668 try
669 {
670 socket_client_connection *sc=new socket_client_connection("./socket");
671 sc->write(string(10000,'x'));
672 // socket is closed regularly
673 delete sc;
674 } catch(...)
675 {
676 std::cerr << "exception in child. ignoring\n";
677 }
678
679 // don't run regular cleanup, otherwise cppunit stuff gets called
680 _exit(0);
681 }
682
683 default:
684 // parent
685 {
686 string received;
687
688 t0 = time(NULL);
689
690 // max 10 sec
691 while (time(NULL) < t0 + 10 )
692 {
693 ss.fill_buffer(1000000);
694
695 if (ss.get_packet(received))
696 break;
697 }
698
699 BOOST_CHECK_EQUAL(string(10000,'x'),received);
700 }
701 }
702 }
703 }
704 kill(pid2,SIGKILL);
705}
706
707BOOST_AUTO_TEST_CASE(BreakAccept)
708{
709 pid_t pid2;
710
711 switch(child_pid=fork())
712 {
713 case -1:
714 {
715 BOOST_FAIL("fork error");
716 break;
717 }
718 case 0:
719 // child
720 {
721 try
722 {
723 // wait till server is really up and waiting
724 sleep(2);
725
726 // connect with very tight timeout and only 1 retry
727 socket_client_connection sc("./socket",50,1);
728 } catch(...)
729 {
730 std::cerr << "exception in child. ignoring\n";
731 }
732
733 // don't call atexit and stuff
734 _exit(0);
735 }
736
737 default:
738 // parent
739 {
740 // don't kill us on broken pipe
741 signal(SIGPIPE, SIG_IGN);
742
743 socket_server ss("./socket");
744
745 // server is "working" while client wants to connect
746 sleep(5);
747
748 time_t t0 = time(NULL);
749
750 // max 5 sec
751 while (time(NULL) < t0 + 5 )
752 {
753 ss.fill_buffer(1000000);
754
755 string data;
756 ss.get_packet(data);
757 }
758
759 // are we still alive and able to process data?
760
761 switch(pid2=fork())
762 {
763 case -1:
764 {
765 BOOST_FAIL("fork error");
766 break;
767 }
768 case 0:
769 // child
770 {
771 try
772 {
773 socket_client_connection *sc=new socket_client_connection("./socket");
774 sc->write(string(10000,'x'));
775 delete sc;
776 // socket is closed regularly
777 } catch(...)
778 {
779 std::cerr << "exception in child. ignoring\n";
780 }
781
782 // don't run regular cleanup, otherwise cppunit stuff gets called
783 _exit(0);
784 }
785
786 default:
787 // parent
788 {
789 string received;
790
791 t0 = time(NULL);
792
793 // max 10 sec
794 while (time(NULL) < t0 + 10 )
795 {
796 ss.fill_buffer(1000000);
797
798 if (ss.get_packet(received))
799 break;
800 }
801
802 BOOST_CHECK_EQUAL(string(10000,'x'),received);
803 }
804 }
805 }
806 }
807 kill(pid2,SIGKILL);
808}
809
810BOOST_AUTO_TEST_SUITE_END()