libt2n: (gerd) bugfixes, better logging, unit tests for wrapper, ignore handler still...
[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
237 //  TODO: missing tests:
238 //  ignore: init, use, server die, ignore
239 //  ignore: init, no server, ignore
240 //  ignore: init, no server, ignore, server ok, connect?
241
242     CPPUNIT_TEST_SUITE_END();
243
244     public:
245
246     void setUp()
247     {
248         pid_t pid;
249
250         switch(pid=fork())
251         {
252             case -1:
253             {
254                 CPPUNIT_FAIL("fork error");
255                 break;
256             }
257             case 0:
258             // child
259             {
260                 int i=0;
261                 while(i < 15 && !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 < 15; i++)
271                         cs.handle(1000000);
272                 }
273
274                 // don't call atexit and stuff
275                 _exit(0);
276             }
277
278             default:
279             // parent
280             {
281                 // wait till server is up
282                 sleep(1);
283
284             }
285         }
286     }
287
288     void tearDown()
289     { }
290
291     void simple_wrap()
292     {
293         wraptype::set_connection(auto_ptr<ConnectionWrapper>
294             (new BasicSocketWrapper("./socket")));
295
296         int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
297
298         CPPUNIT_ASSERT_EQUAL(2,i);
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 double_use()
307     {
308         // only one connection used?
309         wraptype::set_connection(auto_ptr<ConnectionWrapper>
310             (new BasicSocketWrapper("./socket")));
311
312         t2n_exec(&cmd_group_x_client::serverfunc)(17);
313         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
314
315         // count the number of times that "new connection accepted" appears in the server log
316         string::size_type p=0;
317         int cnt=0;
318         while ((p=out.find("new connection accepted",p))++ != string::npos)
319             cnt++;
320
321         CPPUNIT_ASSERT_EQUAL(1,cnt);
322     }
323
324     void double_use_with_close()
325     {
326         wraptype::set_connection(auto_ptr<ConnectionWrapper>
327             (new BasicSocketWrapper("./socket")));
328
329         t2n_exec(&cmd_group_x_client::serverfunc)(17);
330
331         // closes the connection from the client side
332         wraptype::set_connection(auto_ptr<ConnectionWrapper>
333             (new BasicSocketWrapper("./socket")));
334
335         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
336
337         // count the number of times that "new connection accepted" appears in the server log
338         string::size_type p=0;
339         int cnt=0;
340         while ((p=out.find("new connection accepted",p))++ != string::npos)
341             cnt++;
342
343         CPPUNIT_ASSERT_EQUAL(2,cnt);
344     }
345
346     void reconnect_after_close()
347     {
348         wraptype::set_connection(auto_ptr<ConnectionWrapper>
349             (new ReconnectSocketWrapper("./socket")));
350
351         wraptype::get_connection_wrapper()->set_command_timeout_usec(3000000);
352         wraptype::get_connection_wrapper()->set_hello_timeout_usec(3000000);
353
354         // 42 closes connection on the server side
355         t2n_exec(&cmd_group_x_client::serverfunc)(42);
356
357         string out=t2n_exec(&cmd_group_x_client::getserverlog)();
358
359         // count the number of times that "new connection accepted" appears in the server log
360         string::size_type p=0;
361         int cnt=0;
362         while ((p=out.find("new connection accepted",p))++ != string::npos)
363             cnt++;
364
365         CPPUNIT_ASSERT_EQUAL(2,cnt);
366     }
367
368     void reconnect_not_possible()
369     {
370         wraptype::set_connection(auto_ptr<ConnectionWrapper>
371             (new ReconnectSocketWrapper("./socket")));
372
373         // the server doens't like the beast
374         t2n_exec(&cmd_group_x_client::serverfunc)(666);
375
376         CPPUNIT_ASSERT_THROW(t2n_exec(&cmd_group_x_client::serverfunc)(1),t2n_communication_error);
377     }
378
379
380 };
381
382 CPPUNIT_TEST_SUITE_REGISTRATION(test_wrapper);