Changed one more homepage link
[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                 try
261                 {
262                     int i=0;
263                     while(i < 10 && !kill_server)
264                     {
265                         close_server=false;
266
267                         socket_server ss("./socket");
268                         group_command_server<cmd_group_x> cs(ss);
269                         ss.set_logging(&logstream,debug);
270
271                         // max 10 sec
272                         for (; !close_server && !kill_server && i < 10; i++)
273                             cs.handle(1000000);
274                     }
275                 } catch(...)
276                 {
277                     std::cerr << "exception in child. ignoring\n";
278                 }
279
280                 // don't call atexit and stuff
281                 _exit(0);
282             }
283
284             default:
285             // parent
286             {
287                 // wait till server is up
288                 sleep(1);
289
290             }
291         }
292     }
293
294     void tearDown()
295     {
296         // make sure the server-child is dead before the next test runs
297         kill(child_pid,SIGKILL);
298         sleep(1);
299     }
300
301     void no_init_exception()
302     {
303         CPPUNIT_ASSERT_THROW(t2n_exec(&cmd_group_x_client::serverfunc)(1),std::logic_error);
304     }
305
306     void simple_wrap()
307     {
308         wraptype::set_connection(auto_ptr<ConnectionWrapper>
309             (new BasicSocketWrapper("./socket")));
310
311         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
312
313         CPPUNIT_ASSERT_EQUAL(2,i);
314     }
315
316     void double_use()
317     {
318         // only one connection used?
319         wraptype::set_connection(auto_ptr<ConnectionWrapper>
320             (new BasicSocketWrapper("./socket")));
321
322         t2n_exec(&cmd_group_x_client::serverfunc)(17);
323         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
324
325         // count the number of times that "new connection accepted" appears in the server log
326         string::size_type p=0;
327         int cnt=0;
328         while ((p=out.find("new connection accepted",p))++ != string::npos)
329             cnt++;
330
331         CPPUNIT_ASSERT_EQUAL(1,cnt);
332     }
333
334     void double_use_with_close()
335     {
336         wraptype::set_connection(auto_ptr<ConnectionWrapper>
337             (new BasicSocketWrapper("./socket")));
338
339         t2n_exec(&cmd_group_x_client::serverfunc)(17);
340
341         // closes the connection from the client side
342         wraptype::set_connection(auto_ptr<ConnectionWrapper>
343             (new BasicSocketWrapper("./socket")));
344
345         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
346
347         // count the number of times that "new connection accepted" appears in the server log
348         string::size_type p=0;
349         int cnt=0;
350         while ((p=out.find("new connection accepted",p))++ != string::npos)
351             cnt++;
352
353         CPPUNIT_ASSERT_EQUAL(2,cnt);
354     }
355
356     void reconnect_after_close()
357     {
358         wraptype::set_connection(auto_ptr<ConnectionWrapper>
359             (new ReconnectSocketWrapper("./socket")));
360
361         wraptype::get_connection_wrapper()->set_command_timeout_usec(3000000);
362         wraptype::get_connection_wrapper()->set_hello_timeout_usec(3000000);
363
364         // 42 closes connection on the server side
365         t2n_exec(&cmd_group_x_client::serverfunc)(42);
366
367         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
368
369         // count the number of times that "new connection accepted" appears in the server log
370         string::size_type p=0;
371         int cnt=0;
372         while ((p=out.find("new connection accepted",p))++ != string::npos)
373             cnt++;
374
375         CPPUNIT_ASSERT_EQUAL(2,cnt);
376     }
377
378     void reconnect_not_possible()
379     {
380         wraptype::set_connection(auto_ptr<ConnectionWrapper>
381             (new ReconnectSocketWrapper("./socket")));
382
383         // the server doens't like the beast
384         t2n_exec(&cmd_group_x_client::serverfunc)(666);
385
386         CPPUNIT_ASSERT_THROW(t2n_exec(&cmd_group_x_client::serverfunc)(1),t2n_communication_error);
387     }
388
389     void ignore_server_disconnect()
390     {
391         wraptype::set_connection(auto_ptr<ConnectionWrapper>
392             (new ReconnectIgnoreFailureSocketWrapper("./socket")));
393
394         // the server doens't like the beast
395         t2n_exec(&cmd_group_x_client::serverfunc)(666);
396
397         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
398
399         // result is constructed with default constructor on error-and-ignore  -> i=0
400
401         CPPUNIT_ASSERT_EQUAL(0,i);
402     }
403
404     void ignore_handler_reconnects()
405     {
406         wraptype::set_connection(auto_ptr<ConnectionWrapper>
407             (new ReconnectIgnoreFailureSocketWrapper("./socket")));
408
409         wraptype::get_connection_wrapper()->set_command_timeout_usec(3000000);
410         wraptype::get_connection_wrapper()->set_hello_timeout_usec(3000000);
411
412         // 42 closes connection on the server side
413         t2n_exec(&cmd_group_x_client::serverfunc)(42);
414
415         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
416
417         // count the number of times that "new connection accepted" appears in the server log
418         string::size_type p=0;
419         int cnt=0;
420         while ((p=out.find("new connection accepted",p))++ != string::npos)
421             cnt++;
422
423         CPPUNIT_ASSERT_EQUAL(2,cnt);
424     }
425
426 };
427
428 CPPUNIT_TEST_SUITE_REGISTRATION(test_wrapper);
429
430
431 class test_wrapper_noserver : public TestFixture
432 {
433     CPPUNIT_TEST_SUITE(test_wrapper_noserver);
434
435     CPPUNIT_TEST(ignore_noserver);
436     CPPUNIT_TEST(ignore_finds_lateserver);
437     CPPUNIT_TEST(ignore_wrongserver);
438
439     CPPUNIT_TEST_SUITE_END();
440
441     public:
442
443     pid_t child_pid;
444
445     void setUp()
446     {
447         child_pid=0;
448     }
449
450     void tearDown()
451     {
452         // make sure the server-child is dead before the next test runs
453         if (child_pid != 0)
454         {
455             kill(child_pid,SIGKILL);
456             sleep(1);
457         }
458     }
459
460     void ignore_noserver()
461     {
462         wraptype::set_connection(auto_ptr<ConnectionWrapper>
463             (new ReconnectIgnoreFailureSocketWrapper("./socket")));
464
465         // wraptype::get_connection_wrapper()->set_logging(&cerr,debug);
466
467         // there is no server
468
469         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
470
471         // result is constructed with default constructor on error-and-ignore  -> i=0
472
473         CPPUNIT_ASSERT_EQUAL(0,i);
474     }
475
476     void ignore_finds_lateserver()
477     {
478         wraptype::set_connection(auto_ptr<ConnectionWrapper>
479             (new ReconnectIgnoreFailureSocketWrapper("./socket")));
480
481         // there is no server
482         t2n_exec(&cmd_group_x_client::serverfunc)(1);
483
484         // launch a server
485         close_server=false;
486         kill_server=false;
487
488         switch(child_pid=fork())
489         {
490             case -1:
491             {
492                 CPPUNIT_FAIL("fork error");
493                 break;
494             }
495             case 0:
496             // child
497             {
498                 try
499                 {
500                     int i=0;
501                     while(i < 10 && !kill_server)
502                     {
503                         close_server=false;
504
505                         socket_server ss("./socket");
506                         group_command_server<cmd_group_x> cs(ss);
507                         ss.set_logging(&logstream,debug);
508
509                         // max 10 sec
510                         for (; !close_server && !kill_server && i < 10; i++)
511                             cs.handle(1000000);
512                     }
513                 } catch(...)
514                 {
515                     std::cerr << "exception in child. ignoring\n";
516                 }
517
518                 // don't call atexit and stuff
519                 _exit(0);
520             }
521
522             default:
523             // parent
524             {
525                 // wait till server is up
526                 sleep(1);
527             }
528         }
529
530         // server should be active
531         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
532
533         CPPUNIT_ASSERT_EQUAL(2,i);
534     }
535
536     void send_hello(string hello_string, socket_server* ss, int conn_id)
537     {
538         server_connection *sc=ss->get_connection(conn_id);
539         sc->write(hello_string);
540     }
541
542     void ignore_wrongserver()
543     {
544         wraptype::set_connection(auto_ptr<ConnectionWrapper>
545             (new ReconnectIgnoreFailureSocketWrapper("./socket")));
546
547         // launch a server
548
549         switch(child_pid=fork())
550         {
551             case -1:
552             {
553                 CPPUNIT_FAIL("fork error");
554                 break;
555             }
556             case 0:
557             // child
558             {
559                 try
560                 {
561                     socket_server ss("./socket");
562
563                     // server sends garbage
564
565                     ostringstream hello;
566                     hello << "XYZ 123";
567
568                     ss.add_callback(new_connection,bind(&test_wrapper_noserver::send_hello, boost::ref(*this), hello.str(),&ss, _1));
569
570                     // max 10 sec
571                     for (int i=0; i < 10; i++)
572                         ss.fill_buffer(1000000);
573                 } catch(...)
574                 {
575                     std::cerr << "exception in child. ignoring\n";
576                 }
577
578                 // don't call atexit and stuff
579                 _exit(0);
580             }
581
582             default:
583             // parent
584             {
585                 // wait till server is up
586                 sleep(1);
587             }
588         }
589
590         // there is no valid server
591
592         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
593
594         // result is constructed with default constructor on error-and-ignore  -> i=0
595
596         CPPUNIT_ASSERT_EQUAL(0,i);
597     }
598
599
600 };
601
602 CPPUNIT_TEST_SUITE_REGISTRATION(test_wrapper_noserver);