Migrate from cppunit to Boost.test
[libt2n] / test / timeout.cpp
CommitLineData
19facd85
TJ
1/*
2Copyright (C) 2004 by Intra2net AG
45a2ebc9 3
19facd85
TJ
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*/
45a2ebc9
GE
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 <boost/bind.hpp>
34
307b5e74
TJ
35#define BOOST_TEST_DYN_LINK
36#include <boost/test/unit_test.hpp>
45a2ebc9
GE
37
38#include <boost/archive/binary_oarchive.hpp>
39#include <boost/archive/binary_iarchive.hpp>
40#include <boost/archive/xml_oarchive.hpp>
41#include <boost/archive/xml_iarchive.hpp>
42#include <boost/serialization/serialization.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
307b5e74
TJ
50#include "test_fixtures.hxx"
51
45a2ebc9
GE
52#ifdef HAVE_CONFIG_H
53#include <config.h>
54#endif
55
56using namespace std;
57using namespace libt2n;
45a2ebc9
GE
58
59string testfunc2(const string& str)
60{
61 if (str=="throw")
62 throw libt2n::t2n_runtime_error("throw me around");
63 string ret(str);
64 ret+=", testfunc() was here";
65 return ret;
66}
67
68class testfunc2_res : public libt2n::result
69{
70 private:
71 string res;
72
73 friend class boost::serialization::access;
74 template<class Archive>
75 void serialize(Archive & ar, const unsigned int version)
76 {
77 ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result);
78 ar & BOOST_SERIALIZATION_NVP(res);
79 }
80
81 public:
82 testfunc2_res()
83 { }
84
85 testfunc2_res(const string& str)
86 {
87 res=str;
88 }
89
90 string get_data()
91 {
92 return res;
93 }
94};
95
96
97class testfunc2_cmd : public libt2n::command
98{
99 private:
100 string param;
101
102 friend class boost::serialization::access;
103 template<class Archive>
104 void serialize(Archive & ar, const unsigned int version)
105 {
106 ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::command);
107 ar & BOOST_SERIALIZATION_NVP(param);
108 }
109
110 public:
111 testfunc2_cmd()
112 { }
113
114 testfunc2_cmd(const string& str)
115 {
116 param=str;
117 }
118
119 libt2n::result* operator()()
120 {
121 return new testfunc2_res(testfunc2(param));
122 }
123};
124
125#include <boost/serialization/export.hpp>
126
127BOOST_CLASS_EXPORT(testfunc2_cmd)
128BOOST_CLASS_EXPORT(testfunc2_res)
129
130// this is an evil hack to get access to real_write, don't ever do this in an app!!!
131class real_write_connection: public socket_server_connection
132{
133 public:
134 void real_write(const std::string& data)
135 { socket_write(data); }
136};
137
c7857475
GE
138// this is an evil hack to get access to real_write, don't ever do this in an app!!!
139class real_write_client_connection: public socket_client_connection
140{
141 public:
142 void real_write(const std::string& data)
143 { socket_write(data); }
144};
145
45a2ebc9 146
307b5e74
TJ
147class test_timeoutFixture : public KillChildOnShutdownFixture
148{
149protected:
45a2ebc9
GE
150 typedef uint32_t packet_size_indicator;
151
45a2ebc9
GE
152 void send_hello(string hello_string, socket_server* ss, unsigned int conn_id)
153 {
154 server_connection *sc=ss->get_connection(conn_id);
155 sc->write(hello_string);
156 }
157
158 void send_slow_raw_socket(string data, socket_server* ss, unsigned int conn_id)
159 {
160 socket_server_connection *ssc=dynamic_cast<socket_server_connection*>(ss->get_connection(conn_id));
161
162 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
163 real_write_connection *rwc=(real_write_connection*)ssc;
164
165 // we write one char each 0.2 sec
166 for (int pos=0; pos < data.size(); pos++)
167 {
168 string onebyte;
169 onebyte+=data[pos];
170 rwc->real_write(onebyte);
171 usleep(200000);
172 }
173 }
174
307b5e74
TJ
175public:
176 test_timeoutFixture()
177 {
178 }
179
180 ~test_timeoutFixture()
181 {
182 }
183};
184
185BOOST_FIXTURE_TEST_SUITE(test_timeout, test_timeoutFixture)
186
187BOOST_AUTO_TEST_CASE(ConnectTimeout)
188{
189 switch(child_pid=fork())
c7857475 190 {
307b5e74 191 case -1:
c7857475 192 {
307b5e74
TJ
193 BOOST_FAIL("fork error");
194 break;
195 }
196 case 0:
197 // child
198 {
199 try
c7857475 200 {
307b5e74
TJ
201 socket_server ss("./socket");
202 } catch(...)
c7857475 203 {
307b5e74 204 std::cerr << "exception in child. ignoring\n";
c7857475
GE
205 }
206
307b5e74
TJ
207 // don't call atexit and stuff
208 _exit(0);
209 }
c7857475 210
307b5e74
TJ
211 default:
212 // parent
213 {
214 string data;
c7857475 215
307b5e74
TJ
216 // wait till server is up
217 sleep(1);
c7857475 218
307b5e74 219 string errormsg;
c7857475 220
307b5e74 221 socket_client_connection sc("./socket");
b5922184 222
307b5e74
TJ
223 BOOST_CHECK_MESSAGE(sc.connection::is_closed() == true, "connection not closed");
224
225 BOOST_CHECK_MESSAGE(sc.get_last_error_msg() == string("no more retries left after connect error"), "wrong errormessage");
c7857475
GE
226 }
227 }
307b5e74 228}
c7857475 229
307b5e74
TJ
230BOOST_AUTO_TEST_CASE(HelloTimeoutNothing)
231{
232 switch(child_pid=fork())
45a2ebc9 233 {
307b5e74 234 case -1:
45a2ebc9 235 {
307b5e74
TJ
236 BOOST_FAIL("fork error");
237 break;
238 }
239 case 0:
240 // child
241 {
242 try
45a2ebc9 243 {
307b5e74 244 socket_server ss("./socket");
45a2ebc9 245
307b5e74
TJ
246 // max 10 sec
247 for (int i=0; i < 10; i++)
248 ss.fill_buffer(1000000);
249 } catch(...)
250 {
251 std::cerr << "exception in child. ignoring\n";
45a2ebc9
GE
252 }
253
307b5e74
TJ
254 // don't call atexit and stuff
255 _exit(0);
256 }
45a2ebc9 257
307b5e74
TJ
258 default:
259 // parent
260 {
261 string data;
45a2ebc9 262
307b5e74
TJ
263 // wait till server is up
264 sleep(1);
265 socket_client_connection sc("./socket");
266 command_client cc(&sc,1000000,1000000);
45a2ebc9 267
307b5e74 268 t2n_exception* ep=cc.get_constuctor_exception();
45a2ebc9 269
307b5e74
TJ
270 string errormsg;
271 if (ep)
272 errormsg=ep->what();
273
274 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
45a2ebc9
GE
275 }
276 }
307b5e74 277}
45a2ebc9 278
307b5e74
TJ
279BOOST_AUTO_TEST_CASE(HelloTimeoutSlowData)
280{
281 switch(child_pid=fork())
45a2ebc9 282 {
307b5e74 283 case -1:
45a2ebc9 284 {
307b5e74
TJ
285 BOOST_FAIL("fork error");
286 break;
287 }
288 case 0:
289 // child
290 {
291 try
45a2ebc9 292 {
307b5e74 293 socket_server ss("./socket");
45a2ebc9 294
307b5e74
TJ
295 // create a valid packet
296 ostringstream hello;
297 hello << "T2Nv" << PROTOCOL_VERSION << ';';
298 int byteordercheck=1;
299 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
300 hello << ';';
45a2ebc9 301
307b5e74
TJ
302 packet_size_indicator psize=htonl(hello.str().size());
303 std::string send_data(hello.str());
304 send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
45a2ebc9 305
307b5e74 306 ss.add_callback(new_connection,bind(&test_timeout::HelloTimeoutSlowData::send_slow_raw_socket, boost::ref(*this), send_data,&ss, _1));
441d41fe 307
307b5e74
TJ
308 // max 10 sec
309 for (int i=0; i < 10; i++)
310 ss.fill_buffer(1000000);
311 } catch(...)
312 {
313 std::cerr << "exception in child. ignoring\n";
45a2ebc9
GE
314 }
315
307b5e74
TJ
316 // don't call atexit and stuff
317 _exit(0);
318 }
45a2ebc9 319
307b5e74
TJ
320 default:
321 // parent
322 {
323 string data;
45a2ebc9 324
307b5e74
TJ
325 // wait till server is up
326 sleep(1);
327 socket_client_connection sc("./socket");
328 command_client cc(&sc,1000000,1000000);
45a2ebc9 329
307b5e74 330 t2n_exception* ep=cc.get_constuctor_exception();
45a2ebc9 331
307b5e74
TJ
332 string errormsg;
333 if (ep)
334 errormsg=ep->what();
335
336 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
45a2ebc9
GE
337 }
338 }
307b5e74 339}
45a2ebc9 340
307b5e74
TJ
341BOOST_AUTO_TEST_CASE(CommandTimeout)
342{
343 switch(child_pid=fork())
45a2ebc9 344 {
307b5e74 345 case -1:
45a2ebc9 346 {
307b5e74
TJ
347 BOOST_FAIL("fork error");
348 break;
349 }
350 case 0:
351 // child
352 {
353 try
45a2ebc9 354 {
307b5e74 355 socket_server ss("./socket");
45a2ebc9 356
307b5e74
TJ
357 ostringstream hello;
358 hello << "T2Nv" << PROTOCOL_VERSION << ';';
359 int byteordercheck=1;
360 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
361 hello << ';';
441d41fe 362
307b5e74 363 ss.add_callback(new_connection,bind(&test_timeout::CommandTimeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
45a2ebc9 364
307b5e74
TJ
365 // max 10 sec
366 for (int i=0; i < 10; i++)
367 ss.fill_buffer(1000000);
368 } catch(...)
369 {
370 std::cerr << "exception in child. ignoring\n";
45a2ebc9
GE
371 }
372
307b5e74
TJ
373 // don't call atexit and stuff
374 _exit(0);
375 }
45a2ebc9 376
307b5e74
TJ
377 default:
378 // parent
379 {
380 string data;
45a2ebc9 381
307b5e74
TJ
382 // wait till server is up
383 sleep(1);
384 socket_client_connection sc("./socket");
45a2ebc9 385
307b5e74
TJ
386 command_client cc(&sc,1000000,1000000);
387 result_container rc;
45a2ebc9 388
307b5e74 389 string errormsg;
45a2ebc9 390
307b5e74
TJ
391 try
392 {
393 cc.send_command(new testfunc2_cmd("hello"),rc);
45a2ebc9 394 }
307b5e74
TJ
395 catch(t2n_transfer_error &e)
396 { errormsg=e.what(); }
397 catch(...)
398 { throw; }
399
400 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
45a2ebc9
GE
401 }
402 }
307b5e74 403}
45a2ebc9 404
307b5e74
TJ
405BOOST_AUTO_TEST_CASE(CommandSlowResponse)
406{
407 switch(child_pid=fork())
45a2ebc9 408 {
307b5e74 409 case -1:
45a2ebc9 410 {
307b5e74
TJ
411 BOOST_FAIL("fork error");
412 break;
413 }
414 case 0:
415 // child
416 {
417 try
45a2ebc9 418 {
307b5e74 419 socket_server ss("./socket");
45a2ebc9 420
307b5e74
TJ
421 ostringstream hello;
422 hello << "T2Nv" << PROTOCOL_VERSION << ';';
423 int byteordercheck=1;
424 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
425 hello << ';';
45a2ebc9 426
307b5e74 427 ss.add_callback(new_connection,bind(&test_timeout::CommandSlowResponse::send_hello, boost::ref(*this), hello.str(),&ss, _1));
45a2ebc9 428
307b5e74
TJ
429 // max 10 sec
430 for (int i=0; i < 10; i++)
431 {
432 ss.fill_buffer(1000000);
45a2ebc9 433
307b5e74
TJ
434 string data;
435 unsigned int cid;
436
437 if(ss.get_packet(data,cid))
438 {
439 // create a valid packet & send
440 string response="abcdefghijklmnopqrstuvwxyz";
441 packet_size_indicator psize=htonl(response.size());
442 std::string send_data(response);
443 send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
444 send_slow_raw_socket(send_data,&ss,cid);
45a2ebc9
GE
445 }
446 }
307b5e74
TJ
447 } catch(...)
448 {
449 std::cerr << "exception in child. ignoring\n";
45a2ebc9
GE
450 }
451
307b5e74
TJ
452 // don't call atexit and stuff
453 _exit(0);
454 }
45a2ebc9 455
307b5e74
TJ
456 default:
457 // parent
458 {
459 string data;
45a2ebc9 460
307b5e74
TJ
461 // wait till server is up
462 sleep(1);
463 socket_client_connection sc("./socket");
45a2ebc9 464
307b5e74
TJ
465 command_client cc(&sc,1000000,1000000);
466 result_container rc;
45a2ebc9 467
307b5e74 468 string errormsg;
45a2ebc9 469
307b5e74
TJ
470 try
471 {
472 cc.send_command(new testfunc2_cmd("hello"),rc);
45a2ebc9 473 }
307b5e74
TJ
474 catch(t2n_transfer_error &e)
475 { errormsg=e.what(); }
476 catch(...)
477 { throw; }
478
479 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
45a2ebc9
GE
480 }
481 }
307b5e74 482}
45a2ebc9 483
307b5e74
TJ
484BOOST_AUTO_TEST_CASE(DisconnectOnWrite)
485{
486 switch(child_pid=fork())
c7857475 487 {
307b5e74 488 case -1:
c7857475 489 {
307b5e74
TJ
490 BOOST_FAIL("fork error");
491 break;
492 }
493 case 0:
494 // child
495 {
496 try
c7857475 497 {
307b5e74 498 socket_server ss("./socket");
c7857475 499
307b5e74
TJ
500 // bail out as soon as we get something
501 ss.fill_buffer(-1);
502 } catch(...)
503 {
504 std::cerr << "exception in child. ignoring\n";
c7857475
GE
505 }
506
307b5e74
TJ
507 // don't call atexit and stuff
508 _exit(0);
509 }
c7857475 510
307b5e74
TJ
511 default:
512 // parent
513 {
514 string data;
c7857475 515
307b5e74
TJ
516 // don't kill us on broken pipe
517 signal(SIGPIPE, SIG_IGN);
c7857475 518
307b5e74
TJ
519 // wait till server is up
520 sleep(1);
521 socket_client_connection sc("./socket");
c7857475 522
307b5e74 523 string errormsg;
c7857475 524
307b5e74 525 string huge(5000000,'x');
c7857475 526
307b5e74
TJ
527 try
528 {
529 sc.write(huge);
c7857475 530 }
307b5e74
TJ
531 catch(t2n_transfer_error &e)
532 { errormsg=e.what(); }
533 catch(...)
534 { throw; }
535
536 BOOST_CHECK_EQUAL(string("write() returned Broken pipe"),errormsg);
c7857475
GE
537 }
538 }
307b5e74 539}
c7857475 540
307b5e74
TJ
541BOOST_AUTO_TEST_CASE(WriteTwice)
542{
543 switch(child_pid=fork())
039e52da 544 {
307b5e74 545 case -1:
039e52da 546 {
307b5e74
TJ
547 BOOST_FAIL("fork error");
548 break;
549 }
550 case 0:
551 // child
552 {
553 try
039e52da 554 {
307b5e74 555 socket_server ss("./socket");
039e52da 556
307b5e74
TJ
557 // bail out as soon as we get something
558 ss.fill_buffer(-1);
559 ss.fill_buffer(-1);
560 } catch(...)
561 {
562 std::cerr << "exception in child. ignoring\n";
039e52da
GE
563 }
564
307b5e74
TJ
565 // don't call atexit and stuff
566 _exit(0);
567 }
039e52da 568
307b5e74
TJ
569 default:
570 // parent
571 {
572 string data;
039e52da 573
307b5e74
TJ
574 // don't kill us on broken pipe
575 signal(SIGPIPE, SIG_IGN);
039e52da 576
307b5e74
TJ
577 // wait till server is up
578 sleep(1);
579 socket_client_connection sc("./socket");
039e52da 580
307b5e74 581 string errormsg;
039e52da 582
307b5e74 583 sc.write("somedata");
039e52da 584
307b5e74 585 sleep(1);
039e52da 586
307b5e74
TJ
587 // server should disconnect now
588 try
589 {
590 sc.write("other data(2)");
591 sleep(1);
592 sc.write("other data(3)");
039e52da 593 }
307b5e74
TJ
594 catch(t2n_transfer_error &e)
595 { errormsg=e.what(); }
596 catch(...)
597 { throw; }
598
599 BOOST_CHECK_EQUAL(string("write() returned Broken pipe"),errormsg);
039e52da
GE
600 }
601 }
307b5e74 602}
039e52da 603
307b5e74
TJ
604BOOST_AUTO_TEST_CASE(DisconnectOnRead)
605{
606 pid_t pid2;
c7857475 607
307b5e74
TJ
608 switch(child_pid=fork())
609 {
610 case -1:
611 {
612 BOOST_FAIL("fork error");
613 break;
614 }
615 case 0:
616 // child
c7857475 617 {
307b5e74 618 try
c7857475 619 {
307b5e74
TJ
620 // wait till server is up
621 sleep(1);
622
623 socket_client_connection sc("./socket");
624
625 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
626 real_write_client_connection *rwc=(real_write_client_connection*)&sc;
627 rwc->real_write(string(10000,'x'));
628 } catch(...)
c7857475 629 {
307b5e74
TJ
630 std::cerr << "exception in child. ignoring\n";
631 }
c7857475 632
307b5e74
TJ
633 // don't call atexit and stuff
634 _exit(0);
635 }
c7857475 636
307b5e74
TJ
637 default:
638 // parent
639 {
640 // don't kill us on broken pipe
641 signal(SIGPIPE, SIG_IGN);
c7857475 642
307b5e74
TJ
643 socket_server ss("./socket");
644
645 time_t t0 = time(NULL);
c7857475 646
307b5e74
TJ
647 // max 5 sec
648 while (time(NULL) < t0 + 5 )
c7857475 649 {
307b5e74 650 ss.fill_buffer(1000000);
c7857475 651
307b5e74
TJ
652 string data;
653 ss.get_packet(data);
654 }
c7857475 655
307b5e74 656 // are we still alive and able to process data?
c7857475 657
307b5e74
TJ
658 switch(pid2=fork())
659 {
660 case -1:
c7857475 661 {
307b5e74
TJ
662 BOOST_FAIL("fork error");
663 break;
c7857475 664 }
307b5e74
TJ
665 case 0:
666 // child
c7857475 667 {
307b5e74 668 try
c7857475 669 {
307b5e74
TJ
670 socket_client_connection *sc=new socket_client_connection("./socket");
671 sc->write(string(10000,'x'));
672 // socket is closed regularly
673 delete sc;
674 } catch(...)
c7857475 675 {
307b5e74 676 std::cerr << "exception in child. ignoring\n";
c7857475
GE
677 }
678
307b5e74
TJ
679 // don't run regular cleanup, otherwise cppunit stuff gets called
680 _exit(0);
681 }
c7857475 682
307b5e74
TJ
683 default:
684 // parent
685 {
686 string received;
c7857475 687
307b5e74 688 t0 = time(NULL);
c7857475 689
307b5e74
TJ
690 // max 10 sec
691 while (time(NULL) < t0 + 10 )
692 {
693 ss.fill_buffer(1000000);
c7857475 694
307b5e74
TJ
695 if (ss.get_packet(received))
696 break;
c7857475 697 }
307b5e74
TJ
698
699 BOOST_CHECK_EQUAL(string(10000,'x'),received);
c7857475
GE
700 }
701 }
702 }
703 }
307b5e74
TJ
704 kill(pid2,SIGKILL);
705}
c7857475 706
307b5e74
TJ
707BOOST_AUTO_TEST_CASE(BreakAccept)
708{
709 pid_t pid2;
c7857475 710
307b5e74
TJ
711 switch(child_pid=fork())
712 {
713 case -1:
c7857475 714 {
307b5e74
TJ
715 BOOST_FAIL("fork error");
716 break;
717 }
718 case 0:
719 // child
720 {
721 try
c7857475 722 {
307b5e74
TJ
723 // wait till server is really up and waiting
724 sleep(2);
c7857475 725
307b5e74
TJ
726 // connect with very tight timeout and only 1 retry
727 socket_client_connection sc("./socket",50,1);
728 } catch(...)
729 {
730 std::cerr << "exception in child. ignoring\n";
c7857475
GE
731 }
732
307b5e74
TJ
733 // don't call atexit and stuff
734 _exit(0);
735 }
c7857475 736
307b5e74
TJ
737 default:
738 // parent
739 {
740 // don't kill us on broken pipe
741 signal(SIGPIPE, SIG_IGN);
c7857475 742
307b5e74 743 socket_server ss("./socket");
c7857475 744
307b5e74
TJ
745 // server is "working" while client wants to connect
746 sleep(5);
c7857475 747
307b5e74 748 time_t t0 = time(NULL);
c7857475 749
307b5e74
TJ
750 // max 5 sec
751 while (time(NULL) < t0 + 5 )
752 {
753 ss.fill_buffer(1000000);
c7857475 754
307b5e74
TJ
755 string data;
756 ss.get_packet(data);
757 }
c7857475 758
307b5e74
TJ
759 // are we still alive and able to process data?
760
761 switch(pid2=fork())
762 {
763 case -1:
c7857475 764 {
307b5e74
TJ
765 BOOST_FAIL("fork error");
766 break;
767 }
768 case 0:
769 // child
770 {
771 try
c7857475 772 {
307b5e74
TJ
773 socket_client_connection *sc=new socket_client_connection("./socket");
774 sc->write(string(10000,'x'));
775 delete sc;
776 // socket is closed regularly
777 } catch(...)
c7857475 778 {
307b5e74 779 std::cerr << "exception in child. ignoring\n";
c7857475
GE
780 }
781
307b5e74
TJ
782 // don't run regular cleanup, otherwise cppunit stuff gets called
783 _exit(0);
784 }
c7857475 785
307b5e74
TJ
786 default:
787 // parent
788 {
789 string received;
c7857475 790
307b5e74 791 t0 = time(NULL);
c7857475 792
307b5e74
TJ
793 // max 10 sec
794 while (time(NULL) < t0 + 10 )
795 {
796 ss.fill_buffer(1000000);
c7857475 797
307b5e74
TJ
798 if (ss.get_packet(received))
799 break;
c7857475 800 }
307b5e74
TJ
801
802 BOOST_CHECK_EQUAL(string(10000,'x'),received);
c7857475
GE
803 }
804 }
805 }
806 }
307b5e74
TJ
807 kill(pid2,SIGKILL);
808}
45a2ebc9 809
307b5e74 810BOOST_AUTO_TEST_SUITE_END()