COOLFluiD  Release kernel
COOLFluiD is a Collaborative Simulation Environment (CSE) focused on complex MultiPhysics simulations.
utest-ui-network-connection.cpp
Go to the documentation of this file.
1 // Copyright (C) 2010-2013 von Karman Institute for Fluid Dynamics, Belgium
2 //
3 // This software is distributed under the terms of the
4 // GNU Lesser General Public License version 3 (LGPLv3).
5 // See doc/lgpl.txt and doc/gpl.txt for the license text.
6 
7 #define BOOST_TEST_DYN_LINK
8 #define BOOST_TEST_MODULE "Test module for the ui network Connection class"
9 
10 #include <iostream>
11 
12 #include <boost/assign/list_of.hpp>
13 #include <boost/test/unit_test.hpp>
14 
15 #include "common/TypeInfo.hpp"
18 
21 
22 #define NETWORK_PORT 62784
23 #define NETWORK_HOST "127.0.0.1"
24 
25 using namespace boost;
26 using namespace boost::asio::ip;
27 
28 using namespace cf3::common::XML;
29 using namespace cf3::ui::network;
30 
31 
33 
35 {
36 public:
37 
38  enum Action { INVALID, READ, SEND, ACCEPT, CONNECT };
39 
42  boost::system::error_code error_raised;
43 
44 }; // LastCallbackInfo
45 
47 
49 {
50 public:
51 
52  virtual void error(const std::string &message)
53  {
54  messages.push_back( message );
55  }
56 
57  std::vector< std::string > messages;
58 
59 }; // MyErrorHandler
60 
62 
63 SignalFrame generate_message_frame( const std::string & message )
64 {
65  SignalFrame frame( "message", "cpath:/", "cpath:/" );
66 
67  frame.set_option( "text", cf3::common::class_name<std::string>(), message );
68 
69  return frame;
70 }
71 
73 
74 std::string get_message( const SignalFrame & frame )
75 {
76  return frame.options().value<std::string>( "text" );;
77 }
78 
80 
81 class Client
82 {
83 
84 public:
85 
86  Client( asio::io_service& ios )
87  : io_service( ios ),
88  endpoint( address::from_string(NETWORK_HOST), NETWORK_PORT ),
89  error_handler(new ErrorHandler())
90  {
91  init_connect( endpoint );
92  }
93 
95 
96  void init_connect( tcp::endpoint& endpoint )
97  {
98  connection = TCPConnection::create( io_service );
99  tcp::socket& socket = connection->socket();
100 
101  connection->set_error_handler(error_handler);
102 
103  socket.async_connect( endpoint,
104  boost::bind( &Client::callback_connect,
105  this,
106  asio::placeholders::error )
107  );
108  }
109 
111 
112  void init_read()
113  {
114  connection->read( args,
115  boost::bind( &Client::callback_read,
116  this,
117  asio::placeholders::error )
118  );
119  }
120 
122 
123  void init_send( SignalFrame & frame )
124  {
125  connection->send( frame,
126  boost::bind( &Client::callback_send,
127  this,
128  asio::placeholders::error
129  )
130  );
131  }
132 
134 
135  void callback_send( const boost::system::error_code & error )
136  {
137  last_callback_info.error_raised = error;
138  last_callback_info.action = LastCallbackInfo::SEND;
139  }
140 
142 
143  void callback_connect( const boost::system::error_code & error )
144  {
145  last_callback_info.error_raised = error;
146  last_callback_info.action = LastCallbackInfo::CONNECT;
147 
148  // on error, socket is open but not connected
149  // hence, we need to explicitely close or a runtime error will be raised
150  // when trying to shut the connection down in Connection destructor.
151  if(error)
152  connection->socket().close();
153  }
154 
156 
157  void callback_read( const boost::system::error_code & error )
158  {
159  last_callback_info.error_raised = error;
160  last_callback_info.action = LastCallbackInfo::READ;
161  }
162 
163 public: // data (breaks encapsulation to uTests purpose)
164 
165  tcp::endpoint endpoint;
168  asio::io_service & io_service;
169  boost::shared_ptr<ErrorHandler> error_handler;
171 
172 }; // Client
173 
175 
176 class Server
177 {
178 public: // nested structs
179 
180  struct ClientInfo
181  {
182 
183  public:
185 
187 
188  boost::shared_ptr<ErrorHandler> error_handler;
189 
191 
192  }; // ClientInfo
193 
194 
195 public:
196 
197  Server( asio::io_service& io_service ) :
198  m_acceptor( io_service, tcp::endpoint( tcp::v4(), NETWORK_PORT ) )
199  {
200  init_accept();
201  }
202 
203  void init_accept()
204  {
205  TCPConnection::Ptr conn = TCPConnection::create( m_acceptor.get_io_service() );
206 
207  m_acceptor.async_accept( conn->socket(),
208  boost::bind( &Server::callback_accept,
209  this,
210  conn,// 1st param for the callback fct
211  asio::placeholders::error ) ); // 2nd param
212  }
213 
215 
216  void init_read( TCPConnection::Ptr conn, SignalFrame & buffer )
217  {
218  conn->read( buffer,
219  boost::bind( &Server::callback_read,
220  this,
221  conn, // 1st param for the callback fct
222  asio::placeholders::error ) // 2nd param
223  );
224  }
225 
227 
228  void init_send( TCPConnection::Ptr conn, SignalFrame & buffer )
229  {
230  conn->send( buffer,
231  boost::bind( &Server::callback_send,
232  this,
233  conn, // 1st param for the callback fct
234  asio::placeholders::error // 2nd param
235  )
236  );
237  }
238 
240 
241  void callback_accept( TCPConnection::Ptr conn, const boost::system::error_code & error )
242  {
243  last_callback_info.error_raised = error;
244  last_callback_info.action = LastCallbackInfo::ACCEPT;
245  last_callback_info.connection = conn;
246 
247  ClientInfo& info = m_clients[conn];
248 
249  info.connection = conn;
250  info.buffer = SignalFrame( "message", "cpath:/", "cpath:/" );
251  info.error_handler = boost::shared_ptr<ErrorHandler>(new ErrorHandler());
252  info.id = m_clients.size() - 1; // start at 0
253  }
254 
256 
257  void callback_send( TCPConnection::Ptr conn, const boost::system::error_code & error )
258  {
259  last_callback_info.error_raised = error;
260  last_callback_info.action = LastCallbackInfo::SEND;
261  last_callback_info.connection = conn;
262 
263  // if error is "End of file", client is disconnected => remove the socket
264  if( error == boost::asio::error::eof )
265  m_clients.erase(conn);
266  }
267 
269 
270  void callback_read( TCPConnection::Ptr conn, const boost::system::error_code & error )
271  {
272  last_callback_info.error_raised = error;
273  last_callback_info.action = LastCallbackInfo::READ;
274  last_callback_info.connection = conn;
275 
276  // if error is "End of file", client is disconnected => remove the socket
277  if( error == boost::asio::error::eof )
278  m_clients.erase(conn);
279  }
280 
281 public: // data (breaks encapsulation to uTests purpose)
282 
283  std::map<TCPConnection::ConstPtr, ClientInfo> m_clients;
284  tcp::acceptor m_acceptor;
285 
287 
288 }; // Server
289 
291 
292 BOOST_AUTO_TEST_SUITE( uiNetworkConnectionSuite )
293 
294 
296 BOOST_AUTO_TEST_CASE( connect_failure )
297 {
298  asio::io_service ios;
299  Client client( ios );
300  LastCallbackInfo & info = client.last_callback_info;
301 
302  // no server, client should fail to connect (connection refused)
303 
304  ios.run(); // wait for asynchronous operation (connect) to finish
305 
306  BOOST_CHECK_EQUAL ( info.action, LastCallbackInfo::CONNECT );
307  BOOST_CHECK_EQUAL ( info.error_raised, asio::error::connection_refused );
308 }
309 
311 
312 BOOST_AUTO_TEST_CASE( multi_client_read )
313 {
314  asio::io_service ios_server;
315  Server server ( ios_server );
316  LastCallbackInfo & info_server = server.last_callback_info;
317  std::vector<Client*> clients;
318  std::vector<asio::io_service*> clients_ios(3); // pointers because io_service is noncopyable
319  std::vector<std::string> client_msgs(3);
320 
321  client_msgs[0] = "I am client ONE: the first one that has been created!";
322  client_msgs[1] = "I am client TWO: the second one that has been created!";
323  client_msgs[2] = "I am client THREE: the last one that has been created!";
324 
325  clients_ios[0] = new asio::io_service();
326  clients_ios[1] = new asio::io_service();
327  clients_ios[2] = new asio::io_service();
328 
330  // 1. connect the clients
332 
333  for ( int i = 0 ; i < 3 ; ++i )
334  {
335  asio::io_service & ios = *clients_ios[i];
336  Client * client = new Client( ios );
337  LastCallbackInfo & info = client->last_callback_info;
338 
339  server.init_accept();
340 
341  // wait for the connection to proceed
342  ios.run();
343  ios_server.run_one(); // wait for at most one operation to finish
344 
345  clients.push_back( client );
346  }
347 
349  // 2. each client sends a different string to the server
351 
352  TCPConnection::Ptr conn;
353 
354  std::map<TCPConnection::ConstPtr, Server::ClientInfo>::iterator it = server.m_clients.begin();
355 
356  // initiate a read on all clients
357  for( ; it != server.m_clients.end() ; ++it )
358  server.init_read( it->second.connection, it->second.buffer );
359 
360  // send data from clients
361  for( int i = 0 ; i < 3 ; ++i )
362  {
363  SignalFrame frame = generate_message_frame(client_msgs[i]);
364  clients[i]->init_send( frame );
365  }
366 
367  return;
368 
369  // process read data
370  for ( int i = 0 ; i < 3 ; ++i )
371  {
372  int cnt = ios_server.run_one(); // returns after at most 1 async operation hsa finished
373 
374  // check the right callback was called and the operation success state
375  BOOST_CHECK_EQUAL ( info_server.action, LastCallbackInfo::READ );
376  BOOST_CHECK_EQUAL ( info_server.error_raised, boost::system::errc::success );
377 
378  // init some variables
379  Server::ClientInfo & client = server.m_clients[ info_server.connection ];
380  int client_id = client.id;
381  std::string msg;
382 
383  to_string( *client.buffer.xml_doc.get(), msg );
384  std::cout << msg << std::endl;
385 
386  BOOST_REQUIRE_NO_THROW ( msg = get_message( client.buffer ) );
387 
388  BOOST_CHECK_EQUAL( msg, client_msgs[client_id] );
389  }
390 
391 }
392 
394 
395 BOOST_AUTO_TEST_CASE( multi_client_send )
396 {
397  // 1 server, 3 clients connected to it. 3 phases:
398  // phase 1: the clients connect to the server
399  // phase 2: each sends a string, the server has to receive them and identify the client that spoke
400  // phase 3: server sends a string to each client, each of them has to receive the correct message
401 
402  asio::io_service ios_server;
403  Server server ( ios_server );
404  LastCallbackInfo & info_server = server.last_callback_info;
405  std::vector<Client*> clients;
406  std::vector<asio::io_service*> clients_ios(3); // pointers because io_service is noncopyable
407  std::vector<std::string> client_msgs(3);
408  std::vector<std::string> server_msgs(3);
409 
410  client_msgs[0] = "I am client ONE: the first one that has been created!";
411  client_msgs[1] = "I am client TWO: the second one that has been created!";
412  client_msgs[2] = "I am client THREE: the last one that has been created!";
413 
414  server_msgs[0] = "Hello client ONE, how is it going?";
415  server_msgs[1] = "Hello client TWO, is the weather nice at your place?";
416  server_msgs[2] = "Hello client THREE, it is quite cold here!";
417 
418  clients_ios[0] = new asio::io_service();
419  clients_ios[1] = new asio::io_service();
420  clients_ios[2] = new asio::io_service();
421 
423  // 1. connect the three clients to the server
425 
426  for ( int i = 0 ; i < 3 ; ++i )
427  {
428  asio::io_service & ios = *clients_ios[i];
429  Client * client = new Client( ios );
430  LastCallbackInfo & info = client->last_callback_info;
431 
432  server.init_accept();
433 
434  // wait for the connection to proceed
435  ios.run();
436  ios_server.run_one(); // wait for at most one operation to finish
437 
438  // check everythings is OK
439  BOOST_CHECK_EQUAL ( info.action, LastCallbackInfo::CONNECT );
440  BOOST_CHECK_EQUAL ( info.error_raised, boost::system::errc::success );
441 
442  BOOST_CHECK_EQUAL ( info_server.action, LastCallbackInfo::ACCEPT );
443  BOOST_CHECK_EQUAL ( info_server.error_raised, boost::system::errc::success );
444 
445  clients.push_back( client );
446  }
447 
448  // server should have 3 clients
449 // BOOST_REQUIRE_EQUAL ( server.m_clients.size(), size_t(3) );
450 
452  // 2. each client sends a different string to the server
454 
455  //
456  // a. send strings
457  //
458  TCPConnection::Ptr conn;
459 
460  std::map<TCPConnection::ConstPtr, Server::ClientInfo>::iterator it = server.m_clients.begin();
461 
463 
464  for( ; it != server.m_clients.end() ; ++it )
465  server.init_read( it->second.connection, it->second.buffer );
466 
467  for( int i = 0 ; i < 3 ; ++i )
468  {
469  SignalFrame frame = generate_message_frame(client_msgs[i]);
470  clients[i]->init_send( frame );
471  }
472 
473  return;
474 
475  for ( int i = 0 ; i < 3 ; ++i )
476  {
477  int cnt = ios_server.run_one();
478 
479  std::cout << cnt << std::endl;
480 
481  BOOST_CHECK_EQUAL ( info_server.action, LastCallbackInfo::READ );
482  BOOST_CHECK_EQUAL ( info_server.error_raised, boost::system::errc::success );
483 
484 // conn = info_server.connection;
485 
486  // must be present as a client
487 // BOOST_CHECK ( server.m_clients.find(conn) != server.m_clients.end() );
488 
489  Server::ClientInfo & client = server.m_clients[ info_server.connection ];
490  int client_id = client.id;
491  std::string msg;
492 
493  to_string( *client.buffer.xml_doc.get(), msg );
494  std::cout << msg << std::endl;
495 
496  BOOST_REQUIRE_NO_THROW ( msg = get_message( client.buffer ) );
497 
498  BOOST_CHECK_EQUAL( msg, client_msgs[client_id] );
499  }
500 
501 
502  // 1. server accepts 3 clients and sends a different string to each of them
503  // 2. each client sends a string, the srever should be able to tell from which
504  // client the come
505 }
506 
508 
509 BOOST_AUTO_TEST_CASE( disconnect )
510 {
511  // 1. server closes the connection, client should throw an error (eof)
512  // 2. client closes the connection, server should remove the client from the vector
513 }
514 
516 
517 BOOST_AUTO_TEST_CASE( bad_header )
518 {
519  // 1. header is not a valid interger value
520  // 2. header value is too small
521  // 3. header value is to big
522 }
523 
525 
527 {
528  // data is not valid XML
529 }
530 
532 
533 BOOST_AUTO_TEST_SUITE_END()
SignalFrame generate_message_frame(const std::string &message)
void to_string(const XmlNode &node, std::string &str)
virtual void error(const std::string &message)
boost::shared_ptr< XmlDoc > xml_doc
void init_read(TCPConnection::Ptr conn, SignalFrame &buffer)
external boost library namespace
Network layer for XML communications.
Classes that implement the XML protocol for use in COOLFluiD.
Definition: Component.hpp:43
XmlNode set_option(const std::string &value_key, const std::string type_name, const std::string &value_str, const std::string &descr=std::string())
tcp::endpoint endpoint
boost::shared_ptr< TCPConnection const > ConstPtr
TCPConnection::ConstPtr connection
BOOST_AUTO_TEST_CASE(connect_failure)
Client(asio::io_service &ios)
void callback_read(TCPConnection::Ptr conn, const boost::system::error_code &error)
Manages a set of maps.
Definition: SignalFrame.hpp:31
boost::shared_ptr< TCPConnection > Ptr
void callback_read(const boost::system::error_code &error)
void callback_accept(TCPConnection::Ptr conn, const boost::system::error_code &error)
boost::shared_ptr< ErrorHandler > error_handler
boost::shared_ptr< ErrorHandler > error_handler
asio::io_service & io_service
void init_connect(tcp::endpoint &endpoint)
std::map< TCPConnection::ConstPtr, ClientInfo > m_clients
void callback_send(TCPConnection::Ptr conn, const boost::system::error_code &error)
const TYPE value(const std::string &opt_name) const
Get the value of the option with given name.
Definition: OptionList.hpp:104
void init_send(SignalFrame &frame)
void callback_connect(const boost::system::error_code &error)
std::vector< std::string > messages
std::string get_message(const SignalFrame &frame)
#define NETWORK_PORT
LastCallbackInfo last_callback_info
void init_send(TCPConnection::Ptr conn, SignalFrame &buffer)
tcp::acceptor m_acceptor
SignalOptions & options(const std::string &name=std::string())
LastCallbackInfo last_callback_info
unsigned int Uint
typedef for unsigned int
Definition: CF.hpp:90
void callback_send(const boost::system::error_code &error)
Server(asio::io_service &io_service)
boost::system::error_code error_raised
#define NETWORK_HOST
TCPConnection::Ptr connection
Send comments to:
COOLFluiD Web Admin