OpenWalnut  1.4.0
WModuleConnector_test.h
00001 //---------------------------------------------------------------------------
00002 //
00003 // Project: OpenWalnut ( http://www.openwalnut.org )
00004 //
00005 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
00006 // For more information see http://www.openwalnut.org/copying
00007 //
00008 // This file is part of OpenWalnut.
00009 //
00010 // OpenWalnut is free software: you can redistribute it and/or modify
00011 // it under the terms of the GNU Lesser General Public License as published by
00012 // the Free Software Foundation, either version 3 of the License, or
00013 // (at your option) any later version.
00014 //
00015 // OpenWalnut is distributed in the hope that it will be useful,
00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 // GNU Lesser General Public License for more details.
00019 //
00020 // You should have received a copy of the GNU Lesser General Public License
00021 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
00022 //
00023 //---------------------------------------------------------------------------
00024 
00025 #ifndef WMODULECONNECTOR_TEST_H
00026 #define WMODULECONNECTOR_TEST_H
00027 
00028 #include <iostream>
00029 #include <string>
00030 
00031 #include <boost/shared_ptr.hpp>
00032 
00033 #include <cxxtest/TestSuite.h>
00034 
00035 #include "../WModuleConnector.h"
00036 #include "../WModuleInputData.h"
00037 #include "../WModuleOutputData.h"
00038 #include "../WModuleInputConnector.h"
00039 #include "../WModuleOutputConnector.h"
00040 #include "../WModule.h"
00041 #include "../../common/WSegmentationFault.h"
00042 #include "../../common/WTransferable.h"
00043 #include "../../common/WPrototyped.h"
00044 #include "../../common/WLogger.h"
00045 #include "../exceptions/WModuleConnectorInitFailed.h"
00046 #include "../exceptions/WModuleConnectionFailed.h"
00047 #include "../exceptions/WModuleConnectorsIncompatible.h"
00048 #include "../exceptions/WModuleException.h"
00049 #include "../exceptions/WModuleConnectorUnconnected.h"
00050 
00051 /**
00052  * Test class used to test data transfer and compatibility checks.
00053  */
00054 class WTestTransferableBase: public WTransferable
00055 {
00056 friend class WModuleConnectorTest;
00057 
00058 public:
00059     /**
00060      * Constructor.
00061      */
00062     WTestTransferableBase(): WTransferable()
00063     {
00064         // do nothing here
00065         m_data = 0;
00066     };
00067 
00068     /**
00069      * Gets the name of this prototype.
00070      *
00071      * \return the name.
00072      */
00073     virtual const std::string getName() const
00074     {
00075         return "WTestTransferableBase";
00076     }
00077 
00078     /**
00079      * Gets the description for this prototype.
00080      *
00081      * \return the description
00082      */
00083     virtual const std::string getDescription() const
00084     {
00085         return "Test class for testing transfer of data.";
00086     }
00087 
00088     /**
00089      * Returns a prototype instantiated with the true type of the deriving class.
00090      *
00091      * \return the prototype.
00092      */
00093     static boost::shared_ptr< WPrototyped > getPrototype()
00094     {
00095         return boost::shared_ptr< WPrototyped >( new WTestTransferableBase() );
00096     }
00097 
00098     /**
00099      * Gives the magic int.
00100      *
00101      * \return the currently set data
00102      */
00103     int get() const
00104     {
00105         return m_data;
00106     }
00107 
00108     /**
00109      * Sets the new int.
00110      *
00111      * \param i the int used for testing.
00112      */
00113     void set( int i )
00114     {
00115         m_data = i;
00116     }
00117 
00118 protected:
00119     /**
00120      * The data.
00121      */
00122     int m_data;
00123 
00124 private:
00125 };
00126 
00127 /**
00128  * Derived test class used to test data transfer and compatibility checks, especially the inheritance checks.
00129  */
00130 class WTestTransferableDerived: public WTestTransferableBase
00131 {
00132 friend class WModuleConnectorTest;
00133 
00134 public:
00135     /**
00136      * Constructor.
00137      */
00138     WTestTransferableDerived(): WTestTransferableBase()
00139     {
00140     };
00141 
00142     /**
00143      * Gets the name of this prototype.
00144      *
00145      * \return the name.
00146      */
00147     virtual const std::string getName() const
00148     {
00149         return "WTestTransferableDerived";
00150     }
00151 
00152     /**
00153      * Gets the description for this prototype.
00154      *
00155      * \return the description
00156      */
00157     virtual const std::string getDescription() const
00158     {
00159         return "Test class for testing transfer of data.";
00160     }
00161 
00162     /**
00163      * Returns a prototype instantiated with the true type of the deriving class.
00164      *
00165      * \return the prototype.
00166      */
00167     static boost::shared_ptr< WPrototyped > getPrototype()
00168     {
00169         return boost::shared_ptr< WPrototyped >( new WTestTransferableDerived() );
00170     }
00171 
00172 protected:
00173 private:
00174 };
00175 
00176 /**
00177  * Class implementing a simple module since WModuleConnector itself is not usable for proper
00178  * testing itself because it is has pure virtual methods, i.e. is abstract.
00179  */
00180 class WModuleImpl: public WModule
00181 {
00182 friend class WModuleConnectorTest;
00183 
00184 public:
00185     /**
00186      * Constructor.
00187      *
00188      * \param n a string to test with (sets initial value).
00189      */
00190     explicit WModuleImpl( std::string n="?" ): WModule()
00191     {
00192         this->n = n;
00193     }
00194 
00195     /**
00196      * Destructor.
00197      */
00198     virtual ~WModuleImpl()
00199     {
00200     }
00201 
00202     /**
00203      * Create instance of this module class.
00204      *
00205      * \return new instance of this module.
00206      */
00207     virtual boost::shared_ptr< WModule > factory() const
00208     {
00209         return boost::shared_ptr< WModule >( new WModuleImpl() );
00210     }
00211 
00212     /**
00213      * Returns name of this module.
00214      *
00215      * \return the name of this module.
00216      */
00217     virtual const std::string getName() const
00218     {
00219         return "testmodule";
00220     }
00221 
00222     /**
00223      * Returns description of module.
00224      *
00225      * \return the description.
00226      */
00227     const std::string getDescription() const
00228     {
00229         return "testdesc";
00230     }
00231 
00232     /**
00233      * Set up connectors.
00234      */
00235     virtual void connectors()
00236     {
00237         m_input = boost::shared_ptr< WModuleInputData< WTestTransferableBase > >(
00238                 new WModuleInputData< WTestTransferableBase > ( shared_from_this(), "in1", "desc1" )
00239         );
00240         // add it to the list of connectors. Please note, that a connector NOT added via addConnector will not work as expected.
00241         addConnector( m_input );
00242 
00243         m_output = boost::shared_ptr< WModuleOutputData< WTestTransferableBase > >(
00244                 new WModuleOutputData< WTestTransferableBase > ( shared_from_this(), "out1", "desc2" )
00245         );
00246         // add it to the list of connectors. Please note, that a connector NOT added via addConnector will not work as expected.
00247         addConnector( m_output );
00248 
00249         // now, the same with the derived class as type
00250         m_inputDerived = boost::shared_ptr< WModuleInputData< WTestTransferableDerived > >(
00251                 new WModuleInputData< WTestTransferableDerived > ( shared_from_this(), "in2", "desc1" )
00252         );
00253         // add it to the list of connectors. Please note, that a connector NOT added via addConnector will not work as expected.
00254         addConnector( m_inputDerived );
00255 
00256         m_outputDerived = boost::shared_ptr< WModuleOutputData< WTestTransferableDerived > >(
00257                 new WModuleOutputData< WTestTransferableDerived > ( shared_from_this(), "out2", "desc2" )
00258         );
00259         // add it to the list of connectors. Please note, that a connector NOT added via addConnector will not work as expected.
00260         addConnector( m_outputDerived );
00261     }
00262 
00263 protected:
00264     /**
00265      * temporary name string
00266      */
00267     std::string n;
00268 
00269     // required since pure virtual
00270     virtual void moduleMain()
00271     {
00272         // Since the modules run in a separate thread: such loops are possible
00273         while( !m_shutdownFlag() )
00274         {
00275             // do fancy stuff
00276             sleep( 1 );
00277         }
00278     }
00279 
00280     /**
00281      * Notifier called whenever a connection got established.
00282      */
00283     virtual void notifyConnectionEstablished( boost::shared_ptr< WModuleConnector > /*here*/,
00284                                               boost::shared_ptr< WModuleConnector > /*there*/ )
00285     {
00286         // std::cout << "connection established between " << n << ":" << here->getCanonicalName() << " and "
00287         //           << there->getCanonicalName() << std::endl;
00288     }
00289 
00290     /**
00291      * Notifier called whenever a connection got closed.
00292      */
00293     virtual void notifyConnectionClosed( boost::shared_ptr< WModuleConnector > /*here*/,
00294                                               boost::shared_ptr< WModuleConnector > /*there*/ )
00295     {
00296         // std::cout << "connection closed between " << n << ":" <<  here->getCanonicalName() << " and "
00297         //           <<  there->getCanonicalName() << std::endl;
00298     }
00299 
00300     /**
00301      * Notifier called whenever a changed data was propagated to one of this modules connectors.
00302      *
00303      * param input  the local connector receiving the event.
00304      * \param output the remote connector propagating the event.
00305      */
00306     virtual void notifyDataChange( boost::shared_ptr< WModuleConnector > /*input */,
00307                                    boost::shared_ptr< WModuleConnector > output )
00308     {
00309         // just copy the data and add one
00310         boost::shared_ptr< WModuleOutputData< WTestTransferableBase > > o =
00311             boost::dynamic_pointer_cast< WModuleOutputData< WTestTransferableBase > >( output );
00312         if( !o.get() )
00313         {
00314             return;
00315         }
00316 
00317         boost::shared_ptr< WTestTransferableBase > ds = o->getData();
00318         if( ds.get() )
00319         {
00320             data = ds->get() + 1;
00321         }
00322 
00323         // std::cout << "change to " << data << " in " << input->getCanonicalName() << " from " << output->getCanonicalName()
00324         //          << std::endl;
00325     }
00326 
00327 private:
00328     /**
00329      * The data lastly submitted.
00330      */
00331     int data;
00332 
00333     /**
00334      * Input connection.
00335      */
00336     boost::shared_ptr< WModuleInputData< WTestTransferableBase > > m_input;
00337 
00338     /**
00339      * Input connection with a derived class as transferable.
00340      */
00341     boost::shared_ptr< WModuleInputData< WTestTransferableDerived > > m_inputDerived;
00342 
00343     /**
00344      * Output connection.
00345      */
00346     boost::shared_ptr< WModuleOutputData< WTestTransferableBase > > m_output;
00347 
00348     /**
00349      * Output connection with a derived class as transferable
00350      */
00351     boost::shared_ptr< WModuleOutputData< WTestTransferableDerived > > m_outputDerived;
00352 };
00353 
00354 /**
00355  * Tests the WModuleConnector class. We use WModuleConnector's direct derived classes WModuleInputConnector and
00356  * WModuleOutputConnector to test their common functionality implemented in WModuleConnector (which has pure virtual members -> so
00357  * can't be instantiated directly).
00358  */
00359 class WModuleConnectorTest : public CxxTest::TestSuite
00360 {
00361 public:
00362     /**
00363      * Setup logger and other stuff for each test.
00364      */
00365     void setUp()
00366     {
00367         WLogger::startup();
00368     }
00369 
00370     /**
00371      * Simple module to test with.
00372      */
00373     boost::shared_ptr< WModuleImpl > m1;
00374 
00375     /**
00376      * Simple module to test with.
00377      */
00378     boost::shared_ptr< WModuleImpl > m2;
00379 
00380     /**
00381      * Simple module to test with.
00382      */
00383     boost::shared_ptr< WModuleImpl > m3;
00384 
00385     /**
00386      * Initialized the test modules.
00387      */
00388     void createModules( void )
00389     {
00390         // init 3 separate test modules
00391         m1 = boost::shared_ptr< WModuleImpl >( new WModuleImpl( "m1" ) );
00392         m2 = boost::shared_ptr< WModuleImpl >( new WModuleImpl( "m2" ) );
00393         m3 = boost::shared_ptr< WModuleImpl >( new WModuleImpl( "m3" ) );
00394     }
00395 
00396     /**
00397      * Initializes modules. This is normally done by the module container.
00398      */
00399     void initModules( void )
00400     {
00401         m1->initialize();
00402         m2->initialize();
00403         m3->initialize();
00404     }
00405 
00406     /**
00407      * Initialize some connections.
00408      */
00409     void initConnections( void )
00410     {
00411         // connect output with input (cyclic)
00412         m1->m_output->connect( m2->m_input );
00413         m1->m_input->connect( m2->m_output );
00414     }
00415 
00416     /**
00417      * Test whether modules can be created without exception and proper initialization of connection lists.
00418      */
00419     void testModuleCreation( void )
00420     {
00421         TS_ASSERT_THROWS_NOTHING( createModules() );
00422 
00423         // check whether there are NO connectors.
00424         // The constructor should now create connectors since shared_ptr are needed -> init in constructor leads to exception
00425         // (it is enough to test one of them)
00426         TS_ASSERT( m1->m_inputConnectors.size() == 0 );
00427         TS_ASSERT( m1->m_outputConnectors.size() == 0 );
00428     }
00429 
00430     /**
00431      * Test whether modules can be initialized without problems.
00432      */
00433     void testModuleInitialization( void )
00434     {
00435         createModules();
00436 
00437         TS_ASSERT_THROWS_NOTHING( initModules() );
00438 
00439         // now there should be 1 everywhere
00440         TS_ASSERT( m1->m_inputConnectors.size() == 2 );
00441         TS_ASSERT( m1->m_outputConnectors.size() == 2 );
00442         TS_ASSERT( m2->m_inputConnectors.size() == 2 );
00443         TS_ASSERT( m2->m_outputConnectors.size() == 2 );
00444         TS_ASSERT( m3->m_inputConnectors.size() == 2 );
00445         TS_ASSERT( m3->m_outputConnectors.size() == 2 );
00446 
00447         // now we have 3 properly initialized modules?
00448         TS_ASSERT( m1->isInitialized()() );
00449         TS_ASSERT( m2->isInitialized()() );
00450         TS_ASSERT( m3->isInitialized()() );
00451     }
00452 
00453     /**
00454      * Test whether module initialization is robust against double init.
00455      */
00456     void testModuleTwiceInitialization( void )
00457     {
00458         WException::disableBacktrace();
00459 
00460         createModules();
00461         initModules();
00462 
00463         // try initializing twice
00464         TS_ASSERT_THROWS( m1->initialize(), WModuleConnectorInitFailed );
00465         TS_ASSERT( m1->isInitialized()() );
00466     }
00467 
00468     /**
00469      * Test whether automatic compatibility check works.
00470      */
00471     void testModuleConnectorCompatibility( void )
00472     {
00473         WException::disableBacktrace();
00474 
00475         createModules();
00476         initModules();
00477 
00478         // connect input with input and output with output should fail
00479         TS_ASSERT_THROWS( m1->m_input->connect( m2->m_input ), WModuleConnectorsIncompatible );
00480         TS_ASSERT_THROWS( m1->m_output->connect( m2->m_output ), WModuleConnectorsIncompatible );
00481 
00482         // there should be nothing connected.
00483         TS_ASSERT( m1->m_output->m_connected.size() == 0 );
00484         TS_ASSERT( m1->m_input->m_connected.size() == 0 );
00485         TS_ASSERT( m2->m_output->m_connected.size() == 0 );
00486         TS_ASSERT( m2->m_input->m_connected.size() == 0 );
00487     }
00488 
00489     /**
00490      * Test whether automatic type compatibility check works.
00491      */
00492     void testModuleConnectorTypeCompatibility( void )
00493     {
00494         WException::disableBacktrace();
00495 
00496         createModules();
00497         initModules();
00498 
00499         TS_ASSERT( m1->m_input->m_connected.size() == 0 );
00500         TS_ASSERT( m1->m_output->m_connected.size() == 0 );
00501         TS_ASSERT( m1->m_inputDerived->m_connected.size() == 0 );
00502         TS_ASSERT( m1->m_outputDerived->m_connected.size() == 0 );
00503 
00504         // connect an input with base type to output of derived type
00505         TS_ASSERT_THROWS_NOTHING( m1->m_input->connect( m2->m_outputDerived ) );
00506         TS_ASSERT( m1->m_input->m_connected.size() == 1 );
00507         TS_ASSERT( m2->m_outputDerived->m_connected.size() == 1 );
00508 
00509         // connect an input of derived type with output of base type
00510         TS_ASSERT_THROWS( m1->m_output->connect( m2->m_inputDerived ), WModuleConnectorsIncompatible );
00511         TS_ASSERT( m1->m_output->m_connected.size() == 0 );
00512         TS_ASSERT( m1->m_inputDerived->m_connected.size() == 0 );
00513     }
00514 
00515     /**
00516      * Test whether connection works properly
00517      */
00518     void testModuleConnection( void )
00519     {
00520         createModules();
00521         initModules();
00522 
00523         TS_ASSERT_THROWS_NOTHING( initConnections() );
00524 
00525         // check that every connector has a connection count of 1
00526         TS_ASSERT( m1->m_output->m_connected.size() == 1 );
00527         TS_ASSERT( m1->m_input->m_connected.size() == 1 );
00528         TS_ASSERT( m2->m_output->m_connected.size() == 1 );
00529         TS_ASSERT( m2->m_input->m_connected.size() == 1 );
00530     }
00531 
00532     /**
00533      * Test whether connecting twice is not possible.
00534      */
00535     void testModuleTwiceConnection( void )
00536     {
00537         createModules();
00538         initModules();
00539         initConnections();
00540 
00541         // try to connect twice
00542         TS_ASSERT_THROWS_NOTHING( m1->m_output->connect( m2->m_input ) );
00543         TS_ASSERT_THROWS_NOTHING( m1->m_input->connect( m2->m_output ) );
00544         TS_ASSERT( m1->m_output->m_connected.size() == 1 );
00545         TS_ASSERT( m1->m_input->m_connected.size() == 1 );
00546         TS_ASSERT( m2->m_output->m_connected.size() == 1 );
00547         TS_ASSERT( m2->m_input->m_connected.size() == 1 );
00548     }
00549 
00550     /**
00551      * Test whether the connection can properly be disconnected.
00552      */
00553     void testModuleDisconnect( void )
00554     {
00555         createModules();
00556         initModules();
00557         initConnections();
00558 
00559         // Disconnect something not connected
00560         TS_ASSERT_THROWS_NOTHING( m1->m_output->disconnect( m1->m_input ) );
00561         TS_ASSERT( m1->m_output->m_connected.size() == 1 );
00562         TS_ASSERT( m1->m_input->m_connected.size() == 1 );
00563 
00564         // Disconnect a connected
00565         TS_ASSERT_THROWS_NOTHING( m1->m_output->disconnect( m2->m_input ) );
00566         TS_ASSERT( m1->m_output->m_connected.size() == 0 );
00567         TS_ASSERT( m1->m_input->m_connected.size() == 1 );
00568         TS_ASSERT( m2->m_output->m_connected.size() == 1 );
00569         TS_ASSERT( m2->m_input->m_connected.size() == 0 );
00570     }
00571 
00572     /**
00573      * Test whether all connections can be removed in one step.
00574      */
00575     void testModuleDisconnectAll( void )
00576     {
00577         createModules();
00578         initModules();
00579         initConnections();
00580 
00581         // connect m3
00582         TS_ASSERT_THROWS_NOTHING( m3->m_input->connect( m2->m_output ) );
00583 
00584         // now m2->out should have 2 connections
00585         TS_ASSERT( m2->m_output->m_connected.size() == 2 );
00586         TS_ASSERT( m3->m_input->m_connected.size() == 1 );
00587 
00588         // remove both connections
00589         m2->m_output->disconnectAll();
00590         TS_ASSERT( m2->m_output->m_connected.size() == 0 );
00591         TS_ASSERT( m1->m_input->m_connected.size() == 0 );
00592         TS_ASSERT( m3->m_input->m_connected.size() == 0 );
00593     }
00594 
00595     /**
00596      * Test whether module clean up is working properly.
00597      */
00598     void testModuleCleanup( void )
00599     {
00600         createModules();
00601         initModules();
00602         initConnections();
00603 
00604         TS_ASSERT_THROWS_NOTHING( m1->cleanup() );
00605         TS_ASSERT( m1->m_inputConnectors.size() == 0 );
00606         TS_ASSERT( m1->m_outputConnectors.size() == 0 );
00607     }
00608 
00609     /**
00610      * Tests the propagation of data.
00611      */
00612     void testModulePropagateDataChange( void )
00613     {
00614         createModules();
00615         initModules();
00616         initConnections();
00617 
00618         // set some data, propagate change
00619         boost::shared_ptr< WTestTransferableBase > data( new WTestTransferableBase() );
00620         int d = 5;
00621         data->set( d );
00622         TS_ASSERT_THROWS_NOTHING( m1->m_output->updateData( data ) );
00623 
00624         // got the data transferred?
00625         TS_ASSERT( m1->m_output->getData()->get() == d );
00626         TS_ASSERT( m2->m_input->getData()->get() == d );
00627         TS_ASSERT( m2->data == d + 1 );
00628     }
00629 
00630     /**
00631      * Tests several cases of unset data.
00632      */
00633     void testModuleInvalidData( void )
00634     {
00635         WException::disableBacktrace();
00636 
00637         createModules();
00638         initModules();
00639         initConnections();
00640 
00641         // try to get data from an unconnected connector
00642         TS_ASSERT( !m3->m_input->getData().get() );
00643 
00644         // try to get uninitialized data -> should return an "NULL" Pointer
00645         TS_ASSERT( m2->m_input->getData() == boost::shared_ptr< WTestTransferableBase >() );
00646     }
00647 };
00648 
00649 #endif  // WMODULECONNECTOR_TEST_H
00650