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