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