WModuleConnector_test.h 13.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------

#ifndef WMODULECONNECTOR_TEST_H
#define WMODULECONNECTOR_TEST_H

#include <iostream>
29
#include <string>
30 31 32 33 34 35

#include <boost/shared_ptr.hpp>

#include <cxxtest/TestSuite.h>

#include "../WModuleConnector.h"
36 37
#include "../WModuleInputData.hpp"
#include "../WModuleOutputData.hpp"
38
#include "../WModuleInputConnector.h"
39
#include "../WModuleOutputConnector.h"
40 41 42
#include "../WModule.h"
#include "../../common/WSegmentationFault.h"
#include "../exceptions/WModuleConnectorInitFailed.h"
43 44 45
#include "../exceptions/WModuleConnectionFailed.h"
#include "../exceptions/WModuleConnectorsIncompatible.h"
#include "../exceptions/WModuleException.h"
46
#include "../exceptions/WModuleConnectorUnconnected.h"
47 48

/** 
Alexander Wiebel's avatar
Alexander Wiebel committed
49 50
 * Class implementing a simple module since WModuleConnector itself is not usable for proper
 * testing itself because it is has pure virtual methods, i.e. is abstract.
51 52 53 54 55 56
 */
class WModuleImpl: public WModule
{
friend class WModuleConnectorTest;

public:
57
    explicit WModuleImpl( std::string n="?" ): WModule()
58
    {
59
        this->n = n;
60 61 62 63 64 65
    }

    virtual ~WModuleImpl()
    {
    }

66 67 68 69 70
    virtual boost::shared_ptr< WModule > factory() const
    {
        return boost::shared_ptr< WModule >( new WModuleImpl() );
    }

71 72 73 74 75 76 77 78 79 80 81 82
    // required since pure virtual
    virtual const std::string getName() const
    {
        return "testmodule";
    }

    // required since pure virtual
    virtual const std::string getDescription() const
    {
        return "testdesc";
    }

Sebastian Eichelbaum's avatar
[MERGE]  
Sebastian Eichelbaum committed
83 84 85 86 87
    virtual void connectToGui()
    {
        // do nothing here
    }

88
    virtual void connectors()
89
    {
Alexander Wiebel's avatar
Alexander Wiebel committed
90
        m_input= boost::shared_ptr< WModuleInputData< int > >(
Alexander Wiebel's avatar
Alexander Wiebel committed
91
                new WModuleInputData< int > ( shared_from_this(), "in1", "desc1" )
92 93
        );
        // add it to the list of connectors. Please note, that a connector NOT added via addConnector will not work as expected.
Alexander Wiebel's avatar
Alexander Wiebel committed
94
        addConnector( m_input );
95

Alexander Wiebel's avatar
Alexander Wiebel committed
96
        m_output= boost::shared_ptr< WModuleOutputData< int > >(
Alexander Wiebel's avatar
Alexander Wiebel committed
97
                new WModuleOutputData< int > ( shared_from_this(), "out1", "desc2" )
98 99
        );
        // add it to the list of connectors. Please note, that a connector NOT added via addConnector will not work as expected.
Alexander Wiebel's avatar
Alexander Wiebel committed
100
        addConnector( m_output );
101 102 103
    }

protected:
104

Alexander Wiebel's avatar
Alexander Wiebel committed
105
    /**
106 107 108
     * temporary name string
     */
    std::string n;
109

Alexander Wiebel's avatar
Alexander Wiebel committed
110
    // required since pure virtual
111
    virtual void moduleMain()
112 113 114 115 116 117 118 119 120
    {
        // Since the modules run in a separate thread: such loops are possible
        while ( !m_FinishRequested )
        {
            // do fancy stuff
            sleep( 1 );
        }
    }

Alexander Wiebel's avatar
Alexander Wiebel committed
121 122
    virtual void notifyConnectionEstablished( boost::shared_ptr< WModuleConnector > /*here*/,
                                              boost::shared_ptr< WModuleConnector > /*there*/ )
123
    {
124 125
        // std::cout << "connection established between " << n << ":" << here->getCanonicalName() << " and "
        //           << there->getCanonicalName() << std::endl;
126 127
    }

Alexander Wiebel's avatar
Alexander Wiebel committed
128 129
    virtual void notifyConnectionClosed( boost::shared_ptr< WModuleConnector > /*here*/,
                                              boost::shared_ptr< WModuleConnector > /*there*/ )
130
    {
131 132
        // std::cout << "connection closed between " << n << ":" <<  here->getCanonicalName() << " and "
        //           <<  there->getCanonicalName() << std::endl;
133 134
    }

Alexander Wiebel's avatar
Alexander Wiebel committed
135 136
    virtual void notifyDataChange( boost::shared_ptr< WModuleConnector > /*input*/,
                                   boost::shared_ptr< WModuleConnector > output )
137
    {
Alexander Wiebel's avatar
Alexander Wiebel committed
138
        // just copy the data and add one
Alexander Wiebel's avatar
Alexander Wiebel committed
139
        data = *( boost::shared_dynamic_cast< WModuleOutputData< int > >( output )->getData() ) + 1;
140

Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
141 142
        // std::cout << "change to " << data << " in " << input->getCanonicalName() << " from " << output->getCanonicalName()
        //          << std::endl;
143 144 145 146
    }

private:

Alexander Wiebel's avatar
Alexander Wiebel committed
147
    /**
148 149 150 151
     * The data lastly submitted.
     */
    int data;

Alexander Wiebel's avatar
Alexander Wiebel committed
152
    /**
153 154
     * Input connection.
     */
Alexander Wiebel's avatar
Alexander Wiebel committed
155
    boost::shared_ptr< WModuleInputData< int > > m_input;
Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
156

Alexander Wiebel's avatar
Alexander Wiebel committed
157
    /**
158 159
     * Output connection.
     */
Alexander Wiebel's avatar
Alexander Wiebel committed
160
    boost::shared_ptr< WModuleOutputData< int > > m_output;
161 162 163 164 165 166 167 168 169 170 171 172
};


