/*************************************************************************** * Copyright (C) 2004 by Intra2net AG * * info@intra2net.com * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include #endif using namespace std; using namespace libt2n; using namespace CppUnit; // the server part stringstream logstream; bool close_server=false; bool kill_server=false; int serverfunc(int i) { // magic commands if (i==42) close_server=true; if (i==666) kill_server=true; return i+1; } std::string getserverlog(void) { return logstream.str(); } class serverfunc_res : public libt2n::result { private: int res; friend class boost::serialization::access; template void serialize(Archive & ar, const unsigned int version) { ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result); ar & BOOST_SERIALIZATION_NVP(res); } public: serverfunc_res() { } serverfunc_res(int i) { res=i; } int get_data() { return res; } }; class getserverlog_res : public libt2n::result { private: std::string res; friend class boost::serialization::access; template void serialize(Archive & ar, const unsigned int version) { ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result); ar & BOOST_SERIALIZATION_NVP(res); } public: getserverlog_res() { } getserverlog_res(std::string s) { res=s; } std::string get_data() { return res; } }; class cmd_group_x : public command { private: friend class boost::serialization::access; template void serialize(Archive & ar, const unsigned int version) { ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::command); } }; class serverfunc_cmd : public cmd_group_x { private: int param; friend class boost::serialization::access; template void serialize(Archive & ar, const unsigned int version) { ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(cmd_group_x); ar & BOOST_SERIALIZATION_NVP(param); } public: serverfunc_cmd() { } serverfunc_cmd(int i) { param=i; } libt2n::result* operator()() { return new serverfunc_res(serverfunc(param)); } }; class getserverlog_cmd : public cmd_group_x { private: friend class boost::serialization::access; template void serialize(Archive & ar, const unsigned int version) { ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(cmd_group_x); } public: getserverlog_cmd() { } libt2n::result* operator()() { return new getserverlog_res(getserverlog()); } }; BOOST_CLASS_EXPORT(serverfunc_res) BOOST_CLASS_EXPORT(getserverlog_res) BOOST_CLASS_EXPORT(cmd_group_x) BOOST_CLASS_EXPORT(serverfunc_cmd) BOOST_CLASS_EXPORT(getserverlog_cmd) class cmd_group_x_client : public command_client { public: cmd_group_x_client(libt2n::client_connection *_c, long long _command_timeout_usec=command_timeout_usec_default, long long _hello_timeout_usec=hello_timeout_usec_default) : libt2n::command_client(_c,_command_timeout_usec,_hello_timeout_usec) {} int serverfunc(int i) { libt2n::result_container rc; send_command(new serverfunc_cmd(i), rc); serverfunc_res* res=dynamic_cast(rc.get_result()); if (!res) throw libt2n::t2n_communication_error("result object of wrong type"); return res->get_data(); } std::string getserverlog(void) { libt2n::result_container rc; send_command(new getserverlog_cmd(), rc); getserverlog_res* res=dynamic_cast(rc.get_result()); if (!res) throw libt2n::t2n_communication_error("result object of wrong type"); return res->get_data(); } }; typedef T2nSingletonWrapper wraptype; template<> std::auto_ptr wraptype::SingletonObject = std::auto_ptr(); template<> std::auto_ptr wraptype::WrappedConnection = std::auto_ptr(); class test_wrapper : public TestFixture { CPPUNIT_TEST_SUITE(test_wrapper); CPPUNIT_TEST(no_init_exception); // must be called first!!! CPPUNIT_TEST(simple_wrap); CPPUNIT_TEST(double_use); CPPUNIT_TEST(double_use_with_close); CPPUNIT_TEST(reconnect_after_close); CPPUNIT_TEST(reconnect_not_possible); CPPUNIT_TEST(ignore_server_disconnect); CPPUNIT_TEST(ignore_handler_reconnects); CPPUNIT_TEST_SUITE_END(); public: pid_t child_pid; void setUp() { close_server=false; kill_server=false; switch(child_pid=fork()) { case -1: { CPPUNIT_FAIL("fork error"); break; } case 0: // child { int i=0; while(i < 10 && !kill_server) { close_server=false; socket_server ss("./socket"); group_command_server cs(ss); ss.set_logging(&logstream,debug); // max 10 sec for (; !close_server && !kill_server && i < 10; i++) cs.handle(1000000); } // don't call atexit and stuff _exit(0); } default: // parent { // wait till server is up sleep(1); } } } void tearDown() { // make sure the server-child is dead before the next test runs kill(child_pid,SIGKILL); sleep(1); } void no_init_exception() { CPPUNIT_ASSERT_THROW(t2n_exec(&cmd_group_x_client::serverfunc)(1),std::logic_error); } void simple_wrap() { wraptype::set_connection(auto_ptr (new BasicSocketWrapper("./socket"))); int i=t2n_exec(&cmd_group_x_client::serverfunc)(1); CPPUNIT_ASSERT_EQUAL(2,i); } void double_use() { // only one connection used? wraptype::set_connection(auto_ptr (new BasicSocketWrapper("./socket"))); t2n_exec(&cmd_group_x_client::serverfunc)(17); string out=t2n_exec(&cmd_group_x_client::getserverlog)(); // count the number of times that "new connection accepted" appears in the server log string::size_type p=0; int cnt=0; while ((p=out.find("new connection accepted",p))++ != string::npos) cnt++; CPPUNIT_ASSERT_EQUAL(1,cnt); } void double_use_with_close() { wraptype::set_connection(auto_ptr (new BasicSocketWrapper("./socket"))); t2n_exec(&cmd_group_x_client::serverfunc)(17); // closes the connection from the client side wraptype::set_connection(auto_ptr (new BasicSocketWrapper("./socket"))); string out=t2n_exec(&cmd_group_x_client::getserverlog)(); // count the number of times that "new connection accepted" appears in the server log string::size_type p=0; int cnt=0; while ((p=out.find("new connection accepted",p))++ != string::npos) cnt++; CPPUNIT_ASSERT_EQUAL(2,cnt); } void reconnect_after_close() { wraptype::set_connection(auto_ptr (new ReconnectSocketWrapper("./socket"))); wraptype::get_connection_wrapper()->set_command_timeout_usec(3000000); wraptype::get_connection_wrapper()->set_hello_timeout_usec(3000000); // 42 closes connection on the server side t2n_exec(&cmd_group_x_client::serverfunc)(42); string out=t2n_exec(&cmd_group_x_client::getserverlog)(); // count the number of times that "new connection accepted" appears in the server log string::size_type p=0; int cnt=0; while ((p=out.find("new connection accepted",p))++ != string::npos) cnt++; CPPUNIT_ASSERT_EQUAL(2,cnt); } void reconnect_not_possible() { wraptype::set_connection(auto_ptr (new ReconnectSocketWrapper("./socket"))); // the server doens't like the beast t2n_exec(&cmd_group_x_client::serverfunc)(666); CPPUNIT_ASSERT_THROW(t2n_exec(&cmd_group_x_client::serverfunc)(1),t2n_communication_error); } void ignore_server_disconnect() { wraptype::set_connection(auto_ptr (new ReconnectIgnoreFailureSocketWrapper("./socket"))); // the server doens't like the beast t2n_exec(&cmd_group_x_client::serverfunc)(666); int i=t2n_exec(&cmd_group_x_client::serverfunc)(1); // result is constructed with default constructor on error-and-ignore -> i=0 CPPUNIT_ASSERT_EQUAL(0,i); } void ignore_handler_reconnects() { wraptype::set_connection(auto_ptr (new ReconnectIgnoreFailureSocketWrapper("./socket"))); wraptype::get_connection_wrapper()->set_command_timeout_usec(3000000); wraptype::get_connection_wrapper()->set_hello_timeout_usec(3000000); // 42 closes connection on the server side t2n_exec(&cmd_group_x_client::serverfunc)(42); string out=t2n_exec(&cmd_group_x_client::getserverlog)(); // count the number of times that "new connection accepted" appears in the server log string::size_type p=0; int cnt=0; while ((p=out.find("new connection accepted",p))++ != string::npos) cnt++; CPPUNIT_ASSERT_EQUAL(2,cnt); } }; CPPUNIT_TEST_SUITE_REGISTRATION(test_wrapper); class test_wrapper_noserver : public TestFixture { CPPUNIT_TEST_SUITE(test_wrapper_noserver); CPPUNIT_TEST(ignore_noserver); CPPUNIT_TEST(ignore_finds_lateserver); CPPUNIT_TEST(ignore_wrongserver); CPPUNIT_TEST_SUITE_END(); public: pid_t child_pid; void setUp() { child_pid=0; } void tearDown() { // make sure the server-child is dead before the next test runs if (child_pid != 0) { kill(child_pid,SIGKILL); sleep(1); } } void ignore_noserver() { wraptype::set_connection(auto_ptr (new ReconnectIgnoreFailureSocketWrapper("./socket"))); // wraptype::get_connection_wrapper()->set_logging(&cerr,debug); // there is no server int i=t2n_exec(&cmd_group_x_client::serverfunc)(1); // result is constructed with default constructor on error-and-ignore -> i=0 CPPUNIT_ASSERT_EQUAL(0,i); } void ignore_finds_lateserver() { wraptype::set_connection(auto_ptr (new ReconnectIgnoreFailureSocketWrapper("./socket"))); // there is no server t2n_exec(&cmd_group_x_client::serverfunc)(1); // launch a server close_server=false; kill_server=false; switch(child_pid=fork()) { case -1: { CPPUNIT_FAIL("fork error"); break; } case 0: // child { int i=0; while(i < 10 && !kill_server) { close_server=false; socket_server ss("./socket"); group_command_server cs(ss); ss.set_logging(&logstream,debug); // max 10 sec for (; !close_server && !kill_server && i < 10; i++) cs.handle(1000000); } // don't call atexit and stuff _exit(0); } default: // parent { // wait till server is up sleep(1); } } // server should be active int i=t2n_exec(&cmd_group_x_client::serverfunc)(1); CPPUNIT_ASSERT_EQUAL(2,i); } void send_hello(string hello_string, socket_server* ss, int conn_id) { server_connection *sc=ss->get_connection(conn_id); sc->write(hello_string); } void ignore_wrongserver() { wraptype::set_connection(auto_ptr (new ReconnectIgnoreFailureSocketWrapper("./socket"))); // launch a server switch(child_pid=fork()) { case -1: { CPPUNIT_FAIL("fork error"); break; } case 0: // child { socket_server ss("./socket"); // server sends garbage ostringstream hello; hello << "XYZ 123"; ss.add_callback(new_connection,bind(&test_wrapper_noserver::send_hello, boost::ref(*this), hello.str(),&ss, _1)); // max 10 sec for (int i=0; i < 10; i++) ss.fill_buffer(1000000); // don't call atexit and stuff _exit(0); } default: // parent { // wait till server is up sleep(1); } } // there is no valid server int i=t2n_exec(&cmd_group_x_client::serverfunc)(1); // result is constructed with default constructor on error-and-ignore -> i=0 CPPUNIT_ASSERT_EQUAL(0,i); } }; CPPUNIT_TEST_SUITE_REGISTRATION(test_wrapper_noserver);