c820531afd9a912469bdd81e922badbb78757d3c
[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 //  TODO: missing tests:
240 //  ignore: init, no server, ignore
241 //  ignore: init, no server, ignore, server ok, connect?
242
243     CPPUNIT_TEST_SUITE_END();
244
245     public:
246
247     pid_t child_pid;
248
249     void setUp()
250     {
251         close_server=false;
252         kill_server=false;
253
254         switch(child_pid=fork())
255         {
256             case -1:
257             {
258                 CPPUNIT_FAIL("fork error");
259                 break;
260             }
261             case 0:
262             // child
263             {
264                 int i=0;
265                 while(i < 10 && !kill_server)
266                 {
267                     close_server=false;
268
269                     socket_server ss("./socket");
270                     group_command_server<cmd_group_x> cs(ss);
271                     ss.set_logging(&logstream,debug);
272
273                     // max 10 sec
274                     for (; !close_server && !kill_server && i < 10; i++)
275                         cs.handle(1000000);
276                 }
277
278                 // don't call atexit and stuff
279                 _exit(0);
280             }
281
282             default:
283             // parent
284             {
285                 // wait till server is up
286                 sleep(1);
287
288             }
289         }
290     }
291
292     void tearDown()
293     {
294         // make sure the server-child is dead before the next test runs
295         kill(child_pid,SIGKILL);
296         sleep(1);
297     }
298
299     void no_init_exception()
300     {
301         CPPUNIT_ASSERT_THROW(t2n_exec(&cmd_group_x_client::serverfunc)(1),std::logic_error);
302     }
303
304     void simple_wrap()
305     {
306         wraptype::set_connection(auto_ptr<ConnectionWrapper>
307             (new BasicSocketWrapper("./socket")));
308
309         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
310
311         CPPUNIT_ASSERT_EQUAL(2,i);
312     }
313
314     void double_use()
315     {
316         // only one connection used?
317         wraptype::set_connection(auto_ptr<ConnectionWrapper>
318             (new BasicSocketWrapper("./socket")));
319
320         t2n_exec(&cmd_group_x_client::serverfunc)(17);
321         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
322
323         // count the number of times that "new connection accepted" appears in the server log
324         string::size_type p=0;
325         int cnt=0;
326         while ((p=out.find("new connection accepted",p))++ != string::npos)
327             cnt++;
328
329         CPPUNIT_ASSERT_EQUAL(1,cnt);
330     }
331
332     void double_use_with_close()
333     {
334         wraptype::set_connection(auto_ptr<ConnectionWrapper>
335             (new BasicSocketWrapper("./socket")));
336
337         t2n_exec(&cmd_group_x_client::serverfunc)(17);
338
339         // closes the connection from the client side
340         wraptype::set_connection(auto_ptr<ConnectionWrapper>
341             (new BasicSocketWrapper("./socket")));
342
343         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
344
345         // count the number of times that "new connection accepted" appears in the server log
346         string::size_type p=0;
347         int cnt=0;
348         while ((p=out.find("new connection accepted",p))++ != string::npos)
349             cnt++;
350
351         CPPUNIT_ASSERT_EQUAL(2,cnt);
352     }
353
354     void reconnect_after_close()
355     {
356         wraptype::set_connection(auto_ptr<ConnectionWrapper>
357             (new ReconnectSocketWrapper("./socket")));
358
359         wraptype::get_connection_wrapper()->set_command_timeout_usec(3000000);
360         wraptype::get_connection_wrapper()->set_hello_timeout_usec(3000000);
361
362         // 42 closes connection on the server side
363         t2n_exec(&cmd_group_x_client::serverfunc)(42);
364
365         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
366
367         // count the number of times that "new connection accepted" appears in the server log
368         string::size_type p=0;
369         int cnt=0;
370         while ((p=out.find("new connection accepted",p))++ != string::npos)
371             cnt++;
372
373         CPPUNIT_ASSERT_EQUAL(2,cnt);
374     }
375
376     void reconnect_not_possible()
377     {
378         wraptype::set_connection(auto_ptr<ConnectionWrapper>
379             (new ReconnectSocketWrapper("./socket")));
380
381         // the server doens't like the beast
382         t2n_exec(&cmd_group_x_client::serverfunc)(666);
383
384         CPPUNIT_ASSERT_THROW(t2n_exec(&cmd_group_x_client::serverfunc)(1),t2n_communication_error);
385     }
386
387     void ignore_server_disconnect()
388     {
389         wraptype::set_connection(auto_ptr<ConnectionWrapper>
390             (new ReconnectIgnoreFailureSocketWrapper("./socket")));
391
392         // the server doens't like the beast
393         t2n_exec(&cmd_group_x_client::serverfunc)(666);
394
395         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
396
397         // result is constructed with default constructor on error-and-ignore  -> i=0
398
399         CPPUNIT_ASSERT_EQUAL(0,i);
400     }
401
402     void ignore_handler_reconnects()
403     {
404         wraptype::set_connection(auto_ptr<ConnectionWrapper>
405             (new ReconnectIgnoreFailureSocketWrapper("./socket")));
406
407         wraptype::get_connection_wrapper()->set_command_timeout_usec(3000000);
408         wraptype::get_connection_wrapper()->set_hello_timeout_usec(3000000);
409
410         // 42 closes connection on the server side
411         t2n_exec(&cmd_group_x_client::serverfunc)(42);
412
413         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
414
415         // count the number of times that "new connection accepted" appears in the server log
416         string::size_type p=0;
417         int cnt=0;
418         while ((p=out.find("new connection accepted",p))++ != string::npos)
419             cnt++;
420
421         CPPUNIT_ASSERT_EQUAL(2,cnt);
422     }
423
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
438     CPPUNIT_TEST_SUITE_END();
439
440     public:
441
442     pid_t child_pid;
443
444     void setUp()
445     {  }
446
447     void tearDown()
448     {  }
449
450     void ignore_noserver()
451     {
452         wraptype::set_connection(auto_ptr<ConnectionWrapper>
453             (new ReconnectIgnoreFailureSocketWrapper("./socket")));
454
455         // wraptype::get_connection_wrapper()->set_logging(&cerr,debug);
456
457         // there is no server
458
459         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
460
461         // result is constructed with default constructor on error-and-ignore  -> i=0
462
463         CPPUNIT_ASSERT_EQUAL(0,i);
464     }
465
466     void ignore_finds_lateserver()
467     {
468         wraptype::set_connection(auto_ptr<ConnectionWrapper>
469             (new ReconnectIgnoreFailureSocketWrapper("./socket")));
470
471         // there is no server
472         t2n_exec(&cmd_group_x_client::serverfunc)(1);
473
474         // launch a server
475
476         close_server=false;
477         kill_server=false;
478
479         switch(child_pid=fork())
480         {
481             case -1:
482             {
483                 CPPUNIT_FAIL("fork error");
484                 break;
485             }
486             case 0:
487             // child
488             {
489                 int i=0;
490                 while(i < 10 && !kill_server)
491                 {
492                     close_server=false;
493
494                     socket_server ss("./socket");
495                     group_command_server<cmd_group_x> cs(ss);
496                     ss.set_logging(&logstream,debug);
497
498                     // max 10 sec
499                     for (; !close_server && !kill_server && i < 10; i++)
500                         cs.handle(1000000);
501                 }
502
503                 // don't call atexit and stuff
504                 _exit(0);
505             }
506
507             default:
508             // parent
509             {
510                 // wait till server is up
511                 sleep(1);
512             }
513         }
514
515         // server should be active
516         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
517
518         CPPUNIT_ASSERT_EQUAL(2,i);
519
520         // make sure the server-child is dead before the next test runs
521         kill(child_pid,SIGKILL);
522         sleep(1);
523     }
524
525
526 };
527
528 CPPUNIT_TEST_SUITE_REGISTRATION(test_wrapper_noserver);