client_wrapper.hxx, socket_wrapper.hxx: reorder member initialization order
[libt2n] / test / wrapper.cpp
1 /*
2 Copyright (C) 2004 by Intra2net AG
3
4 The software in this package is distributed under the GNU General
5 Public License version 2 (with a special exception described below).
6
7 A copy of GNU General Public License (GPL) is included in this distribution,
8 in the file COPYING.GPL.
9
10 As a special exception, if other files instantiate templates or use macros
11 or inline functions from this file, or you compile this file and link it
12 with other works to produce a work based on this file, this file
13 does not by itself cause the resulting work to be covered
14 by the GNU General Public License.
15
16 However the source code for this file must still be made available
17 in accordance with section (3) of the GNU General Public License.
18
19 This exception does not invalidate any other reasons why a work based
20 on 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 #define BOOST_TEST_DYN_LINK
34 #include <boost/test/unit_test.hpp>
35
36 #include <boost/archive/binary_oarchive.hpp>
37 #include <boost/archive/binary_iarchive.hpp>
38 #include <boost/archive/xml_oarchive.hpp>
39 #include <boost/archive/xml_iarchive.hpp>
40 #include <boost/serialization/serialization.hpp>
41 #include <boost/serialization/export.hpp>
42
43 #include <container.hxx>
44 #include <socket_client.hxx>
45 #include <socket_server.hxx>
46 #include <command_client.hxx>
47 #include <command_server.hxx>
48 #include <client_wrapper.hxx>
49 #include <socket_wrapper.hxx>
50
51 #include "test_fixtures.hxx"
52
53 #include <config.h>
54
55 using namespace std;
56 using namespace libt2n;
57
58 // the server part
59
60 stringstream logstream;
61 bool close_server=false;
62 bool kill_server=false;
63
64 int serverfunc(int i)
65 {
66     // magic commands
67     if (i==42)
68         close_server=true;
69     if (i==666)
70         kill_server=true;
71
72     return i+1;
73 }
74
75 std::string getserverlog(void)
76 {
77     return logstream.str();
78 }
79
80 class serverfunc_res : public libt2n::result
81 {
82     private:
83         int res;
84
85         friend class boost::serialization::access;
86         template<class Archive>
87         void serialize(Archive & ar, const unsigned int version)
88         {
89             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result);
90             ar & BOOST_SERIALIZATION_NVP(res);
91         }
92
93     public:
94         serverfunc_res()
95             { }
96
97         serverfunc_res(int i)
98         {
99             res=i;
100         }
101
102         int get_data()
103         {
104             return res;
105         }
106 };
107
108 class getserverlog_res : public libt2n::result
109 {
110     private:
111         std::string res;
112
113         friend class boost::serialization::access;
114         template<class Archive>
115         void serialize(Archive & ar, const unsigned int version)
116         {
117             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result);
118             ar & BOOST_SERIALIZATION_NVP(res);
119         }
120
121     public:
122         getserverlog_res()
123             { }
124
125         getserverlog_res(std::string s)
126         {
127             res=s;
128         }
129
130         std::string get_data()
131         {
132             return res;
133         }
134 };
135
136 class cmd_group_x : public command
137 {
138     private:
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(libt2n::command);
144         }
145 };
146
147 class serverfunc_cmd : public cmd_group_x
148 {
149     private:
150         int param;
151
152         friend class boost::serialization::access;
153         template<class Archive>
154         void serialize(Archive & ar, const unsigned int version)
155         {
156             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(cmd_group_x);
157             ar & BOOST_SERIALIZATION_NVP(param);
158         }
159
160     public:
161         serverfunc_cmd()
162             { }
163
164         serverfunc_cmd(int i)
165         {
166             param=i;
167         }
168
169         libt2n::result* operator()()
170         {
171             return new serverfunc_res(serverfunc(param));
172         }
173 };
174
175 class getserverlog_cmd : public cmd_group_x
176 {
177     private:
178         friend class boost::serialization::access;
179         template<class Archive>
180         void serialize(Archive & ar, const unsigned int version)
181         {
182             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(cmd_group_x);
183         }
184
185     public:
186         getserverlog_cmd()
187             { }
188
189         libt2n::result* operator()()
190         {
191             return new getserverlog_res(getserverlog());
192         }
193 };
194
195 BOOST_CLASS_EXPORT(serverfunc_res)
196 BOOST_CLASS_EXPORT(getserverlog_res)
197 BOOST_CLASS_EXPORT(cmd_group_x)
198 BOOST_CLASS_EXPORT(serverfunc_cmd)
199 BOOST_CLASS_EXPORT(getserverlog_cmd)
200
201 class cmd_group_x_client : public command_client
202 {
203     public:
204         cmd_group_x_client(libt2n::client_connection *_c,
205          long long _command_timeout_usec=command_timeout_usec_default,
206          long long _hello_timeout_usec=hello_timeout_usec_default)
207          : libt2n::command_client(_c,_command_timeout_usec,_hello_timeout_usec)
208         {}
209
210     int serverfunc(int i)
211     {
212         libt2n::result_container rc;
213
214         send_command(new serverfunc_cmd(i), rc);
215         serverfunc_res* res=dynamic_cast<serverfunc_res*>(rc.get_result());
216         if (!res) throw libt2n::t2n_communication_error("result object of wrong type");
217         return res->get_data();
218     }
219
220     std::string getserverlog(void)
221     {
222         libt2n::result_container rc;
223
224         send_command(new getserverlog_cmd(), rc);
225         getserverlog_res* res=dynamic_cast<getserverlog_res*>(rc.get_result());
226         if (!res) throw libt2n::t2n_communication_error("result object of wrong type");
227         return res->get_data();
228     }
229 };
230
231 typedef T2nSingletonWrapper<cmd_group_x_client> wraptype;
232
233 template<>
234 std::auto_ptr<wraptype> wraptype::SingletonObject = std::auto_ptr<wraptype>();
235
236 template<>
237 std::auto_ptr<ConnectionWrapper> wraptype::WrappedConnection = std::auto_ptr<ConnectionWrapper>();
238
239
240 class test_wrapperFixture : public KillChildOnShutdownFixture
241 {
242 public:
243     test_wrapperFixture()
244     {
245         close_server=false;
246         kill_server=false;
247
248         switch(child_pid=fork())
249         {
250             case -1:
251             {
252                 BOOST_FAIL("fork error");
253                 break;
254             }
255             case 0:
256             // child
257             {
258                 try
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                 } catch(...)
274                 {
275                     std::cerr << "exception in child. ignoring\n";
276                 }
277
278                 // don't call atexit and stuff
279                 _exit(0);
280             }
281
282             default:
283             // parent
284             {
285                 // don't kill us on broken pipe
286                 signal(SIGPIPE, SIG_IGN);
287
288                 // wait till server is up
289                 sleep(1);
290             }
291         }
292     }
293
294     ~test_wrapperFixture()
295     {
296     }
297 };
298
299 BOOST_FIXTURE_TEST_SUITE(test_wrapper, test_wrapperFixture)
300
301 BOOST_AUTO_TEST_CASE(no_init_exception) // must be called first!
302 {
303     BOOST_REQUIRE_THROW(t2n_exec(&cmd_group_x_client::serverfunc)(1),std::logic_error);
304 }
305
306 BOOST_AUTO_TEST_CASE(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     BOOST_CHECK_EQUAL(2,i);
314 }
315
316 BOOST_AUTO_TEST_CASE(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     BOOST_CHECK_EQUAL(1,cnt);
332 }
333
334 BOOST_AUTO_TEST_CASE(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     BOOST_CHECK_EQUAL(2,cnt);
354 }
355
356 BOOST_AUTO_TEST_CASE(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     BOOST_CHECK_EQUAL(2,cnt);
376 }
377
378 BOOST_AUTO_TEST_CASE(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     BOOST_REQUIRE_THROW(t2n_exec(&cmd_group_x_client::serverfunc)(1),t2n_communication_error);
387 }
388
389 BOOST_AUTO_TEST_CASE(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     BOOST_CHECK_EQUAL(0,i);
402 }
403
404 BOOST_AUTO_TEST_CASE(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     BOOST_CHECK_EQUAL(2,cnt);
424 }
425
426 BOOST_AUTO_TEST_SUITE_END()
427
428 BOOST_FIXTURE_TEST_SUITE(test_wrapper_noserver, KillChildOnShutdownFixture)
429
430 BOOST_AUTO_TEST_CASE(ignore_noserver)
431 {
432     wraptype::set_connection(auto_ptr<ConnectionWrapper>
433         (new ReconnectIgnoreFailureSocketWrapper("./socket")));
434
435     // wraptype::get_connection_wrapper()->set_logging(&cerr,debug);
436
437     // there is no server
438
439     int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
440
441     // result is constructed with default constructor on error-and-ignore  -> i=0
442
443     BOOST_CHECK_EQUAL(0,i);
444 }
445
446 BOOST_AUTO_TEST_CASE(ignore_finds_lateserver)
447 {
448     wraptype::set_connection(auto_ptr<ConnectionWrapper>
449         (new ReconnectIgnoreFailureSocketWrapper("./socket")));
450
451     // there is no server
452     t2n_exec(&cmd_group_x_client::serverfunc)(1);
453
454     // launch a server
455     close_server=false;
456     kill_server=false;
457
458     switch(child_pid=fork())
459     {
460         case -1:
461         {
462             BOOST_FAIL("fork error");
463             break;
464         }
465         case 0:
466         // child
467         {
468             try
469             {
470                 int i=0;
471                 while(i < 10 && !kill_server)
472                 {
473                     close_server=false;
474
475                     socket_server ss("./socket");
476                     group_command_server<cmd_group_x> cs(ss);
477                     ss.set_logging(&logstream,debug);
478
479                     // max 10 sec
480                     for (; !close_server && !kill_server && i < 10; i++)
481                         cs.handle(1000000);
482                 }
483             } catch(...)
484             {
485                 std::cerr << "exception in child. ignoring\n";
486             }
487
488             // don't call atexit and stuff
489             _exit(0);
490         }
491
492         default:
493         // parent
494         {
495             // wait till server is up
496             sleep(1);
497         }
498     }
499
500     // server should be active
501     int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
502
503     BOOST_CHECK_EQUAL(2,i);
504 }
505
506 BOOST_AUTO_TEST_CASE(ignore_wrongserver)
507 {
508     wraptype::set_connection(auto_ptr<ConnectionWrapper>
509         (new ReconnectIgnoreFailureSocketWrapper("./socket")));
510
511     // launch a server
512
513     switch(child_pid=fork())
514     {
515         case -1:
516         {
517             BOOST_FAIL("fork error");
518             break;
519         }
520         case 0:
521         // child
522         {
523             try
524             {
525                 socket_server ss("./socket");
526
527                 // server sends garbage
528
529                 ostringstream hello;
530                 hello << "XYZ 123";
531
532                 ss.add_callback(new_connection,bind(&test_wrapper_noserver::ignore_wrongserver::send_hello, boost::ref(*this), hello.str(),&ss, _1));
533
534                 // max 10 sec
535                 for (int i=0; i < 10; i++)
536                     ss.fill_buffer(1000000);
537             } catch(...)
538             {
539                 std::cerr << "exception in child. ignoring\n";
540             }
541
542             // don't call atexit and stuff
543             _exit(0);
544         }
545
546         default:
547         // parent
548         {
549             // wait till server is up
550             sleep(1);
551         }
552     }
553
554     // there is no valid server
555
556     int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
557
558     // result is constructed with default constructor on error-and-ignore  -> i=0
559
560     BOOST_CHECK_EQUAL(0,i);
561 }
562
563 BOOST_AUTO_TEST_SUITE_END()