Update to newer boost autoconf macros
[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#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
56using namespace std;
57using namespace libt2n;
58using namespace CppUnit;
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
241class 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
443CPPUNIT_TEST_SUITE_REGISTRATION(test_wrapper);
444
445
446class 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
617CPPUNIT_TEST_SUITE_REGISTRATION(test_wrapper_noserver);