/**
 * Tests the WModuleConnector class. We use WModuleConnector's direct derived classes WModuleInputConnector and
 * WModuleOutputConnector to test their common functionality implemented in WModuleConnector (which has pure virtual members -> so
 * can't be instantiated directly).
 */
class WModuleConnectorTest : public CxxTest::TestSuite
{
public:

173 174
    /**
     * Simple module to test with.
175
     */
Alexander Wiebel's avatar
Alexander Wiebel committed
176
    boost::shared_ptr< WModuleImpl > m1;
177 178 179 180

    /**
     * Simple module to test with.
     */
Alexander Wiebel's avatar
Alexander Wiebel committed
181
    boost::shared_ptr< WModuleImpl > m2;
182

183 184 185
    /**
     * Simple module to test with.
     */
Alexander Wiebel's avatar
Alexander Wiebel committed
186
    boost::shared_ptr< WModuleImpl > m3;
187 188 189 190 191 192

    /**
     * Initialized the test modules.
     */
    void createModules( void )
    {
Alexander Wiebel's avatar
Alexander Wiebel committed
193
        // init 3 separate test modules
Alexander Wiebel's avatar
Alexander Wiebel committed
194 195 196
        m1 = boost::shared_ptr< WModuleImpl >( new WModuleImpl( "m1" ) );
        m2 = boost::shared_ptr< WModuleImpl >( new WModuleImpl( "m2" ) );
        m3 = boost::shared_ptr< WModuleImpl >( new WModuleImpl( "m3" ) );
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
    }

    /**
     * Initializes modules. This is normally done by the module container.
     */
    void initModules( void )
    {
        m1->initialize();
        m2->initialize();
        m3->initialize();
    }

    void initConnections( void )
    {
        // connect output with input (cyclic)
Alexander Wiebel's avatar
Alexander Wiebel committed
212 213
        m1->m_output->connect( m2->m_input );
        m1->m_input->connect( m2->m_output );
214 215 216 217 218 219 220 221
    }

