Remove code duplication in test fixtures
[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
130class test_timeoutFixture : public KillChildOnShutdownFixture
131{
132protected:
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
161BOOST_FIXTURE_TEST_SUITE(test_timeout, test_timeoutFixture)
162
163BOOST_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
206BOOST_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
255BOOST_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
317BOOST_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
381BOOST_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
460BOOST_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
517BOOST_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
580BOOST_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
683BOOST_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
786BOOST_AUTO_TEST_SUITE_END()