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