    /**
     * Test whether modules can be created without exception and proper initialization of connection lists.
     */
    void testModuleCreation( void )
    {
        TS_ASSERT_THROWS_NOTHING( createModules() );
222 223 224

        // check whether there are NO connectors.
        // The constructor should now create connectors since shared_ptr are needed -> init in constructor leads to exception
Alexander Wiebel's avatar
Alexander Wiebel committed
225
        // (it is enough to test one of them)
Alexander Wiebel's avatar
Alexander Wiebel committed
226 227
        TS_ASSERT( m1->m_inputConnectors.size() == 0 );
        TS_ASSERT( m1->m_outputConnectors.size() == 0 );
228
    }
229

230 231 232 233 234 235 236 237
    /**
     * Test whether modules can be initialized without problems.
     */
    void testModuleInitialization( void )
    {
        createModules();

        TS_ASSERT_THROWS_NOTHING( initModules() );
238 239

        // now there should be 1 everywhere
Alexander Wiebel's avatar
Alexander Wiebel committed
240 241 242 243 244 245
        TS_ASSERT( m1->m_inputConnectors.size() == 1 );
        TS_ASSERT( m1->m_outputConnectors.size() == 1 );
        TS_ASSERT( m2->m_inputConnectors.size() == 1 );
        TS_ASSERT( m2->m_outputConnectors.size() == 1 );
        TS_ASSERT( m3->m_inputConnectors.size() == 1 );
        TS_ASSERT( m3->m_outputConnectors.size() == 1 );
246

247
        // now we have 3 properly initialized modules?
248 249
        TS_ASSERT( m1->isInitialized() );
        TS_ASSERT( m2->isInitialized() );
250 251 252 253 254 255 256 257
        TS_ASSERT( m3->isInitialized() );
    }

    /**
     * Test whether module initialization is robust against double init.
     */
    void testModuleTwiceInitialization( void )
    {
258 259
        WException::disableBacktrace();

260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
        createModules();
        initModules();

        // init connectors
        // TODO(ebaum): replace this with the module container, since the module container should manage this
        // well actually this also tests the WModule::addConnector method and instantiation of WModuleInputConnector and
        // WModuleOutputConnector.

        // try initializing twice
        TS_ASSERT_THROWS( m1->initialize(), WModuleConnectorInitFailed );
        TS_ASSERT( m1->isInitialized() );
    }

