Prepare libt2n 0.7 release
[libt2n] / test / wrapper.cpp
... / ...
CommitLineData
1/*
2Copyright (C) 2004 by Intra2net AG
3
4The software in this package is distributed under the GNU General
5Public License version 2 (with a special exception described below).
6
7A copy of GNU General Public License (GPL) is included in this distribution,
8in the file COPYING.GPL.
9
10As a special exception, if other files instantiate templates or use macros
11or inline functions from this file, or you compile this file and link it
12with other works to produce a work based on this file, this file
13does not by itself cause the resulting work to be covered
14by the GNU General Public License.
15
16However the source code for this file must still be made available
17in accordance with section (3) of the GNU General Public License.
18
19This exception does not invalidate any other reasons why a work based
20on 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
55using namespace std;
56using namespace libt2n;
57
58// the server part
59
60stringstream logstream;
61bool close_server=false;
62bool kill_server=false;
63
64int 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
75std::string getserverlog(void)
76{
77 return logstream.str();
78}
79
80class 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
108class 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
136class 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
147class 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
175class 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
195BOOST_CLASS_EXPORT(serverfunc_res)
196BOOST_CLASS_EXPORT(getserverlog_res)
197BOOST_CLASS_EXPORT(cmd_group_x)
198BOOST_CLASS_EXPORT(serverfunc_cmd)
199BOOST_CLASS_EXPORT(getserverlog_cmd)
200
201class 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
231typedef T2nSingletonWrapper<cmd_group_x_client> wraptype;
232
233template<>
234std::auto_ptr<wraptype> wraptype::SingletonObject = std::auto_ptr<wraptype>();
235
236template<>
237std::auto_ptr<ConnectionWrapper> wraptype::WrappedConnection = std::auto_ptr<ConnectionWrapper>();
238
239
240class test_wrapperFixture : public KillChildOnShutdownFixture
241{
242public:
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
299BOOST_FIXTURE_TEST_SUITE(test_wrapper, test_wrapperFixture)
300
301BOOST_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
306BOOST_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
316BOOST_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
334BOOST_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
356BOOST_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
378BOOST_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
389BOOST_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
404BOOST_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
426BOOST_AUTO_TEST_SUITE_END()
427
428BOOST_FIXTURE_TEST_SUITE(test_wrapper_noserver, KillChildOnShutdownFixture)
429
430BOOST_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
446BOOST_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
506BOOST_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
563BOOST_AUTO_TEST_SUITE_END()