Remove code duplication in test fixtures
[libt2n] / test / timeout.cpp
CommitLineData
19facd85
TJ
1/*
2Copyright (C) 2004 by Intra2net AG
45a2ebc9 3
19facd85
TJ
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*/
45a2ebc9
GE
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
307b5e74
TJ
35#define BOOST_TEST_DYN_LINK
36#include <boost/test/unit_test.hpp>
45a2ebc9
GE
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
307b5e74
TJ
50#include "test_fixtures.hxx"
51
45a2ebc9
GE
52#ifdef HAVE_CONFIG_H
53#include <config.h>
54#endif
55
56using namespace std;
57using namespace libt2n;
45a2ebc9
GE
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
307b5e74
TJ
130class test_timeoutFixture : public KillChildOnShutdownFixture
131{
132protected:
45a2ebc9
GE
133 typedef uint32_t packet_size_indicator;
134
df94ded3
TJ
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
45a2ebc9 137 {
df94ded3
TJ
138 public:
139 void real_write(const std::string& data)
140 { socket_write(data); }
141 };
45a2ebc9
GE
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 }
307b5e74
TJ
159};
160
161BOOST_FIXTURE_TEST_SUITE(test_timeout, test_timeoutFixture)
162
163BOOST_AUTO_TEST_CASE(ConnectTimeout)
164{
165 switch(child_pid=fork())
c7857475 166 {
307b5e74 167 case -1:
c7857475 168 {
307b5e74
TJ
169 BOOST_FAIL("fork error");
170 break;
171 }
172 case 0:
173 // child
174 {
175 try
c7857475 176 {
307b5e74
TJ
177 socket_server ss("./socket");
178 } catch(...)
c7857475 179 {
307b5e74 180 std::cerr << "exception in child. ignoring\n";
c7857475
GE
181 }
182
307b5e74
TJ
183 // don't call atexit and stuff
184 _exit(0);
185 }
c7857475 186
307b5e74
TJ
187 default:
188 // parent
189 {
190 string data;
c7857475 191
307b5e74
TJ
192 // wait till server is up
193 sleep(1);
c7857475 194
307b5e74 195 string errormsg;
c7857475 196
307b5e74 197 socket_client_connection sc("./socket");
b5922184 198
307b5e74
TJ
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");
c7857475
GE
202 }
203 }
307b5e74 204}
c7857475 205
307b5e74
TJ
206BOOST_AUTO_TEST_CASE(HelloTimeoutNothing)
207{
208 switch(child_pid=fork())
45a2ebc9 209 {
307b5e74 210 case -1:
45a2ebc9 211 {
307b5e74
TJ
212 BOOST_FAIL("fork error");
213 break;
214 }
215 case 0:
216 // child
217 {
218 try
45a2ebc9 219 {
307b5e74 220 socket_server ss("./socket");
45a2ebc9 221
307b5e74
TJ
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";
45a2ebc9
GE
228 }
229
307b5e74
TJ
230 // don't call atexit and stuff
231 _exit(0);
232 }
45a2ebc9 233
307b5e74
TJ
234 default:
235 // parent
236 {
237 string data;
45a2ebc9 238
307b5e74
TJ
239 // wait till server is up
240 sleep(1);
241 socket_client_connection sc("./socket");
242 command_client cc(&sc,1000000,1000000);
45a2ebc9 243
307b5e74 244 t2n_exception* ep=cc.get_constuctor_exception();
45a2ebc9 245
307b5e74
TJ
246 string errormsg;
247 if (ep)
248 errormsg=ep->what();
249
250 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
45a2ebc9
GE
251 }
252 }
307b5e74 253}
45a2ebc9 254
307b5e74
TJ
255BOOST_AUTO_TEST_CASE(HelloTimeoutSlowData)
256{
257 switch(child_pid=fork())
45a2ebc9 258 {
307b5e74 259 case -1:
45a2ebc9 260 {
307b5e74
TJ
261 BOOST_FAIL("fork error");
262 break;
263 }
264 case 0:
265 // child
266 {
267 try
45a2ebc9 268 {
307b5e74 269 socket_server ss("./socket");
45a2ebc9 270
307b5e74
TJ
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 << ';';
45a2ebc9 277
307b5e74
TJ
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));
45a2ebc9 281
307b5e74 282 ss.add_callback(new_connection,bind(&test_timeout::HelloTimeoutSlowData::send_slow_raw_socket, boost::ref(*this), send_data,&ss, _1));
441d41fe 283
307b5e74
TJ
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";
45a2ebc9
GE
290 }
291
307b5e74
TJ
292 // don't call atexit and stuff
293 _exit(0);
294 }
45a2ebc9 295
307b5e74
TJ
296 default:
297 // parent
298 {
299 string data;
45a2ebc9 300
307b5e74
TJ
301 // wait till server is up
302 sleep(1);
303 socket_client_connection sc("./socket");
304 command_client cc(&sc,1000000,1000000);
45a2ebc9 305
307b5e74 306 t2n_exception* ep=cc.get_constuctor_exception();
45a2ebc9 307
307b5e74
TJ
308 string errormsg;
309 if (ep)
310 errormsg=ep->what();
311
312 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
45a2ebc9
GE
313 }
314 }
307b5e74 315}
45a2ebc9 316
307b5e74
TJ
317BOOST_AUTO_TEST_CASE(CommandTimeout)
318{
319 switch(child_pid=fork())
45a2ebc9 320 {
307b5e74 321 case -1:
45a2ebc9 322 {
307b5e74
TJ
323 BOOST_FAIL("fork error");
324 break;
325 }
326 case 0:
327 // child
328 {
329 try
45a2ebc9 330 {
307b5e74 331 socket_server ss("./socket");
45a2ebc9 332
307b5e74
TJ
333 ostringstream hello;
334 hello << "T2Nv" << PROTOCOL_VERSION << ';';
335 int byteordercheck=1;
336 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
337 hello << ';';
441d41fe 338
307b5e74 339 ss.add_callback(new_connection,bind(&test_timeout::CommandTimeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
45a2ebc9 340
307b5e74
TJ
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";
45a2ebc9
GE
347 }
348
307b5e74
TJ
349 // don't call atexit and stuff
350 _exit(0);
351 }
45a2ebc9 352
307b5e74
TJ
353 default:
354 // parent
355 {
356 string data;
45a2ebc9 357
307b5e74
TJ
358 // wait till server is up
359 sleep(1);
360 socket_client_connection sc("./socket");
45a2ebc9 361
307b5e74
TJ
362 command_client cc(&sc,1000000,1000000);
363 result_container rc;
45a2ebc9 364
307b5e74 365 string errormsg;
45a2ebc9 366
307b5e74
TJ
367 try
368 {
369 cc.send_command(new testfunc2_cmd("hello"),rc);
45a2ebc9 370 }
307b5e74
TJ
371 catch(t2n_transfer_error &e)
372 { errormsg=e.what(); }
373 catch(...)
374 { throw; }
375
376 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
45a2ebc9
GE
377 }
378 }
307b5e74 379}
45a2ebc9 380
307b5e74
TJ
381BOOST_AUTO_TEST_CASE(CommandSlowResponse)
382{
383 switch(child_pid=fork())
45a2ebc9 384 {
307b5e74 385 case -1:
45a2ebc9 386 {
307b5e74
TJ
387 BOOST_FAIL("fork error");
388 break;
389 }
390 case 0:
391 // child
392 {
393 try
45a2ebc9 394 {
307b5e74 395 socket_server ss("./socket");
45a2ebc9 396
307b5e74
TJ
397 ostringstream hello;
398 hello << "T2Nv" << PROTOCOL_VERSION << ';';
399 int byteordercheck=1;
400 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
401 hello << ';';
45a2ebc9 402
307b5e74 403 ss.add_callback(new_connection,bind(&test_timeout::CommandSlowResponse::send_hello, boost::ref(*this), hello.str(),&ss, _1));
45a2ebc9 404
307b5e74
TJ
405 // max 10 sec
406 for (int i=0; i < 10; i++)
407 {
408 ss.fill_buffer(1000000);
45a2ebc9 409
307b5e74
TJ
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);
45a2ebc9
GE
421 }
422 }
307b5e74
TJ
423 } catch(...)
424 {
425 std::cerr << "exception in child. ignoring\n";
45a2ebc9
GE
426 }
427
307b5e74
TJ
428 // don't call atexit and stuff
429 _exit(0);
430 }
45a2ebc9 431
307b5e74
TJ
432 default:
433 // parent
434 {
435 string data;
45a2ebc9 436
307b5e74
TJ
437 // wait till server is up
438 sleep(1);
439 socket_client_connection sc("./socket");
45a2ebc9 440
307b5e74
TJ
441 command_client cc(&sc,1000000,1000000);
442 result_container rc;
45a2ebc9 443
307b5e74 444 string errormsg;
45a2ebc9 445
307b5e74
TJ
446 try
447 {
448 cc.send_command(new testfunc2_cmd("hello"),rc);
45a2ebc9 449 }
307b5e74
TJ
450 catch(t2n_transfer_error &e)
451 { errormsg=e.what(); }
452 catch(...)
453 { throw; }
454
455 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
45a2ebc9
GE
456 }
457 }
307b5e74 458}
45a2ebc9 459
307b5e74
TJ
460BOOST_AUTO_TEST_CASE(DisconnectOnWrite)
461{
462 switch(child_pid=fork())
c7857475 463 {
307b5e74 464 case -1:
c7857475 465 {
307b5e74
TJ
466 BOOST_FAIL("fork error");
467 break;
468 }
469 case 0:
470 // child
471 {
472 try
c7857475 473 {
307b5e74 474 socket_server ss("./socket");
c7857475 475
307b5e74
TJ
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";
c7857475
GE
481 }
482
307b5e74
TJ
483 // don't call atexit and stuff
484 _exit(0);
485 }
c7857475 486
307b5e74
TJ
487 default:
488 // parent
489 {
490 string data;
c7857475 491
307b5e74
TJ
492 // don't kill us on broken pipe
493 signal(SIGPIPE, SIG_IGN);
c7857475 494
307b5e74
TJ
495 // wait till server is up
496 sleep(1);
497 socket_client_connection sc("./socket");
c7857475 498
307b5e74 499 string errormsg;
c7857475 500
307b5e74 501 string huge(5000000,'x');
c7857475 502
307b5e74
TJ
503 try
504 {
505 sc.write(huge);
c7857475 506 }
307b5e74
TJ
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);
c7857475
GE
513 }
514 }
307b5e74 515}
c7857475 516
307b5e74
TJ
517BOOST_AUTO_TEST_CASE(WriteTwice)
518{
519 switch(child_pid=fork())
039e52da 520 {
307b5e74 521 case -1:
039e52da 522 {
307b5e74
TJ
523 BOOST_FAIL("fork error");
524 break;
525 }
526 case 0:
527 // child
528 {
529 try
039e52da 530 {
307b5e74 531 socket_server ss("./socket");
039e52da 532
307b5e74
TJ
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";
039e52da
GE
539 }
540
307b5e74
TJ
541 // don't call atexit and stuff
542 _exit(0);
543 }
039e52da 544
307b5e74
TJ
545 default:
546 // parent
547 {
548 string data;
039e52da 549
307b5e74
TJ
550 // don't kill us on broken pipe
551 signal(SIGPIPE, SIG_IGN);
039e52da 552
307b5e74
TJ
553 // wait till server is up
554 sleep(1);
555 socket_client_connection sc("./socket");
039e52da 556
307b5e74 557 string errormsg;
039e52da 558
307b5e74 559 sc.write("somedata");
039e52da 560
307b5e74 561 sleep(1);
039e52da 562
307b5e74
TJ
563 // server should disconnect now
564 try
565 {
566 sc.write("other data(2)");
567 sleep(1);
568 sc.write("other data(3)");
039e52da 569 }
307b5e74
TJ
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);
039e52da
GE
576 }
577 }
307b5e74 578}
039e52da 579
307b5e74
TJ
580BOOST_AUTO_TEST_CASE(DisconnectOnRead)
581{
582 pid_t pid2;
c7857475 583
307b5e74
TJ
584 switch(child_pid=fork())
585 {
586 case -1:
587 {
588 BOOST_FAIL("fork error");
589 break;
590 }
591 case 0:
592 // child
c7857475 593 {
307b5e74 594 try
c7857475 595 {
307b5e74
TJ
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(...)
c7857475 605 {
307b5e74
TJ
606 std::cerr << "exception in child. ignoring\n";
607 }
c7857475 608
307b5e74
TJ
609 // don't call atexit and stuff
610 _exit(0);
611 }
c7857475 612
307b5e74
TJ
613 default:
614 // parent
615 {
616 // don't kill us on broken pipe
617 signal(SIGPIPE, SIG_IGN);
c7857475 618
307b5e74
TJ
619 socket_server ss("./socket");
620
621 time_t t0 = time(NULL);
c7857475 622
307b5e74
TJ
623 // max 5 sec
624 while (time(NULL) < t0 + 5 )
c7857475 625 {
307b5e74 626 ss.fill_buffer(1000000);
c7857475 627
307b5e74
TJ
628 string data;
629 ss.get_packet(data);
630 }
c7857475 631
307b5e74 632 // are we still alive and able to process data?
c7857475 633
307b5e74
TJ
634 switch(pid2=fork())
635 {
636 case -1:
c7857475 637 {
307b5e74
TJ
638 BOOST_FAIL("fork error");
639 break;
c7857475 640 }
307b5e74
TJ
641 case 0:
642 // child
c7857475 643 {
307b5e74 644 try
c7857475 645 {
307b5e74
TJ
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(...)
c7857475 651 {
307b5e74 652 std::cerr << "exception in child. ignoring\n";
c7857475
GE
653 }
654
307b5e74
TJ
655 // don't run regular cleanup, otherwise cppunit stuff gets called
656 _exit(0);
657 }
c7857475 658
307b5e74
TJ
659 default:
660 // parent
661 {
662 string received;
c7857475 663
307b5e74 664 t0 = time(NULL);
c7857475 665
307b5e74
TJ
666 // max 10 sec
667 while (time(NULL) < t0 + 10 )
668 {
669 ss.fill_buffer(1000000);
c7857475 670
307b5e74
TJ
671 if (ss.get_packet(received))
672 break;
c7857475 673 }
307b5e74
TJ
674
675 BOOST_CHECK_EQUAL(string(10000,'x'),received);
c7857475
GE
676 }
677 }
678 }
679 }
307b5e74
TJ
680 kill(pid2,SIGKILL);
681}
c7857475 682
307b5e74
TJ
683BOOST_AUTO_TEST_CASE(BreakAccept)
684{
685 pid_t pid2;
c7857475 686
307b5e74
TJ
687 switch(child_pid=fork())
688 {
689 case -1:
c7857475 690 {
307b5e74
TJ
691 BOOST_FAIL("fork error");
692 break;
693 }
694 case 0:
695 // child
696 {
697 try
c7857475 698 {
307b5e74
TJ
699 // wait till server is really up and waiting
700 sleep(2);
c7857475 701
307b5e74
TJ
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";
c7857475
GE
707 }
708
307b5e74
TJ
709 // don't call atexit and stuff
710 _exit(0);
711 }
c7857475 712
307b5e74
TJ
713 default:
714 // parent
715 {
716 // don't kill us on broken pipe
717 signal(SIGPIPE, SIG_IGN);
c7857475 718
307b5e74 719 socket_server ss("./socket");
c7857475 720
307b5e74
TJ
721 // server is "working" while client wants to connect
722 sleep(5);
c7857475 723
307b5e74 724 time_t t0 = time(NULL);
c7857475 725
307b5e74
TJ
726 // max 5 sec
727 while (time(NULL) < t0 + 5 )
728 {
729 ss.fill_buffer(1000000);
c7857475 730
307b5e74
TJ
731 string data;
732 ss.get_packet(data);
733 }
c7857475 734
307b5e74
TJ
735 // are we still alive and able to process data?
736
737 switch(pid2=fork())
738 {
739 case -1:
c7857475 740 {
307b5e74
TJ
741 BOOST_FAIL("fork error");
742 break;
743 }
744 case 0:
745 // child
746 {
747 try
c7857475 748 {
307b5e74
TJ
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(...)
c7857475 754 {
307b5e74 755 std::cerr << "exception in child. ignoring\n";
c7857475
GE
756 }
757
307b5e74
TJ
758 // don't run regular cleanup, otherwise cppunit stuff gets called
759 _exit(0);
760 }
c7857475 761
307b5e74
TJ
762 default:
763 // parent
764 {
765 string received;
c7857475 766
307b5e74 767 t0 = time(NULL);
c7857475 768
307b5e74
TJ
769 // max 10 sec
770 while (time(NULL) < t0 + 10 )
771 {
772 ss.fill_buffer(1000000);
c7857475 773
307b5e74
TJ
774 if (ss.get_packet(received))
775 break;
c7857475 776 }
307b5e74
TJ
777
778 BOOST_CHECK_EQUAL(string(10000,'x'),received);
c7857475
GE
779 }
780 }
781 }
782 }
307b5e74
TJ
783 kill(pid2,SIGKILL);
784}
45a2ebc9 785
307b5e74 786BOOST_AUTO_TEST_SUITE_END()