CppUnit project page CppUnit home page

inputbasedtest.h

Go to the documentation of this file.
00001 #ifndef CPPUT_INPUTTEST_H_INCLUDED
00002 # define CPPUT_INPUTTEST_H_INCLUDED
00003 
00004 # include "forwards.h"
00005 # include <json/value.h>
00006 # include <cpptl/any.h>
00007 # include <cpptl/reflection.h>
00008 
00009 
00010 namespace CppUT {
00011 
00012 // input based test are factory based.
00013 // Input data is provided to the factory
00014 // test is executed based on the input and assertion are done.
00015 // This is a single test case.
00016 // Fixture is stand-alone and not related to test case in anyway.
00017 
00018 class CPPUT_API TableDataSource
00019 {
00020 public:
00021    virtual ~TableDataSource()
00022    {
00023    }
00024 
00025    virtual CppTL::Any getCellValue( int column, int row ) const = 0;
00026    virtual int rowCount() const = 0;
00027    virtual int columnCount() const = 0;
00028 };
00029 
00030 class CPPUT_API JsonTableDataSource : public TableDataSource
00031 {
00032 public:
00033    JsonTableDataSource( const Json::Value &data )
00034       : data_( data )
00035    {
00036    }
00037 
00038 public: // overridden from TableDataSource
00039    virtual CppTL::Any getCellValue( int column, int row ) const
00040    {
00041       const Json::Value &value = data_[row][column];
00042       switch ( value.type() )
00043       {
00044       case Json::nullValue: 
00045          return CppTL::Any();
00046       case Json::intValue:
00047          return CppTL::Any( value.asInt() );
00048       case Json::uintValue:
00049          return CppTL::Any( value.asUInt() );
00050       case Json::realValue:
00051          return CppTL::Any( value.asDouble() );
00052       case Json::stringValue:
00053          return CppTL::Any( value.asString() );
00054       case Json::booleanValue:
00055          return CppTL::Any( value.asBool() );
00056       default:
00057          return CppTL::Any(); // unsupported conversion, will blow on compare or get/set.
00058       }
00059    }
00060 
00061    virtual int rowCount() const
00062    {
00063       return data_.size();
00064    }
00065 
00066    virtual int columnCount() const
00067    {
00068       int column = 0;
00069       return data_[column].size();
00070    }
00071    Json::Value data_;
00072 };
00073 
00074 
00075 
00076 class CPPUT_API InputTest
00077 {
00078 public:
00079    virtual ~InputTest()
00080    {
00081    }
00082 
00083    virtual void operator()( const TableDataSource &table ) = 0;
00084 };
00085 
00086 
00087 
00088 template<class Functor>
00089 struct Bind234
00090 {
00091    typedef CPPTL_TYPENAME Functor::result_type result_type;
00092    typedef CPPTL_TYPENAME Functor::arg1_type arg1_type;
00093    typedef arg1_type first_argument_type;
00094    typedef CPPTL_TYPENAME Functor::arg2_type arg2_type;
00095    typedef CPPTL_TYPENAME Functor::arg3_type arg3_type;
00096    typedef CPPTL_TYPENAME Functor::arg4_type arg4_type;
00097    typedef CppTL::Functor1<arg1_type> functor_type;
00098    Bind234( const Functor &functor, arg2_type a2, arg3_type a3, arg4_type a4 )
00099       : functor_( functor ), a2_( a2 ), a3_( a3 ), a4_( a4 )
00100    {
00101    }
00102 
00103    void operator()( const arg1_type &arg1 ) const
00104    {
00105       functor_( arg1, a2_, a3_, a4_ );
00106    }
00107 
00108 private:
00109    Functor functor_;
00110    arg2_type a2_;
00111    arg3_type a3_;
00112    arg4_type a4_;
00113 };
00114 
00115 
00117 template<typename Functor, typename A2, typename A3, typename A4>
00118 CppTL::Functor1<CPPTL_TYPENAME Functor::arg1_type>
00119 bind234( const Functor &functor, A2 a2, A3 a3, A4 a4 )
00120 {
00121    return CppTL::fn1( Bind234<Functor>( functor, a2, a3, a4 ) );
00122 }
00123 
00124 /* \brief Input based test for table with action based on column header.
00125  * \code
00126  * class OperationInputTest;
00127  * CPPTL_DECLARE_TYPE_AND_PTR_INFO( OperationInputTest );     // Not required if RTTI are always enabled
00128  * CPPTL_REFLECT_REGISTER_CLASS( OperationInputTest )
00129  * class OperationInputTest : public CppUT::ColumnInputTest
00130  * {
00131  * public:
00132  *    CPPUT_INPUT_FIXTURE_BEGIN( OperationInputTest )
00133  *       CPPTL_REFLECT_METHOD_WITH_RETURN( result )
00134  *       CPPTL_REFLECT_RENAMED_ATTRIBUT( lhs_, "leftHandSide" )
00135  *       CPPTL_REFLECT_RENAMED_ATTRIBUT( rhs_, "rightHandSide" )
00136  *       CPPTL_REFLECT_RENAMED_ATTRIBUT( operation_, "operation" )
00137  *    CPPUT_INPUT_FIXTURE_END()
00138  *    int result()
00139  *    {
00140  *       if ( operation_ == "add" )
00141  *          return lhs_ + rhs_;
00142  *       else if ( operation_ == "substract" )
00143  *          return lhs_ - rhs_;
00144  *       CPPUT_CHECKING_FAIL( "Unsupported operation: " + operation_ );
00145  *       return 0;
00146  *    }
00147  *    std::string operation_;
00148  *    int lhs_;
00149  *    int rhs_;
00150  * };
00151  * \endcode
00152  */
00153 class CPPUT_API ColumnInputTest : public InputTest
00154 {
00155 public:
00156    ColumnInputTest()
00157    {
00158       this_ = CppTL::makeAny( this );
00159    }
00160 
00161    virtual const CppTL::Class *getClass() const = 0;
00162 
00163    virtual CppTL::Any getThis() = 0;
00164 
00165 public: // overridden from InputTest
00166    virtual void operator()( const TableDataSource &table )
00167    {
00168       // Parse the header:
00169       // For each column, generate an action that process the value.
00170       // Assign order to each action: setup or execution ? assertion ?
00171       this_ = getThis();
00172       int rowCount = table.rowCount();
00173       int columnCount = table.columnCount();
00174       if ( rowCount < 1  ||  columnCount < 1)
00175       {
00176          CPPUT_CHECKING_FAIL( "Table row header is missing." );
00177          return;
00178       }
00179       const CppTL::Class *aClass = getClass();
00180       if ( !aClass )
00181       {
00182          CPPUT_CHECKING_FAIL( "Reflection class not found for input test." );
00183          return;
00184       }
00185       for ( int column = 0; column < columnCount; ++column )
00186       {
00187          CppTL::Any value = table.getCellValue( column, 0 );
00188          const std::string *headerName = ::get( &value, CppTL::Type<std::string>() );
00189          if ( headerName == 0 )
00190          {
00191             CPPUT_CHECKING_FAIL( "Can not convert header column to string." );   // @todo add col index
00192             return;
00193          }
00194          if ( headerName->empty() )
00195             continue;
00196          if ( headerName->at( headerName->length() - 1 ) == '?' ) // check action
00197          {
00198             CppTL::ConstString name( headerName->c_str(), headerName->c_str() + headerName->length() - 1 );
00199             CppTL::Functor0R<CppTL::Any> actual;
00200             const CppTL::Attribut *attribut = aClass->findAttribut( name );
00201             if ( attribut != 0 )
00202             {
00203                actual = CppTL::bindr( CppTL::memfn1r( this, &ColumnInputTest::actionGetAttribut ), 
00204                                       CppTL::cref( *attribut ) );
00205             }
00206             else
00207             {
00208                const CppTL::Method *method = aClass->findMethod( name );
00209                if ( method == 0 )
00210                {
00211                   CPPUT_CHECKING_FAIL( "No method or attribut found by reflection matching header name: " + name );
00212                   return;
00213                }
00214                actual = CppTL::bindr( CppTL::memfn1r( this, &ColumnInputTest::actionGetResult ), 
00215                                       CppTL::cref( *method ) );
00216             }
00217             Action action = bind234( CppTL::memfn4( this, &ColumnInputTest::actionCheckEquals ),
00218                                                     column,
00219                                                     CppTL::cref( table ),
00220                                                     actual );
00221             checkActions_.push_back( action );
00222          }
00223          else // set attribut or invoke method action
00224          {
00225             const CppTL::Attribut *attribut = aClass->findAttribut( headerName->c_str() );
00226             if ( attribut != 0 )
00227             {
00228                Action action = bind234( CppTL::memfn4( this, &ColumnInputTest::actionSetAttribut ),
00229                                                        column,
00230                                                        CppTL::cref( table ),
00231                                                        CppTL::cref( *attribut ) );
00232                setAttributActions_.push_back( action );
00233             }
00234             else
00235             {
00236                const CppTL::Method *method = aClass->findMethod( headerName->c_str() );
00237                if ( method == 0 )
00238                {
00239                   CPPUT_CHECKING_FAIL( "No method or attribut found by reflection matching header name: " + *headerName );
00240                   return;
00241                }
00242                Action action = bind234( CppTL::memfn4( this, &ColumnInputTest::actionInvokeMethod ),
00243                                                        column,
00244                                                        CppTL::cref( table ),
00245                                                        CppTL::cref( *method ) );
00246                checkActions_.push_back( action );
00247             }
00248          }
00249       }
00250       for ( int row = 1; row < rowCount; ++row )
00251       {
00252          processActions( setAttributActions_, row );
00253          processActions( invokeActions_, row );
00254          processActions( checkActions_, row );
00255       }
00256    }
00257 
00258 private:
00259    typedef CppTL::Functor1<int> Action;
00260    typedef std::deque<Action> Actions;
00261 
00262 private:
00263    void processActions( const Actions &actions, int row )
00264    {
00265       Actions::const_iterator it = actions.begin();
00266       Actions::const_iterator itEnd = actions.end();
00267       for ( ; it != itEnd; ++it )
00268       {
00269          const Action &action = *it;
00270          action( row );
00271       }
00272    }
00273 
00274    void actionSetAttribut( int row,
00275                            int column,
00276                            const TableDataSource &table,
00277                            const CppTL::Attribut &attribut )
00278    {
00279       attribut.set( this_, table.getCellValue( column, row ) );
00280    }
00281 
00282    void actionInvokeMethod( int row,
00283                             int column,
00284                             const TableDataSource &table,
00285                             const CppTL::Method &method )
00286    {
00287       CppTL::MethodParameters parameters;
00288       parameters.push_back( table.getCellValue( column, row ) );
00289       method.invoke( this_, parameters );
00290    }
00291 
00292    void actionCheckEquals( int row,
00293                            int column,
00294                            const TableDataSource &table,
00295                            CppTL::Functor0R<CppTL::Any> actual )
00296    {
00297       CppTL::Any actualValue = actual();
00298       CppTL::Any expectedValue = table.getCellValue( column, row );
00299       bool hasSameType = actualValue.hasSameType( expectedValue );
00300       CPPUT_CHECK( hasSameType, "Can not compare cell value: different type!" ); // @todo dump type
00301       if ( !hasSameType )
00302          return;
00303       typedef CppTL::Type<int> TypeInt;
00304       typedef CppTL::Type<double> TypeDouble;
00305       typedef CppTL::Type<std::string> TypeString;
00306       if ( actualValue.type() == CppTL::typeId( TypeInt() ) )
00307       {
00308          CPPUT_CHECK_EQUAL( any_cast( expectedValue, TypeInt() ),
00309                             any_cast( actualValue, TypeInt() ) );
00310       } 
00311       else if ( actualValue.type() == CppTL::typeId( TypeDouble() ) )
00312       {
00313          CPPUT_CHECK_EQUAL( any_cast( expectedValue, TypeDouble() ),
00314                             any_cast( actualValue, TypeDouble() ) );
00315       }
00316       else if ( actualValue.type() == CppTL::typeId( TypeString() ) )
00317       {
00318          CPPUT_CHECK_EQUAL( any_cast( expectedValue, TypeString() ),
00319                             any_cast( actualValue, TypeString() ) );
00320       }
00321       else
00322       {
00323          CPPUT_CHECKING_FAIL( "Unsupported type for equality test in cell value." );
00324       }
00325    }
00326 
00327    CppTL::Any actionGetAttribut( const CppTL::Attribut &attribut )
00328    {
00329       return attribut.get( this_ );
00330    }
00331 
00332    CppTL::Any actionGetResult( const CppTL::Method &method )
00333    {
00334       CppTL::MethodParameters parameters;
00335       return method.invoke( this_, parameters );
00336    }
00337 
00338 private:
00339    CppTL::Any this_;
00340    Actions setAttributActions_;
00341    Actions invokeActions_;
00342    Actions checkActions_;
00343 };
00344 
00345 
00346 #define CPPUT_INPUT_FIXTURE_BEGIN( ClassType )     \
00347    public: /* overridden from ColumnInputTest */   \
00348       virtual CppTL::Any getThis()                 \
00349       {                                            \
00350          return CppTL::makeAny( this );            \
00351       }                                            \
00352    CPPTL_REFLECT_CLASS_BEGIN( ClassType )          \
00353 
00354 #define CPPUT_INPUT_FIXTURE_END() \
00355    CPPTL_REFLECT_CLASS_END()
00356 
00357 } // namespace CppUT
00358 
00359 
00360 
00361 #endif // CPPUT_INPUTTEST_H_INCLUDED

SourceForge Logo hosts this site. Send comments to:
CppUnit Developers