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