Remove code duplication in test fixtures
[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}
427
428BOOST_AUTO_TEST_SUITE_END()
429
430BOOST_FIXTURE_TEST_SUITE(test_wrapper_noserver, KillChildOnShutdownFixture)
431
432BOOST_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
448BOOST_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
508BOOST_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
565BOOST_AUTO_TEST_SUITE_END()