Migrate from cppunit to Boost.test
[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#ifdef HAVE_CONFIG_H
54#include <config.h>
55#endif
56
57using namespace std;
58using namespace libt2n;
59
60// the server part
61
62stringstream logstream;
63bool close_server=false;
64bool kill_server=false;
65
66int 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
77std::string getserverlog(void)
78{
79 return logstream.str();
80}
81
82class 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
110class 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
138class 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
149class 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
177class 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
197BOOST_CLASS_EXPORT(serverfunc_res)
198BOOST_CLASS_EXPORT(getserverlog_res)
199BOOST_CLASS_EXPORT(cmd_group_x)
200BOOST_CLASS_EXPORT(serverfunc_cmd)
201BOOST_CLASS_EXPORT(getserverlog_cmd)
202
203class 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
233typedef T2nSingletonWrapper<cmd_group_x_client> wraptype;
234
235template<>
236std::auto_ptr<wraptype> wraptype::SingletonObject = std::auto_ptr<wraptype>();
237
238template<>
239std::auto_ptr<ConnectionWrapper> wraptype::WrappedConnection = std::auto_ptr<ConnectionWrapper>();
240
241
242class test_wrapperFixture : public KillChildOnShutdownFixture
243{
244public:
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
301BOOST_FIXTURE_TEST_SUITE(test_wrapper, test_wrapperFixture)
302
303BOOST_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
308BOOST_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
318BOOST_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
336BOOST_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
358BOOST_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
380BOOST_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
391BOOST_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
406BOOST_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}
427BOOST_AUTO_TEST_SUITE_END()
428
429
430
431class test_wrapper_noserverFixture : public KillChildOnShutdownFixture
432{
433protected:
434 void send_hello(string hello_string, socket_server* ss, int conn_id)
435 {
436 server_connection *sc=ss->get_connection(conn_id);
437 sc->write(hello_string);
438 }
439
440public:
441 test_wrapper_noserverFixture()
442 {
443 }
444
445 ~test_wrapper_noserverFixture()
446 {
447 }
448};
449
450BOOST_FIXTURE_TEST_SUITE(test_wrapper_noserver, test_wrapper_noserverFixture)
451
452BOOST_AUTO_TEST_CASE(ignore_noserver)
453{
454 wraptype::set_connection(auto_ptr<ConnectionWrapper>
455 (new ReconnectIgnoreFailureSocketWrapper("./socket")));
456
457 // wraptype::get_connection_wrapper()->set_logging(&cerr,debug);
458
459 // there is no server
460
461 int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
462
463 // result is constructed with default constructor on error-and-ignore -> i=0
464
465 BOOST_CHECK_EQUAL(0,i);
466}
467
468BOOST_AUTO_TEST_CASE(ignore_finds_lateserver)
469{
470 wraptype::set_connection(auto_ptr<ConnectionWrapper>
471 (new ReconnectIgnoreFailureSocketWrapper("./socket")));
472
473 // there is no server
474 t2n_exec(&cmd_group_x_client::serverfunc)(1);
475
476 // launch a server
477 close_server=false;
478 kill_server=false;
479
480 switch(child_pid=fork())
481 {
482 case -1:
483 {
484 BOOST_FAIL("fork error");
485 break;
486 }
487 case 0:
488 // child
489 {
490 try
491 {
492 int i=0;
493 while(i < 10 && !kill_server)
494 {
495 close_server=false;
496
497 socket_server ss("./socket");
498 group_command_server<cmd_group_x> cs(ss);
499 ss.set_logging(&logstream,debug);
500
501 // max 10 sec
502 for (; !close_server && !kill_server && i < 10; i++)
503 cs.handle(1000000);
504 }
505 } catch(...)
506 {
507 std::cerr << "exception in child. ignoring\n";
508 }
509
510 // don't call atexit and stuff
511 _exit(0);
512 }
513
514 default:
515 // parent
516 {
517 // wait till server is up
518 sleep(1);
519 }
520 }
521
522 // server should be active
523 int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
524
525 BOOST_CHECK_EQUAL(2,i);
526}
527
528BOOST_AUTO_TEST_CASE(ignore_wrongserver)
529{
530 wraptype::set_connection(auto_ptr<ConnectionWrapper>
531 (new ReconnectIgnoreFailureSocketWrapper("./socket")));
532
533 // launch a server
534
535 switch(child_pid=fork())
536 {
537 case -1:
538 {
539 BOOST_FAIL("fork error");
540 break;
541 }
542 case 0:
543 // child
544 {
545 try
546 {
547 socket_server ss("./socket");
548
549 // server sends garbage
550
551 ostringstream hello;
552 hello << "XYZ 123";
553
554 ss.add_callback(new_connection,bind(&test_wrapper_noserver::ignore_wrongserver::send_hello, boost::ref(*this), hello.str(),&ss, _1));
555
556 // max 10 sec
557 for (int i=0; i < 10; i++)
558 ss.fill_buffer(1000000);
559 } catch(...)
560 {
561 std::cerr << "exception in child. ignoring\n";
562 }
563
564 // don't call atexit and stuff
565 _exit(0);
566 }
567
568 default:
569 // parent
570 {
571 // wait till server is up
572 sleep(1);
573 }
574 }
575
576 // there is no valid server
577
578 int i=t2n_exec(&cmd_group_x_client::serverfunc)(1);
579
580 // result is constructed with default constructor on error-and-ignore -> i=0
581
582 BOOST_CHECK_EQUAL(0,i);
583}
584
585BOOST_AUTO_TEST_SUITE_END()