    /**
     * Test whether automatic compatibility check works.
     */
    void testModuleConnectorCompatibility( void )
    {
278 279
        WException::disableBacktrace();

280 281
        createModules();
        initModules();
282

283
        // connect input with input and output with output should fail
Alexander Wiebel's avatar
Alexander Wiebel committed
284 285
        TS_ASSERT_THROWS( m1->m_input->connect( m2->m_input ), WModuleConnectorsIncompatible );
        TS_ASSERT_THROWS( m1->m_output->connect( m2->m_output ), WModuleConnectorsIncompatible );
286

287
        // there should be nothing connected.
Alexander Wiebel's avatar
Alexander Wiebel committed
288 289 290 291
        TS_ASSERT( m1->m_output->m_Connected.size() == 0 );
        TS_ASSERT( m1->m_input->m_Connected.size() == 0 );
        TS_ASSERT( m2->m_output->m_Connected.size() == 0 );
        TS_ASSERT( m2->m_input->m_Connected.size() == 0 );
292
    }
293

Alexander Wiebel's avatar
Alexander Wiebel committed
294
    /**
295 296 297 298 299 300
     * Test whether connection works properly
     */
    void testModuleConnection( void )
    {
        createModules();
        initModules();
301

302
        TS_ASSERT_THROWS_NOTHING( initConnections() );
303

Alexander Wiebel's avatar
Alexander Wiebel committed
304
        // check that every connector has a connection count of 1
Alexander Wiebel's avatar
Alexander Wiebel committed
305 306 307 308
        TS_ASSERT( m1->m_output->m_Connected.size() == 1 );
        TS_ASSERT( m1->m_input->m_Connected.size() == 1 );
        TS_ASSERT( m2->m_output->m_Connected.size() == 1 );
        TS_ASSERT( m2->m_input->m_Connected.size() == 1 );
309
    }
310

Alexander Wiebel's avatar
Alexander Wiebel committed
311
    /**
312 313 314 315 316 317 318
     * Test whether connecting twice is not possible.
     */
    void testModuleTwiceConnection( void )
    {
        createModules();
        initModules();
        initConnections();
319

320
        // try to connect twice
Alexander Wiebel's avatar
Alexander Wiebel committed
321 322 323 324 325 326
        TS_ASSERT_THROWS_NOTHING( m1->m_output->connect( m2->m_input ) );
        TS_ASSERT_THROWS_NOTHING( m1->m_input->connect( m2->m_output ) );
        TS_ASSERT( m1->m_output->m_Connected.size() == 1 );
        TS_ASSERT( m1->m_input->m_Connected.size() == 1 );
        TS_ASSERT( m2->m_output->m_Connected.size() == 1 );
        TS_ASSERT( m2->m_input->m_Connected.size() == 1 );
327 328
    }

Alexander Wiebel's avatar
Alexander Wiebel committed
329
    /**
330
     * Test whether the connection can properly be disconnected.
331
     */
332
    void testModuleDisconnect( void )
333
    {
334 335 336
        createModules();
        initModules();
        initConnections();
337

338
        // Disconnect something not connected
Alexander Wiebel's avatar
Alexander Wiebel committed
339 340 341
        TS_ASSERT_THROWS_NOTHING( m1->m_output->disconnect( m1->m_input ) );
        TS_ASSERT( m1->m_output->m_Connected.size() == 1 );
        TS_ASSERT( m1->m_input->m_Connected.size() == 1 );
342 343

        // Disconnect a connected
Alexander Wiebel's avatar
Alexander Wiebel committed
344 345 346 347 348
        TS_ASSERT_THROWS_NOTHING( m1->m_output->disconnect( m2->m_input ) );
        TS_ASSERT( m1->m_output->m_Connected.size() == 0 );
        TS_ASSERT( m1->m_input->m_Connected.size() == 1 );
        TS_ASSERT( m2->m_output->m_Connected.size() == 1 );
        TS_ASSERT( m2->m_input->m_Connected.size() == 0 );
349 350
    }

Alexander Wiebel's avatar
Alexander Wiebel committed
351
    /**
352
     * Test whether all connections can be removed in one step.
353
     */
354
    void testModuleDisconnectAll( void )
355
    {
356 357 358 359 360
        createModules();
        initModules();
        initConnections();

        // connect m3
Alexander Wiebel's avatar
Alexander Wiebel committed
361
        TS_ASSERT_THROWS_NOTHING( m3->m_input->connect( m2->m_output ) );
362 363

        // now m2->out should have 2 connections
Alexander Wiebel's avatar
Alexander Wiebel committed
364 365
        TS_ASSERT( m2->m_output->m_Connected.size() == 2 );
        TS_ASSERT( m3->m_input->m_Connected.size() == 1 );
366 367

        // remove both connections
Alexander Wiebel's avatar
Alexander Wiebel committed
368 369 370 371
        m2->m_output->disconnectAll();
        TS_ASSERT( m2->m_output->m_Connected.size() == 0 );
        TS_ASSERT( m1->m_input->m_Connected.size() == 0 );
        TS_ASSERT( m3->m_input->m_Connected.size() == 0 );
372 373
    }

Alexander Wiebel's avatar
Alexander Wiebel committed
374
    /**
375 376 377 378 379 380 381 382 383
     * Test whether module clean up is working properly.
     */
    void testModuleCleanup( void )
    {
        createModules();
        initModules();
        initConnections();

        TS_ASSERT_THROWS_NOTHING( m1->cleanup() );
Alexander Wiebel's avatar
Alexander Wiebel committed
384 385
        TS_ASSERT( m1->m_inputConnectors.size() == 0 );
        TS_ASSERT( m1->m_outputConnectors.size() == 0 );
386
    }
387

388
    /**
389
     * Tests the propagation of data.
390
     */
391
    void testModulePropagateDataChange( void )
392
    {
393 394 395
        createModules();
        initModules();
        initConnections();
396 397

        // set some data, propagate change
Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
398
        int d = 5;
Alexander Wiebel's avatar
Alexander Wiebel committed
399
        TS_ASSERT_THROWS_NOTHING( m1->m_output->updateData( boost::shared_ptr< int >( &d ) ) );
400 401

        // got the data transferred?
Alexander Wiebel's avatar
Alexander Wiebel committed
402 403
        TS_ASSERT( *( m1->m_output->getData() ) == d );
        TS_ASSERT( *( m2->m_input->getData() ) == d );
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
        TS_ASSERT( m2->data == d + 1 );
    }

    /**
     * Tests several cases of unset data.
     */
    void testModuleInvalidData( void )
    {
        WException::disableBacktrace();

        createModules();
        initModules();
        initConnections();

        // try to get data from an unconnected connector
Alexander Wiebel's avatar
Alexander Wiebel committed
419
        TS_ASSERT_THROWS( m3->m_input->getData(), WModuleConnectorUnconnected );
420

Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
421
        // try to get uninitialized data -> should return an "NULL" Pointer
Alexander Wiebel's avatar
Alexander Wiebel committed
422
        TS_ASSERT( m2->m_input->getData() == boost::shared_ptr< int >() );
423 424 425 426 427
    }
};

#endif  // WMODULECONNECTOR_TEST_H