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