Increase version
[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 #ifdef HAVE_CONFIG_H
54 #include <config.h>
55 #endif
56
57 using namespace std;
58 using namespace libt2n;
59
60 // the server part
61
62 stringstream logstream;
63 bool close_server=false;
64 bool kill_server=false;
65
66 int serverfunc(int i)
67 {
68     // magic commands
69     if (i==42)
70         close_server=true;
71     if (i==666)
72         kill_server=true;
73
74     return i+1;
75 }
76
77 std::string getserverlog(void)
78 {
79     return logstream.str();
80 }
81
82 class serverfunc_res : public libt2n::result
83 {
84     private:
85         int res;
86
87         friend class boost::serialization::access;
88         template<class Archive>
89         void serialize(Archive & ar, const unsigned int version)
90         {
91             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result);
92             ar & BOOST_SERIALIZATION_NVP(res);
93         }
94
95     public:
96         serverfunc_res()
97             { }
98
99         serverfunc_res(int i)
100         {
101             res=i;
102         }
103
104         int get_data()
105         {
106             return res;
107         }
108 };
109
110 class getserverlog_res : public libt2n::result
111 {
112     private:
113         std::string res;
114
115         friend class boost::serialization::access;
116         template<class Archive>
117         void serialize(Archive & ar, const unsigned int version)
118         {
119             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result);
120             ar & BOOST_SERIALIZATION_NVP(res);
121         }
122
123     public:
124         getserverlog_res()
125             { }
126
127         getserverlog_res(std::string s)
128         {
129             res=s;
130         }
131
132         std::string get_data()
133         {
134             return res;
135         }
136 };
137
138 class cmd_group_x : public command
139 {
140     private:
141         friend class boost::serialization::access;
142         template<class Archive>
143         void serialize(Archive & ar, const unsigned int version)
144         {
145             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::command);
146         }
147 };
148
149 class serverfunc_cmd : public cmd_group_x
150 {
151     private:
152         int param;
153
154         friend class boost::serialization::access;
155         template<class Archive>
156         void serialize(Archive & ar, const unsigned int version)
157         {
158             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(cmd_group_x);
159             ar & BOOST_SERIALIZATION_NVP(param);
160         }
161
162     public:
163         serverfunc_cmd()
164             { }
165
166         serverfunc_cmd(int i)
167         {
168             param=i;
169         }
170
171         libt2n::result* operator()()
172         {
173             return new serverfunc_res(serverfunc(param));
174         }
175 };
176
177 class getserverlog_cmd : public cmd_group_x
178 {
179     private:
180         friend class boost::serialization::access;
181         template<class Archive>
182         void serialize(Archive & ar, const unsigned int version)
183         {
184             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(cmd_group_x);
185         }
186
187     public:
188         getserverlog_cmd()
189             { }
190
191         libt2n::result* operator()()
192         {
193             return new getserverlog_res(getserverlog());
194         }
195 };
196
197 BOOST_CLASS_EXPORT(serverfunc_res)
198 BOOST_CLASS_EXPORT(getserverlog_res)
199 BOOST_CLASS_EXPORT(cmd_group_x)
200 BOOST_CLASS_EXPORT(serverfunc_cmd)
201 BOOST_CLASS_EXPORT(getserverlog_cmd)
202
203 class cmd_group_x_client : public command_client
204 {
205     public:
206         cmd_group_x_client(libt2n::client_connection *_c,
207          long long _command_timeout_usec=command_timeout_usec_default,
208          long long _hello_timeout_usec=hello_timeout_usec_default)
209          : libt2n::command_client(_c,_command_timeout_usec,_hello_timeout_usec)
210         {}
211
212     int serverfunc(int i)
213     {
214         libt2n::result_container rc;
215
216         send_command(new serverfunc_cmd(i), rc);
217         serverfunc_res* res=dynamic_cast<serverfunc_res*>(rc.get_result());
218         if (!res) throw libt2n::t2n_communication_error("result object of wrong type");
219         return res->get_data();
220     }
221
222     std::string getserverlog(void)
223     {
224         libt2n::result_container rc;
225
226         send_command(new getserverlog_cmd(), rc);
227         getserverlog_res* res=dynamic_cast<getserverlog_res*>(rc.get_result());
228         if (!res) throw libt2n::t2n_communication_error("result object of wrong type");
229         return res->get_data();
230     }
231 };
232
233 typedef T2nSingletonWrapper<cmd_group_x_client> wraptype;
234
235 template<>
236 std::auto_ptr<wraptype> wraptype::SingletonObject = std::auto_ptr<wraptype>();
237
238 template<>
239 std::auto_ptr<ConnectionWrapper> wraptype::WrappedConnection = std::auto_ptr<ConnectionWrapper>();
240
241
242 class test_wrapperFixture : public KillChildOnShutdownFixture
243 {
244 public:
245     test_wrapperFixture()
246     {
247         close_server=false;
248         kill_server=false;
249
250         switch(child_pid=fork())
251         {
252             case -1:
253             {
254                 BOOST_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                 // don't kill us on broken pipe
288                 signal(SIGPIPE, SIG_IGN);
289
290                 // wait till server is up
291                 sleep(1);
292             }
293         }
294     }
295
296     ~test_wrapperFixture()
297     {
298     }
299 };
300
301 BOOST_FIXTURE_TEST_SUITE(test_wrapper, test_wrapperFixture)
302
303 BOOST_AUTO_TEST_CASE(no_init_exception) // must be called first!
304 {
305     BOOST_REQUIRE_THROW(t2n_exec(&cmd_group_x_client::serverfunc)(1),std::logic_error);
306 }
307
308 BOOST_AUTO_TEST_CASE(simple_wrap)
309 {
310     wraptype::set_connection(auto_ptr<ConnectionWrapper>
311         (new BasicSocketWrapper("./socket")));
312
313     int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
314
315     BOOST_CHECK_EQUAL(2,i);
316 }
317
318 BOOST_AUTO_TEST_CASE(double_use)
319 {
320     // only one connection used?
321     wraptype::set_connection(auto_ptr<ConnectionWrapper>
322         (new BasicSocketWrapper("./socket")));
323
324     t2n_exec(&cmd_group_x_client::serverfunc)(17);
325     string out=t2n_exec(&cmd_group_x_client::getserverlog)();
326
327     // count the number of times that "new connection accepted" appears in the server log
328     string::size_type p=0;
329     int cnt=0;
330     while ((p=out.find("new connection accepted",p))++ != string::npos)
331         cnt++;
332
333     BOOST_CHECK_EQUAL(1,cnt);
334 }
335
336 BOOST_AUTO_TEST_CASE(double_use_with_close)
337 {
338     wraptype::set_connection(auto_ptr<ConnectionWrapper>
339         (new BasicSocketWrapper("./socket")));
340
341     t2n_exec(&cmd_group_x_client::serverfunc)(17);
342
343     // closes the connection from the client side
344     wraptype::set_connection(auto_ptr<ConnectionWrapper>
345         (new BasicSocketWrapper("./socket")));
346
347     string out=t2n_exec(&cmd_group_x_client::getserverlog)();
348
349     // count the number of times that "new connection accepted" appears in the server log
350     string::size_type p=0;
351     int cnt=0;
352     while ((p=out.find("new connection accepted",p))++ != string::npos)
353         cnt++;
354
355     BOOST_CHECK_EQUAL(2,cnt);
356 }
357
358 BOOST_AUTO_TEST_CASE(reconnect_after_close)
359 {
360     wraptype::set_connection(auto_ptr<ConnectionWrapper>
361         (new ReconnectSocketWrapper("./socket")));
362
363     wraptype::get_connection_wrapper()->set_command_timeout_usec(3000000);
364     wraptype::get_connection_wrapper()->set_hello_timeout_usec(3000000);
365
366     // 42 closes connection on the server side
367     t2n_exec(&cmd_group_x_client::serverfunc)(42);
368
369     string out=t2n_exec(&cmd_group_x_client::getserverlog)();
370
371     // count the number of times that "new connection accepted" appears in the server log
372     string::size_type p=0;
373     int cnt=0;
374     while ((p=out.find("new connection accepted",p))++ != string::npos)
375         cnt++;
376
377     BOOST_CHECK_EQUAL(2,cnt);
378 }
379
380 BOOST_AUTO_TEST_CASE(reconnect_not_possible)
381 {
382     wraptype::set_connection(auto_ptr<ConnectionWrapper>
383         (new ReconnectSocketWrapper("./socket")));
384
385     // the server doens't like the beast
386     t2n_exec(&cmd_group_x_client::serverfunc)(666);
387
388     BOOST_REQUIRE_THROW(t2n_exec(&cmd_group_x_client::serverfunc)(1),t2n_communication_error);
389 }
390
391 BOOST_AUTO_TEST_CASE(ignore_server_disconnect)
392 {
393     wraptype::set_connection(auto_ptr<ConnectionWrapper>
394         (new ReconnectIgnoreFailureSocketWrapper("./socket")));
395
396     // the server doens't like the beast
397     t2n_exec(&cmd_group_x_client::serverfunc)(666);
398
399     int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
400
401     // result is constructed with default constructor on error-and-ignore  -> i=0
402
403     BOOST_CHECK_EQUAL(0,i);
404 }
405
406 BOOST_AUTO_TEST_CASE(ignore_handler_reconnects)
407 {
408     wraptype::set_connection(auto_ptr<ConnectionWrapper>
409         (new ReconnectIgnoreFailureSocketWrapper("./socket")));
410
411     wraptype::get_connection_wrapper()->set_command_timeout_usec(3000000);
412     wraptype::get_connection_wrapper()->set_hello_timeout_usec(3000000);
413
414     // 42 closes connection on the server side
415     t2n_exec(&cmd_group_x_client::serverfunc)(42);
416
417     string out=t2n_exec(&cmd_group_x_client::getserverlog)();
418
419     // count the number of times that "new connection accepted" appears in the server log
420     string::size_type p=0;
421     int cnt=0;
422     while ((p=out.find("new connection accepted",p))++ != string::npos)
423         cnt++;
424
425     BOOST_CHECK_EQUAL(2,cnt);
426 }
427
428 BOOST_AUTO_TEST_SUITE_END()
429
430 BOOST_FIXTURE_TEST_SUITE(test_wrapper_noserver, KillChildOnShutdownFixture)
431
432 BOOST_AUTO_TEST_CASE(ignore_noserver)
433 {
434     wraptype::set_connection(auto_ptr<ConnectionWrapper>
435         (new ReconnectIgnoreFailureSocketWrapper("./socket")));
436
437     // wraptype::get_connection_wrapper()->set_logging(&cerr,debug);
438
439     // there is no server
440
441     int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
442
443     // result is constructed with default constructor on error-and-ignore  -> i=0
444
445     BOOST_CHECK_EQUAL(0,i);
446 }
447
448 BOOST_AUTO_TEST_CASE(ignore_finds_lateserver)
449 {
450     wraptype::set_connection(auto_ptr<ConnectionWrapper>
451         (new ReconnectIgnoreFailureSocketWrapper("./socket")));
452
453     // there is no server
454     t2n_exec(&cmd_group_x_client::serverfunc)(1);
455
456     // launch a server
457     close_server=false;
458     kill_server=false;
459
460     switch(child_pid=fork())
461     {
462         case -1:
463         {
464             BOOST_FAIL("fork error");
465             break;
466         }
467         case 0:
468         // child
469         {
470             try
471             {
472                 int i=0;
473                 while(i < 10 && !kill_server)
474                 {
475                     close_server=false;
476
477                     socket_server ss("./socket");
478                     group_command_server<cmd_group_x> cs(ss);
479                     ss.set_logging(&logstream,debug);
480
481                     // max 10 sec
482                     for (; !close_server && !kill_server && i < 10; i++)
483                         cs.handle(1000000);
484                 }
485             } catch(...)
486             {
487                 std::cerr << "exception in child. ignoring\n";
488             }
489
490             // don't call atexit and stuff
491             _exit(0);
492         }
493
494         default:
495         // parent
496         {
497             // wait till server is up
498             sleep(1);
499         }
500     }
501
502     // server should be active
503     int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
504
505     BOOST_CHECK_EQUAL(2,i);
506 }
507
508 BOOST_AUTO_TEST_CASE(ignore_wrongserver)
509 {
510     wraptype::set_connection(auto_ptr<ConnectionWrapper>
511         (new ReconnectIgnoreFailureSocketWrapper("./socket")));
512
513     // launch a server
514
515     switch(child_pid=fork())
516     {
517         case -1:
518         {
519             BOOST_FAIL("fork error");
520             break;
521         }
522         case 0:
523         // child
524         {
525             try
526             {
527                 socket_server ss("./socket");
528
529                 // server sends garbage
530
531                 ostringstream hello;
532                 hello << "XYZ 123";
533
534                 ss.add_callback(new_connection,bind(&test_wrapper_noserver::ignore_wrongserver::send_hello, boost::ref(*this), hello.str(),&ss, _1));
535
536                 // max 10 sec
537                 for (int i=0; i < 10; i++)
538                     ss.fill_buffer(1000000);
539             } catch(...)
540             {
541                 std::cerr << "exception in child. ignoring\n";
542             }
543
544             // don't call atexit and stuff
545             _exit(0);
546         }
547
548         default:
549         // parent
550         {
551             // wait till server is up
552             sleep(1);
553         }
554     }
555
556     // there is no valid server
557
558     int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
559
560     // result is constructed with default constructor on error-and-ignore  -> i=0
561
562     BOOST_CHECK_EQUAL(0,i);
563 }
564
565 BOOST_AUTO_TEST_SUITE_END()