OpenWalnut  1.3.1
WModuleConnector_test.h
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #ifndef WMODULECONNECTOR_TEST_H
26 #define WMODULECONNECTOR_TEST_H
27 
28 #include <iostream>
29 #include <string>
30 
31 #include <boost/shared_ptr.hpp>
32 
33 #include <cxxtest/TestSuite.h>
34 
35 #include "../WModuleConnector.h"
36 #include "../WModuleInputData.h"
37 #include "../WModuleOutputData.h"
38 #include "../WModuleInputConnector.h"
39 #include "../WModuleOutputConnector.h"
40 #include "../WModule.h"
41 #include "../../common/WSegmentationFault.h"
42 #include "../../common/WTransferable.h"
43 #include "../../common/WPrototyped.h"
44 #include "../../common/WLogger.h"
45 #include "../exceptions/WModuleConnectorInitFailed.h"
46 #include "../exceptions/WModuleConnectionFailed.h"
47 #include "../exceptions/WModuleConnectorsIncompatible.h"
48 #include "../exceptions/WModuleException.h"
49 #include "../exceptions/WModuleConnectorUnconnected.h"
50 
51 /**
52  * Test class used to test data transfer and compatibility checks.
53  */
55 {
56 friend class WModuleConnectorTest;
57 
58 public:
59  /**
60  * Constructor.
61  */
63  {
64  // do nothing here
65  m_data = 0;
66  };
67 
68  /**
69  * Gets the name of this prototype.
70  *
71  * \return the name.
72  */
73  virtual const std::string getName() const
74  {
75  return "WTestTransferableBase";
76  }
77 
78  /**
79  * Gets the description for this prototype.
80  *
81  * \return the description
82  */
83  virtual const std::string getDescription() const
84  {
85  return "Test class for testing transfer of data.";
86  }
87 
88  /**
89  * Returns a prototype instantiated with the true type of the deriving class.
90  *
91  * \return the prototype.
92  */
93  static boost::shared_ptr< WPrototyped > getPrototype()
94  {
95  return boost::shared_ptr< WPrototyped >( new WTestTransferableBase() );
96  }
97 
98  /**
99  * Gives the magic int.
100  *
101  * \return the currently set data
102  */
103  int get() const
104  {
105  return m_data;
106  }
107 
108  /**
109  * Sets the new int.
110  *
111  * \param i the int used for testing.
112  */
113  void set( int i )
114  {
115  m_data = i;
116  }
117 
118 protected:
119  /**
120  * The data.
121  */
122  int m_data;
123 
124 private:
125 };
126 
127 /**
128  * Derived test class used to test data transfer and compatibility checks, especially the inheritance checks.
129  */
131 {
132 friend class WModuleConnectorTest;
133 
134 public:
135  /**
136  * Constructor.
137  */
139  {
140  };
141 
142  /**
143  * Gets the name of this prototype.
144  *
145  * \return the name.
146  */
147  virtual const std::string getName() const
148  {
149  return "WTestTransferableDerived";
150  }
151 
152  /**
153  * Gets the description for this prototype.
154  *
155  * \return the description
156  */
157  virtual const std::string getDescription() const
158  {
159  return "Test class for testing transfer of data.";
160  }
161 
162  /**
163  * Returns a prototype instantiated with the true type of the deriving class.
164  *
165  * \return the prototype.
166  */
167  static boost::shared_ptr< WPrototyped > getPrototype()
168  {
169  return boost::shared_ptr< WPrototyped >( new WTestTransferableDerived() );
170  }
171 
172 protected:
173 private:
174 };
175 
176 /**
177  * Class implementing a simple module since WModuleConnector itself is not usable for proper
178  * testing itself because it is has pure virtual methods, i.e. is abstract.
179  */
180 class WModuleImpl: public WModule
181 {
182 friend class WModuleConnectorTest;
183 
184 public:
185  /**
186  * Constructor.
187  *
188  * \param n a string to test with (sets initial value).
189  */
190  explicit WModuleImpl( std::string n="?" ): WModule()
191  {
192  this->n = n;
193  }
194 
195  /**
196  * Destructor.
197  */
198  virtual ~WModuleImpl()
199  {
200  }
201 
202  /**
203  * Create instance of this module class.
204  *
205  * \return new instance of this module.
206  */
207  virtual boost::shared_ptr< WModule > factory() const
208  {
209  return boost::shared_ptr< WModule >( new WModuleImpl() );
210  }
211 
212  /**
213  * Returns name of this module.
214  *
215  * \return the name of this module.
216  */
217  virtual const std::string getName() const
218  {
219  return "testmodule";
220  }
221 
222  /**
223  * Returns description of module.
224  *
225  * \return the description.
226  */
227  const std::string getDescription() const
228  {
229  return "testdesc";
230  }
231 
232  /**
233  * Set up connectors.
234  */
235  virtual void connectors()
236  {
237  m_input = boost::shared_ptr< WModuleInputData< WTestTransferableBase > >(
238  new WModuleInputData< WTestTransferableBase > ( shared_from_this(), "in1", "desc1" )
239  );
240  // add it to the list of connectors. Please note, that a connector NOT added via addConnector will not work as expected.
242 
243  m_output = boost::shared_ptr< WModuleOutputData< WTestTransferableBase > >(
244  new WModuleOutputData< WTestTransferableBase > ( shared_from_this(), "out1", "desc2" )
245  );
246  // add it to the list of connectors. Please note, that a connector NOT added via addConnector will not work as expected.
248 
249  // now, the same with the derived class as type
250  m_inputDerived = boost::shared_ptr< WModuleInputData< WTestTransferableDerived > >(
251  new WModuleInputData< WTestTransferableDerived > ( shared_from_this(), "in2", "desc1" )
252  );
253  // add it to the list of connectors. Please note, that a connector NOT added via addConnector will not work as expected.
255 
256  m_outputDerived = boost::shared_ptr< WModuleOutputData< WTestTransferableDerived > >(
257  new WModuleOutputData< WTestTransferableDerived > ( shared_from_this(), "out2", "desc2" )
258  );
259  // add it to the list of connectors. Please note, that a connector NOT added via addConnector will not work as expected.
261  }
262 
263 protected:
264  /**
265  * temporary name string
266  */
267  std::string n;
268 
269  // required since pure virtual
270  virtual void moduleMain()
271  {
272  // Since the modules run in a separate thread: such loops are possible
273  while( !m_shutdownFlag() )
274  {
275  // do fancy stuff
276  sleep( 1 );
277  }
278  }
279 
280  /**
281  * Notifier called whenever a connection got established.
282  */
283  virtual void notifyConnectionEstablished( boost::shared_ptr< WModuleConnector > /*here*/,
284  boost::shared_ptr< WModuleConnector > /*there*/ )
285  {
286  // std::cout << "connection established between " << n << ":" << here->getCanonicalName() << " and "
287  // << there->getCanonicalName() << std::endl;
288  }
289 
290  /**
291  * Notifier called whenever a connection got closed.
292  */
293  virtual void notifyConnectionClosed( boost::shared_ptr< WModuleConnector > /*here*/,
294  boost::shared_ptr< WModuleConnector > /*there*/ )
295  {
296  // std::cout << "connection closed between " << n << ":" << here->getCanonicalName() << " and "
297  // << there->getCanonicalName() << std::endl;
298  }
299 
300  /**
301  * Notifier called whenever a changed data was propagated to one of this modules connectors.
302  *
303  * param input the local connector receiving the event.
304  * \param output the remote connector propagating the event.
305  */
306  virtual void notifyDataChange( boost::shared_ptr< WModuleConnector > /*input */,
307  boost::shared_ptr< WModuleConnector > output )
308  {
309  // just copy the data and add one
310  boost::shared_ptr< WModuleOutputData< WTestTransferableBase > > o =
311  boost::dynamic_pointer_cast< WModuleOutputData< WTestTransferableBase > >( output );
312  if( !o.get() )
313  {
314  return;
315  }
316 
317  boost::shared_ptr< WTestTransferableBase > ds = o->getData();
318  if( ds.get() )
319  {
320  data = ds->get() + 1;
321  }
322 
323  // std::cout << "change to " << data << " in " << input->getCanonicalName() << " from " << output->getCanonicalName()
324  // << std::endl;
325  }
326 
327 private:
328  /**
329  * The data lastly submitted.
330  */
331  int data;
332 
333  /**
334  * Input connection.
335  */
336  boost::shared_ptr< WModuleInputData< WTestTransferableBase > > m_input;
337 
338  /**
339  * Input connection with a derived class as transferable.
340  */
341  boost::shared_ptr< WModuleInputData< WTestTransferableDerived > > m_inputDerived;
342 
343  /**
344  * Output connection.
345  */
346  boost::shared_ptr< WModuleOutputData< WTestTransferableBase > > m_output;
347 
348  /**
349  * Output connection with a derived class as transferable
350  */
351  boost::shared_ptr< WModuleOutputData< WTestTransferableDerived > > m_outputDerived;
352 };
353 
354 /**
355  * Tests the WModuleConnector class. We use WModuleConnector's direct derived classes WModuleInputConnector and
356  * WModuleOutputConnector to test their common functionality implemented in WModuleConnector (which has pure virtual members -> so
357  * can't be instantiated directly).
358  */
359 class WModuleConnectorTest : public CxxTest::TestSuite
360 {
361 public:
362  /**
363  * Setup logger and other stuff for each test.
364  */
365  void setUp()
366  {
368  }
369 
370  /**
371  * Simple module to test with.
372  */
373  boost::shared_ptr< WModuleImpl > m1;
374 
375  /**
376  * Simple module to test with.
377  */
378  boost::shared_ptr< WModuleImpl > m2;
379 
380  /**
381  * Simple module to test with.
382  */
383  boost::shared_ptr< WModuleImpl > m3;
384 
385  /**
386  * Initialized the test modules.
387  */
388  void createModules( void )
389  {
390  // init 3 separate test modules
391  m1 = boost::shared_ptr< WModuleImpl >( new WModuleImpl( "m1" ) );
392  m2 = boost::shared_ptr< WModuleImpl >( new WModuleImpl( "m2" ) );
393  m3 = boost::shared_ptr< WModuleImpl >( new WModuleImpl( "m3" ) );
394  }
395 
396  /**
397  * Initializes modules. This is normally done by the module container.
398  */
399  void initModules( void )
400  {
401  m1->initialize();
402  m2->initialize();
403  m3->initialize();
404  }
405 
406  /**
407  * Initialize some connections.
408  */
409  void initConnections( void )
410  {
411  // connect output with input (cyclic)
412  m1->m_output->connect( m2->m_input );
413  m1->m_input->connect( m2->m_output );
414  }
415 
416  /**
417  * Test whether modules can be created without exception and proper initialization of connection lists.
418  */
419  void testModuleCreation( void )
420  {
421  TS_ASSERT_THROWS_NOTHING( createModules() );
422 
423  // check whether there are NO connectors.
424  // The constructor should now create connectors since shared_ptr are needed -> init in constructor leads to exception
425  // (it is enough to test one of them)
426  TS_ASSERT( m1->m_inputConnectors.size() == 0 );
427  TS_ASSERT( m1->m_outputConnectors.size() == 0 );
428  }
429 
430  /**
431  * Test whether modules can be initialized without problems.
432  */
434  {
435  createModules();
436 
437  TS_ASSERT_THROWS_NOTHING( initModules() );
438 
439  // now there should be 1 everywhere
440  TS_ASSERT( m1->m_inputConnectors.size() == 2 );
441  TS_ASSERT( m1->m_outputConnectors.size() == 2 );
442  TS_ASSERT( m2->m_inputConnectors.size() == 2 );
443  TS_ASSERT( m2->m_outputConnectors.size() == 2 );
444  TS_ASSERT( m3->m_inputConnectors.size() == 2 );
445  TS_ASSERT( m3->m_outputConnectors.size() == 2 );
446 
447  // now we have 3 properly initialized modules?
448  TS_ASSERT( m1->isInitialized()() );
449  TS_ASSERT( m2->isInitialized()() );
450  TS_ASSERT( m3->isInitialized()() );
451  }
452 
453  /**
454  * Test whether module initialization is robust against double init.
455  */
457  {
459 
460  createModules();
461  initModules();
462 
463  // try initializing twice
464  TS_ASSERT_THROWS( m1->initialize(), WModuleConnectorInitFailed );
465  TS_ASSERT( m1->isInitialized()() );
466  }
467 
468  /**
469  * Test whether automatic compatibility check works.
470  */
472  {
474 
475  createModules();
476  initModules();
477 
478  // connect input with input and output with output should fail
479  TS_ASSERT_THROWS( m1->m_input->connect( m2->m_input ), WModuleConnectorsIncompatible );
480  TS_ASSERT_THROWS( m1->m_output->connect( m2->m_output ), WModuleConnectorsIncompatible );
481 
482  // there should be nothing connected.
483  TS_ASSERT( m1->m_output->m_connected.size() == 0 );
484  TS_ASSERT( m1->m_input->m_connected.size() == 0 );
485  TS_ASSERT( m2->m_output->m_connected.size() == 0 );
486  TS_ASSERT( m2->m_input->m_connected.size() == 0 );
487  }
488 
489  /**
490  * Test whether automatic type compatibility check works.
491  */
493  {
495 
496  createModules();
497  initModules();
498 
499  TS_ASSERT( m1->m_input->m_connected.size() == 0 );
500  TS_ASSERT( m1->m_output->m_connected.size() == 0 );
501  TS_ASSERT( m1->m_inputDerived->m_connected.size() == 0 );
502  TS_ASSERT( m1->m_outputDerived->m_connected.size() == 0 );
503 
504  // connect an input with base type to output of derived type
505  TS_ASSERT_THROWS_NOTHING( m1->m_input->connect( m2->m_outputDerived ) );
506  TS_ASSERT( m1->m_input->m_connected.size() == 1 );
507  TS_ASSERT( m2->m_outputDerived->m_connected.size() == 1 );
508 
509  // connect an input of derived type with output of base type
510  TS_ASSERT_THROWS( m1->m_output->connect( m2->m_inputDerived ), WModuleConnectorsIncompatible );
511  TS_ASSERT( m1->m_output->m_connected.size() == 0 );
512  TS_ASSERT( m1->m_inputDerived->m_connected.size() == 0 );
513  }
514 
515  /**
516  * Test whether connection works properly
517  */
518  void testModuleConnection( void )
519  {
520  createModules();
521  initModules();
522 
523  TS_ASSERT_THROWS_NOTHING( initConnections() );
524 
525  // check that every connector has a connection count of 1
526  TS_ASSERT( m1->m_output->m_connected.size() == 1 );
527  TS_ASSERT( m1->m_input->m_connected.size() == 1 );
528  TS_ASSERT( m2->m_output->m_connected.size() == 1 );
529  TS_ASSERT( m2->m_input->m_connected.size() == 1 );
530  }
531 
532  /**
533  * Test whether connecting twice is not possible.
534  */
536  {
537  createModules();
538  initModules();
539  initConnections();
540 
541  // try to connect twice
542  TS_ASSERT_THROWS_NOTHING( m1->m_output->connect( m2->m_input ) );
543  TS_ASSERT_THROWS_NOTHING( m1->m_input->connect( m2->m_output ) );
544  TS_ASSERT( m1->m_output->m_connected.size() == 1 );
545  TS_ASSERT( m1->m_input->m_connected.size() == 1 );
546  TS_ASSERT( m2->m_output->m_connected.size() == 1 );
547  TS_ASSERT( m2->m_input->m_connected.size() == 1 );
548  }
549 
550  /**
551  * Test whether the connection can properly be disconnected.
552  */
553  void testModuleDisconnect( void )
554  {
555  createModules();
556  initModules();
557  initConnections();
558 
559  // Disconnect something not connected
560  TS_ASSERT_THROWS_NOTHING( m1->m_output->disconnect( m1->m_input ) );
561  TS_ASSERT( m1->m_output->m_connected.size() == 1 );
562  TS_ASSERT( m1->m_input->m_connected.size() == 1 );
563 
564  // Disconnect a connected
565  TS_ASSERT_THROWS_NOTHING( m1->m_output->disconnect( m2->m_input ) );
566  TS_ASSERT( m1->m_output->m_connected.size() == 0 );
567  TS_ASSERT( m1->m_input->m_connected.size() == 1 );
568  TS_ASSERT( m2->m_output->m_connected.size() == 1 );
569  TS_ASSERT( m2->m_input->m_connected.size() == 0 );
570  }
571 
572  /**
573  * Test whether all connections can be removed in one step.
574  */
576  {
577  createModules();
578  initModules();
579  initConnections();
580 
581  // connect m3
582  TS_ASSERT_THROWS_NOTHING( m3->m_input->connect( m2->m_output ) );
583 
584  // now m2->out should have 2 connections
585  TS_ASSERT( m2->m_output->m_connected.size() == 2 );
586  TS_ASSERT( m3->m_input->m_connected.size() == 1 );
587 
588  // remove both connections
589  m2->m_output->disconnectAll();
590  TS_ASSERT( m2->m_output->m_connected.size() == 0 );
591  TS_ASSERT( m1->m_input->m_connected.size() == 0 );
592  TS_ASSERT( m3->m_input->m_connected.size() == 0 );
593  }
594 
595  /**
596  * Test whether module clean up is working properly.
597  */
598  void testModuleCleanup( void )
599  {
600  createModules();
601  initModules();
602  initConnections();
603 
604  TS_ASSERT_THROWS_NOTHING( m1->cleanup() );
605  TS_ASSERT( m1->m_inputConnectors.size() == 0 );
606  TS_ASSERT( m1->m_outputConnectors.size() == 0 );
607  }
608 
609  /**
610  * Tests the propagation of data.
611  */
613  {
614  createModules();
615  initModules();
616  initConnections();
617 
618  // set some data, propagate change
619  boost::shared_ptr< WTestTransferableBase > data = boost::shared_ptr< WTestTransferableBase >( new WTestTransferableBase() );
620  int d = 5;
621  data->set( d );
622  TS_ASSERT_THROWS_NOTHING( m1->m_output->updateData( data ) );
623 
624  // got the data transferred?
625  TS_ASSERT( m1->m_output->getData()->get() == d );
626  TS_ASSERT( m2->m_input->getData()->get() == d );
627  TS_ASSERT( m2->data == d + 1 );
628  }
629 
630  /**
631  * Tests several cases of unset data.
632  */
634  {
636 
637  createModules();
638  initModules();
639  initConnections();
640 
641  // try to get data from an unconnected connector
642  TS_ASSERT( !m3->m_input->getData().get() );
643 
644  // try to get uninitialized data -> should return an "NULL" Pointer
645  TS_ASSERT( m2->m_input->getData() == boost::shared_ptr< WTestTransferableBase >() );
646  }
647 };
648 
649 #endif // WMODULECONNECTOR_TEST_H
650