OpenWalnut  1.4.0
WThreadedFunction_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 WTHREADEDFUNCTION_TEST_H
00026 #define WTHREADEDFUNCTION_TEST_H
00027 
00028 #include <string>
00029 
00030 #include <cxxtest/TestSuite.h>
00031 
00032 #include "../WThreadedFunction.h"
00033 #include "../WSharedObject.h"
00034 
00035 /**
00036  * \class WThreadedFunctionTest
00037  *
00038  * Tests the WThreadedFunction class.
00039  */
00040 class WThreadedFunctionTest : public CxxTest::TestSuite
00041 {
00042     /**
00043      * A threaded function.
00044      */
00045     class FuncType
00046     {
00047     public:
00048         /**
00049          * Constructor, initialize some stuff.
00050          *
00051          * \param value An int value.
00052          */
00053         FuncType( int value ) // NOLINT
00054             : m_input( new int( value ) ) // NOLINT
00055         {
00056             // init stuff here
00057             m_result.getWriteTicket()->get() = 0;
00058             m_stopped.getWriteTicket()->get() = false;
00059 
00060             if( value < 0 )
00061             {
00062                 value = -value;
00063             }
00064         }
00065 
00066         /**
00067          * This is the actual thread function.
00068          *
00069          * \param shutdown A flag indicating the thread is supposed to stop.
00070          */
00071         void operator() ( std::size_t, std::size_t, WBoolFlag const& shutdown )
00072         {
00073             for( int i = 1; i <= *m_input.get() && !shutdown(); ++i )
00074             {
00075                 m_result.getWriteTicket()->get() += i;
00076             }
00077             if( shutdown() )
00078             {
00079                 m_stopped.getWriteTicket()->get() = true;
00080             }
00081             sleep( 1 );
00082         }
00083 
00084         /**
00085          * Check if the thread was ordered to stop.
00086          *
00087          * \return true, if the thread was ordered to stop
00088          */
00089         bool stopped()
00090         {
00091             return m_stopped.getReadTicket()->get();
00092         }
00093 
00094         /**
00095          * A method to extract the result.
00096          *
00097          * \return The result of the threaded computation.
00098          */
00099         int getResult()
00100         {
00101             return m_result.getReadTicket()->get();
00102         }
00103 
00104         /**
00105          * Reset everything.
00106          */
00107         void reset()
00108         {
00109             m_result.getWriteTicket()->get() = 0;
00110         }
00111 
00112     private:
00113         //! the input data
00114         boost::shared_ptr< int const > m_input;
00115 
00116         //! the result
00117         WSharedObject< int > m_result;
00118 
00119         //! thread stopped?
00120         WSharedObject< bool > m_stopped;
00121     };
00122 
00123     /**
00124      * A function that throws exceptions.
00125      */
00126     class ExceptionalFuncType
00127     {
00128     public:
00129         /**
00130          * The function.
00131          */
00132         void operator() ( std::size_t, std::size_t, WBoolFlag& )
00133         {
00134             throw WException( std::string( "Test!" ) );
00135         }
00136     };
00137 
00138 public:
00139     /**
00140      * A function computed by multiple threads should correctly set
00141      * its status and compute the correct results.
00142      */
00143     void testMultipleThreads()
00144     {
00145         boost::shared_ptr< FuncType > func( new FuncType( 5 ) );
00146         // test 1 thread
00147         {
00148             WThreadedFunction< FuncType > f( 1, func );
00149 
00150             TS_ASSERT_EQUALS( f.status(), W_THREADS_INITIALIZED );
00151             f.run();
00152             TS_ASSERT_EQUALS( f.status(), W_THREADS_RUNNING );
00153             f.wait();
00154             TS_ASSERT_EQUALS( f.status(), W_THREADS_FINISHED );
00155 
00156             TS_ASSERT_EQUALS( func->getResult(), 15 );
00157             func->reset();
00158 
00159             f.run();
00160             TS_ASSERT_EQUALS( f.status(), W_THREADS_RUNNING );
00161             f.wait();
00162 
00163             TS_ASSERT_EQUALS( func->getResult(), 15 );
00164 
00165             f.run();
00166             TS_ASSERT_EQUALS( f.status(), W_THREADS_RUNNING );
00167             f.wait();
00168 
00169             TS_ASSERT_EQUALS( func->getResult(), 30 );
00170             func->reset();
00171         }
00172         // test 2 threads
00173         {
00174             WThreadedFunction< FuncType > f( 2, func );
00175 
00176             TS_ASSERT_EQUALS( f.status(), W_THREADS_INITIALIZED );
00177             f.run();
00178             TS_ASSERT_EQUALS( f.status(), W_THREADS_RUNNING );
00179             f.wait();
00180             TS_ASSERT_EQUALS( f.status(), W_THREADS_FINISHED );
00181 
00182             TS_ASSERT_EQUALS( func->getResult(), 30 );
00183             func->reset();
00184         }
00185         // test 5 threads
00186         {
00187             WThreadedFunction< FuncType > f( 5, func );
00188 
00189             TS_ASSERT_EQUALS( f.status(), W_THREADS_INITIALIZED );
00190             f.run();
00191             TS_ASSERT_EQUALS( f.status(), W_THREADS_RUNNING );
00192             f.wait();
00193             TS_ASSERT_EQUALS( f.status(), W_THREADS_FINISHED );
00194 
00195             TS_ASSERT_EQUALS( func->getResult(), 75 );
00196             func->reset();
00197         }
00198     }
00199 
00200     /**
00201      * Status should be set correctly when threads are ordered to stop.
00202      */
00203     void testStopThreads()
00204     {
00205         boost::shared_ptr< FuncType > func( new FuncType( 100000000 ) );
00206         WThreadedFunction< FuncType > f( 6, func );
00207 
00208         TS_ASSERT_EQUALS( f.status(), W_THREADS_INITIALIZED );
00209         f.run();
00210         TS_ASSERT_EQUALS( f.status(), W_THREADS_RUNNING );
00211         f.stop();
00212         TS_ASSERT_EQUALS( f.status(), W_THREADS_STOP_REQUESTED );
00213         f.wait();
00214         TS_ASSERT_EQUALS( f.status(), W_THREADS_ABORTED );
00215 
00216         TS_ASSERT( func->stopped() );
00217         func->reset();
00218     }
00219 
00220     /**
00221      * The stop condition should be notified correctly.
00222      */
00223     void testStopCondition()
00224     {
00225         boost::shared_ptr< FuncType > func( new FuncType( 5 ) );
00226         WThreadedFunction< FuncType > f( 6, func );
00227 
00228         TS_ASSERT_EQUALS( f.status(), W_THREADS_INITIALIZED );
00229         f.run();
00230         TS_ASSERT_EQUALS( f.status(), W_THREADS_RUNNING );
00231         f.getThreadsDoneCondition()->wait();
00232         TS_ASSERT_EQUALS( f.status(), W_THREADS_FINISHED );
00233 
00234         TS_ASSERT_EQUALS( func->getResult(), 90 );
00235         func->reset();
00236     }
00237 
00238     /**
00239      * Exceptions should lead to the status beeing changed to W_THREADS_ABORTED. Also,
00240      * exceptions should be forwarded to the exception handler.
00241      */
00242     void testExceptionHandling()
00243     {
00244         boost::shared_ptr< ExceptionalFuncType > func( new ExceptionalFuncType );
00245         WThreadedFunction< ExceptionalFuncType > f( 7, func );
00246         f.subscribeExceptionSignal( boost::bind( &WThreadedFunctionTest::handleException, this, _1 ) );
00247 
00248         m_exceptionCounter.getWriteTicket()->get() = 0;
00249 
00250         f.run();
00251         f.wait();
00252 
00253         TS_ASSERT_EQUALS( f.status(), W_THREADS_ABORTED );
00254         TS_ASSERT_EQUALS( m_exceptionCounter.getReadTicket()->get(), 7 );
00255     }
00256 
00257 private:
00258     /**
00259      * Exception callback.
00260      */
00261     void handleException( WException const& )
00262     {
00263         ++m_exceptionCounter.getWriteTicket()->get();
00264     }
00265 
00266     //! a counter
00267     WSharedObject< int > m_exceptionCounter;
00268 };
00269 
00270 #endif  // WTHREADEDFUNCTION_TEST_H