libt2n: (reinhard) 1 Million is not enough! (+ doc fix)
[libt2n] / test / wrapper.cpp
1 /***************************************************************************
2  *   Copyright (C) 2004 by Intra2net AG                                    *
3  *   info@intra2net.com                                                    *
4  *                                                                         *
5  ***************************************************************************/
6
7 #include <sys/types.h>
8 #include <unistd.h>
9 #include <errno.h>
10 #include <signal.h>
11 #include <stdio.h>
12
13 #include <iostream>
14 #include <string>
15 #include <sstream>
16 #include <stdexcept>
17
18 #include <cppunit/extensions/TestFactoryRegistry.h>
19 #include <cppunit/ui/text/TestRunner.h>
20 #include <cppunit/extensions/HelperMacros.h>
21
22 #include <boost/archive/binary_oarchive.hpp>
23 #include <boost/archive/binary_iarchive.hpp>
24 #include <boost/archive/xml_oarchive.hpp>
25 #include <boost/archive/xml_iarchive.hpp>
26 #include <boost/serialization/serialization.hpp>
27 #include <boost/serialization/export.hpp>
28
29 #include <container.hxx>
30 #include <socket_client.hxx>
31 #include <socket_server.hxx>
32 #include <command_client.hxx>
33 #include <command_server.hxx>
34 #include <client_wrapper.hxx>
35 #include <socket_wrapper.hxx>
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 using namespace std;
42 using namespace libt2n;
43 using namespace CppUnit;
44
45 // the server part
46
47 stringstream logstream;
48 bool close_server=false;
49 bool kill_server=false;
50
51 int serverfunc(int i)
52 {
53     // magic commands
54     if (i==42)
55         close_server=true;
56     if (i==666)
57         kill_server=true;
58
59     return i+1;
60 }
61
62 std::string getserverlog(void)
63 {
64     return logstream.str();
65 }
66
67 class serverfunc_res : public libt2n::result
68 {
69     private:
70         int res;
71
72         friend class boost::serialization::access;
73         template<class Archive>
74         void serialize(Archive & ar, const unsigned int version)
75         {
76             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result);
77             ar & BOOST_SERIALIZATION_NVP(res);
78         }
79
80     public:
81         serverfunc_res()
82             { }
83
84         serverfunc_res(int i)
85         {
86             res=i;
87         }
88
89         int get_data()
90         {
91             return res;
92         }
93 };
94
95 class getserverlog_res : public libt2n::result
96 {
97     private:
98         std::string res;
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::result);
105             ar & BOOST_SERIALIZATION_NVP(res);
106         }
107
108     public:
109         getserverlog_res()
110             { }
111
112         getserverlog_res(std::string s)
113         {
114             res=s;
115         }
116
117         std::string get_data()
118         {
119             return res;
120         }
121 };
122
123 class cmd_group_x : public command
124 {
125     private:
126         friend class boost::serialization::access;
127         template<class Archive>
128         void serialize(Archive & ar, const unsigned int version)
129         {
130             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::command);
131         }
132 };
133
134 class serverfunc_cmd : public cmd_group_x
135 {
136     private:
137         int param;
138
139         friend class boost::serialization::access;
140         template<class Archive>
141         void serialize(Archive & ar, const unsigned int version)
142         {
143             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(cmd_group_x);
144             ar & BOOST_SERIALIZATION_NVP(param);
145         }
146
147     public:
148         serverfunc_cmd()
149             { }
150
151         serverfunc_cmd(int i)
152         {
153             param=i;
154         }
155
156         libt2n::result* operator()()
157         {
158             return new serverfunc_res(serverfunc(param));
159         }
160 };
161
162 class getserverlog_cmd : public cmd_group_x
163 {
164     private:
165         friend class boost::serialization::access;
166         template<class Archive>
167         void serialize(Archive & ar, const unsigned int version)
168         {
169             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(cmd_group_x);
170         }
171
172     public:
173         getserverlog_cmd()
174             { }
175
176         libt2n::result* operator()()
177         {
178             return new getserverlog_res(getserverlog());
179         }
180 };
181
182 BOOST_CLASS_EXPORT(serverfunc_res)
183 BOOST_CLASS_EXPORT(getserverlog_res)
184 BOOST_CLASS_EXPORT(cmd_group_x)
185 BOOST_CLASS_EXPORT(serverfunc_cmd)
186 BOOST_CLASS_EXPORT(getserverlog_cmd)
187
188 class cmd_group_x_client : public command_client
189 {
190     public:
191         cmd_group_x_client(libt2n::client_connection *_c,
192          long long _command_timeout_usec=command_timeout_usec_default,
193          long long _hello_timeout_usec=hello_timeout_usec_default)
194          : libt2n::command_client(_c,_command_timeout_usec,_hello_timeout_usec)
195         {}
196
197     int serverfunc(int i)
198     {
199         libt2n::result_container rc;
200
201         send_command(new serverfunc_cmd(i), rc);
202         serverfunc_res* res=dynamic_cast<serverfunc_res*>(rc.get_result());
203         if (!res) throw libt2n::t2n_communication_error("result object of wrong type");
204         return res->get_data();
205     }
206
207     std::string getserverlog(void)
208     {
209         libt2n::result_container rc;
210
211         send_command(new getserverlog_cmd(), rc);
212         getserverlog_res* res=dynamic_cast<getserverlog_res*>(rc.get_result());
213         if (!res) throw libt2n::t2n_communication_error("result object of wrong type");
214         return res->get_data();
215     }
216 };
217
218 typedef T2nSingletonWrapper<cmd_group_x_client> wraptype;
219
220 template<>
221 std::auto_ptr<wraptype> wraptype::SingletonObject = std::auto_ptr<wraptype>();
222
223 template<>
224 std::auto_ptr<ConnectionWrapper> wraptype::WrappedConnection = std::auto_ptr<ConnectionWrapper>();
225
226 class test_wrapper : public TestFixture
227 {
228     CPPUNIT_TEST_SUITE(test_wrapper);
229
230     CPPUNIT_TEST(no_init_exception); // must be called first!!!
231     CPPUNIT_TEST(simple_wrap);
232     CPPUNIT_TEST(double_use);
233     CPPUNIT_TEST(double_use_with_close);
234     CPPUNIT_TEST(reconnect_after_close);
235     CPPUNIT_TEST(reconnect_not_possible);
236     CPPUNIT_TEST(ignore_server_disconnect);
237     CPPUNIT_TEST(ignore_handler_reconnects);
238
239     CPPUNIT_TEST_SUITE_END();
240
241     public:
242
243     pid_t child_pid;
244
245     void setUp()
246     {
247         close_server=false;
248         kill_server=false;
249
250         switch(child_pid=fork())
251         {
252             case -1:
253             {
254                 CPPUNIT_FAIL("fork error");
255                 break;
256             }
257             case 0:
258             // child
259             {
260                 int i=0;
261                 while(i < 10 && !kill_server)
262                 {
263                     close_server=false;
264
265                     socket_server ss("./socket");
266                     group_command_server<cmd_group_x> cs(ss);
267                     ss.set_logging(&logstream,debug);
268
269                     // max 10 sec
270                     for (; !close_server && !kill_server && i < 10; i++)
271                         cs.handle(1000000);
272                 }
273
274                 // don't call atexit and stuff
275                 _exit(0);
276             }
277
278             default:
279             // parent
280             {
281                 // wait till server is up
282                 sleep(1);
283
284             }
285         }
286     }
287
288     void tearDown()
289     {
290         // make sure the server-child is dead before the next test runs
291         kill(child_pid,SIGKILL);
292         sleep(1);
293     }
294
295     void no_init_exception()
296     {
297         CPPUNIT_ASSERT_THROW(t2n_exec(&cmd_group_x_client::serverfunc)(1),std::logic_error);
298     }
299
300     void simple_wrap()
301     {
302         wraptype::set_connection(auto_ptr<ConnectionWrapper>
303             (new BasicSocketWrapper("./socket")));
304
305         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
306
307         CPPUNIT_ASSERT_EQUAL(2,i);
308     }
309
310     void double_use()
311     {
312         // only one connection used?
313         wraptype::set_connection(auto_ptr<ConnectionWrapper>
314             (new BasicSocketWrapper("./socket")));
315
316         t2n_exec(&cmd_group_x_client::serverfunc)(17);
317         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
318
319         // count the number of times that "new connection accepted" appears in the server log
320         string::size_type p=0;
321         int cnt=0;
322         while ((p=out.find("new connection accepted",p))++ != string::npos)
323             cnt++;
324
325         CPPUNIT_ASSERT_EQUAL(1,cnt);
326     }
327
328     void double_use_with_close()
329     {
330         wraptype::set_connection(auto_ptr<ConnectionWrapper>
331             (new BasicSocketWrapper("./socket")));
332
333         t2n_exec(&cmd_group_x_client::serverfunc)(17);
334
335         // closes the connection from the client side
336         wraptype::set_connection(auto_ptr<ConnectionWrapper>
337             (new BasicSocketWrapper("./socket")));
338
339         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
340
341         // count the number of times that "new connection accepted" appears in the server log
342         string::size_type p=0;
343         int cnt=0;
344         while ((p=out.find("new connection accepted",p))++ != string::npos)
345             cnt++;
346
347         CPPUNIT_ASSERT_EQUAL(2,cnt);
348     }
349
350     void reconnect_after_close()
351     {
352         wraptype::set_connection(auto_ptr<ConnectionWrapper>
353             (new ReconnectSocketWrapper("./socket")));
354
355         wraptype::get_connection_wrapper()->set_command_timeout_usec(3000000);
356         wraptype::get_connection_wrapper()->set_hello_timeout_usec(3000000);
357
358         // 42 closes connection on the server side
359         t2n_exec(&cmd_group_x_client::serverfunc)(42);
360
361         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
362
363         // count the number of times that "new connection accepted" appears in the server log
364         string::size_type p=0;
365         int cnt=0;
366         while ((p=out.find("new connection accepted",p))++ != string::npos)
367             cnt++;
368
369         CPPUNIT_ASSERT_EQUAL(2,cnt);
370     }
371
372     void reconnect_not_possible()
373     {
374         wraptype::set_connection(auto_ptr<ConnectionWrapper>
375             (new ReconnectSocketWrapper("./socket")));
376
377         // the server doens't like the beast
378         t2n_exec(&cmd_group_x_client::serverfunc)(666);
379
380         CPPUNIT_ASSERT_THROW(t2n_exec(&cmd_group_x_client::serverfunc)(1),t2n_communication_error);
381     }
382
383     void ignore_server_disconnect()
384     {
385         wraptype::set_connection(auto_ptr<ConnectionWrapper>
386             (new ReconnectIgnoreFailureSocketWrapper("./socket")));
387
388         // the server doens't like the beast
389         t2n_exec(&cmd_group_x_client::serverfunc)(666);
390
391         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
392
393         // result is constructed with default constructor on error-and-ignore  -> i=0
394
395         CPPUNIT_ASSERT_EQUAL(0,i);
396     }
397
398     void ignore_handler_reconnects()
399     {
400         wraptype::set_connection(auto_ptr<ConnectionWrapper>
401             (new ReconnectIgnoreFailureSocketWrapper("./socket")));
402
403         wraptype::get_connection_wrapper()->set_command_timeout_usec(3000000);
404         wraptype::get_connection_wrapper()->set_hello_timeout_usec(3000000);
405
406         // 42 closes connection on the server side
407         t2n_exec(&cmd_group_x_client::serverfunc)(42);
408
409         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
410
411         // count the number of times that "new connection accepted" appears in the server log
412         string::size_type p=0;
413         int cnt=0;
414         while ((p=out.find("new connection accepted",p))++ != string::npos)
415             cnt++;
416
417         CPPUNIT_ASSERT_EQUAL(2,cnt);
418     }
419
420 };
421
422 CPPUNIT_TEST_SUITE_REGISTRATION(test_wrapper);
423
424
425 class test_wrapper_noserver : public TestFixture
426 {
427     CPPUNIT_TEST_SUITE(test_wrapper_noserver);
428
429     CPPUNIT_TEST(ignore_noserver);
430     CPPUNIT_TEST(ignore_finds_lateserver);
431     CPPUNIT_TEST(ignore_wrongserver);
432
433     CPPUNIT_TEST_SUITE_END();
434
435     public:
436
437     pid_t child_pid;
438
439     void setUp()
440     {
441         child_pid=0;
442     }
443
444     void tearDown()
445     {
446         // make sure the server-child is dead before the next test runs
447         if (child_pid != 0)
448         {
449             kill(child_pid,SIGKILL);
450             sleep(1);
451         }
452     }
453
454     void ignore_noserver()
455     {
456         wraptype::set_connection(auto_ptr<ConnectionWrapper>
457             (new ReconnectIgnoreFailureSocketWrapper("./socket")));
458
459         // wraptype::get_connection_wrapper()->set_logging(&cerr,debug);
460
461         // there is no server
462
463         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
464
465         // result is constructed with default constructor on error-and-ignore  -> i=0
466
467         CPPUNIT_ASSERT_EQUAL(0,i);
468     }
469
470     void ignore_finds_lateserver()
471     {
472         wraptype::set_connection(auto_ptr<ConnectionWrapper>
473             (new ReconnectIgnoreFailureSocketWrapper("./socket")));
474
475         // there is no server
476         t2n_exec(&cmd_group_x_client::serverfunc)(1);
477
478         // launch a server
479         close_server=false;
480         kill_server=false;
481
482         switch(child_pid=fork())
483         {
484             case -1:
485             {
486                 CPPUNIT_FAIL("fork error");
487                 break;
488             }
489             case 0:
490             // child
491             {
492                 int i=0;
493                 while(i < 10 && !kill_server)
494                 {
495                     close_server=false;
496
497                     socket_server ss("./socket");
498                     group_command_server<cmd_group_x> cs(ss);
499                     ss.set_logging(&logstream,debug);
500
501                     // max 10 sec
502                     for (; !close_server && !kill_server && i < 10; i++)
503                         cs.handle(1000000);
504                 }
505
506                 // don't call atexit and stuff
507                 _exit(0);
508             }
509
510             default:
511             // parent
512             {
513                 // wait till server is up
514                 sleep(1);
515             }
516         }
517
518         // server should be active
519         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
520
521         CPPUNIT_ASSERT_EQUAL(2,i);
522     }
523
524     void send_hello(string hello_string, socket_server* ss, int conn_id)
525     {
526         server_connection *sc=ss->get_connection(conn_id);
527         sc->write(hello_string);
528     }
529
530     void ignore_wrongserver()
531     {
532         wraptype::set_connection(auto_ptr<ConnectionWrapper>
533             (new ReconnectIgnoreFailureSocketWrapper("./socket")));
534
535         // launch a server
536
537         switch(child_pid=fork())
538         {
539             case -1:
540             {
541                 CPPUNIT_FAIL("fork error");
542                 break;
543             }
544             case 0:
545             // child
546             {
547                 socket_server ss("./socket");
548
549                 // server sends garbage
550
551                 ostringstream hello;
552                 hello << "XYZ 123";
553
554                 ss.add_callback(new_connection,bind(&test_wrapper_noserver::send_hello, boost::ref(*this), hello.str(),&ss, _1));
555
556                 // max 10 sec
557                 for (int i=0; i < 10; i++)
558                     ss.fill_buffer(1000000);
559                 // don't call atexit and stuff
560                 _exit(0);
561             }
562
563             default:
564             // parent
565             {
566                 // wait till server is up
567                 sleep(1);
568             }
569         }
570
571         // there is no valid server
572
573         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
574
575         // result is constructed with default constructor on error-and-ignore  -> i=0
576
577         CPPUNIT_ASSERT_EQUAL(0,i);
578     }
579
580
581 };
582
583 CPPUNIT_TEST_SUITE_REGISTRATION(test_wrapper_noserver);