File indexing completed on 2024-09-22 04:53:11

0001 #ifndef __MOCKITOPP_DYNAMIC_VFUNCTION_HPP__
0002 #define __MOCKITOPP_DYNAMIC_VFUNCTION_HPP__
0003 
0004 #include <algorithm>
0005 #include <list>
0006 
0007 #include <mockitopp/exceptions.hpp>
0008 #include <mockitopp/detail/stubbing/action.hpp>
0009 #include <mockitopp/detail/util/pointers.hpp>
0010 #include <mockitopp/detail/util/tr1_tuple.hpp>
0011 #include <mockitopp/matchers/Matcher.hpp>
0012 
0013 include(`mockitopp/detail/m4/ENUM_BINARY_PARAMS.m4')dnl
0014 include(`mockitopp/detail/m4/ENUM_PARAMS.m4')dnl
0015 include(`mockitopp/detail/m4/ENUM_TRAILING_PARAMS.m4')dnl
0016 include(`mockitopp/detail/m4/IF.m4')dnl
0017 include(`mockitopp/detail/m4/REPEAT.m4')dnl
0018 // TODO: add documentation
0019 namespace mockitopp
0020 {
0021    namespace detail
0022    {
0023       struct dynamic_vfunction_base
0024       {
0025          int calls;
0026 
0027          // allow polymorphic desctruction with unknown subtype
0028          virtual ~dynamic_vfunction_base() {}
0029 
0030          /**
0031           * verify method is called within a specified range
0032           *
0033           * @param min minimum times method should be called
0034           * @param max maximum times method should be called
0035           */
0036          bool between(int min, int max) const
0037          {
0038             if(calls >= min && calls <= max)
0039                { return true; }
0040             return false;
0041          }
0042 
0043          /**
0044           * verify method is called at least (n) times
0045           *
0046           * @param times minimum number of times method should be called
0047           */
0048          bool atLeast(int times) const
0049             { return calls >= times; }
0050 
0051          /**
0052           * verify method is called at most (n) times
0053           *
0054           * @param times maximum number of times method should be called
0055           */
0056          bool atMost(int times) const
0057             { return calls <= times; }
0058 
0059          /**
0060           * verify method is called exactly (n) times
0061           *
0062           * @param times exact number of times method should be called
0063           */
0064          bool exactly(int times) const
0065             { return calls == times; }
0066 
0067          /**
0068           * verify method is never called
0069           */
0070          bool never() const
0071             { return calls == 0; }
0072       };
0073 
0074       template <typename R>
0075       struct dynamic_vfunction_progress : dynamic_vfunction_base
0076       {
0077          typedef shared_ptr<action<R> > action_type;
0078          typedef std::list<action_type> action_queue_type;
0079 
0080          action_queue_type* stubbing_progress;
0081 
0082          dynamic_vfunction_progress& thenReturn(R value)
0083          {
0084             stubbing_progress->push_back(action_type(new returnable_action<R>(value)));
0085             return *this;
0086          }
0087 
0088          template <typename T>
0089          dynamic_vfunction_progress& thenThrow(T throwable)
0090          {
0091             stubbing_progress->push_back(action_type(new throwable_action<R, T>(throwable)));
0092             return *this;
0093          }
0094       };
0095 
0096       template <>
0097       struct dynamic_vfunction_progress<void> : dynamic_vfunction_base
0098       {
0099          typedef shared_ptr<action<void> > action_type;
0100          typedef std::list<action_type>    action_queue_type;
0101 
0102          action_queue_type* stubbing_progress;
0103 
0104          dynamic_vfunction_progress& thenReturn()
0105          {
0106             stubbing_progress->push_back(action_type(new returnable_action<void>()));
0107             return *this;
0108          }
0109 
0110          template <typename T>
0111          dynamic_vfunction_progress& thenThrow(T throwable)
0112          {
0113             stubbing_progress->push_back(action_type(new throwable_action<void, T>(throwable)));
0114             return *this;
0115          }
0116       };
0117 
0118       template <typename K, typename V>
0119       struct map_entry
0120       {
0121          K key;
0122          V val;
0123 
0124          map_entry(const K& k, const V& v) : key(k), val(v) {}
0125 
0126          template <typename K2, typename V2>
0127             bool operator== (const map_entry<K2, V2>& rhs) const
0128             { return key == rhs.key; }
0129 
0130          template <typename T>
0131             bool operator== (const T& rhs) const
0132             { return key == rhs; }
0133       };
0134 
0135       template <typename T>
0136       struct matcher_element
0137       {
0138          matcher::Matcher<T>* matcher;
0139   
0140          matcher_element(const matcher::Matcher<T>& _matcher)
0141             : matcher(_matcher.clone())
0142             {}
0143 
0144          matcher_element(const matcher_element& rhs)
0145             : matcher(rhs.matcher->clone())
0146             {}
0147 
0148          ~matcher_element()
0149             { delete matcher; }
0150 
0151          bool operator== (typename tr1::add_reference<typename tr1::add_const<T>::type>::type rhs) const
0152             { return (*matcher == rhs); }
0153 
0154          bool operator== (const matcher_element& rhs) const
0155             { return (matcher == rhs.matcher); }
0156       };
0157 
0158       template <typename T> struct dynamic_vfunction;
0159 
0160       // TODO: clean up impl
0161       // TODO: add sequence matcher
0162 
0163       // TODO: clean up typedef nomenclature
0164 define(`DEFINE_DYNAMIC_VFUNCTION', `
0165       // $1 arity template
0166       template <typename R, typename C`'M4_ENUM_TRAILING_PARAMS($1, typename A)>
0167       struct dynamic_vfunction<R (C::*)(M4_ENUM_PARAMS($1, A))> : private dynamic_vfunction_progress<R>
0168       {
0169          typedef tr1::tuple<M4_ENUM_PARAMS($1, A)> exact_tuple_type;
0170          typedef tr1::tuple<M4_ENUM_BINARY_PARAMS($1, matcher_element<A, >, M4_INTERCEPT) > fuzzy_tuple_type;
0171 
0172          typedef typename dynamic_vfunction_progress<R>::action_type       action_type;
0173          typedef typename dynamic_vfunction_progress<R>::action_queue_type action_queue_type;
0174 
0175          std::list<map_entry<exact_tuple_type, action_queue_type> > exact_matches;
0176          std::list<map_entry<fuzzy_tuple_type, action_queue_type> > fuzzy_matches;
0177          std::list<map_entry<exact_tuple_type, int> >               args_to_calls;
0178 
0179          dynamic_vfunction()
0180             : dynamic_vfunction_progress<R>()
0181             , exact_matches()
0182             , fuzzy_matches()
0183             {}
0184 
0185          template <typename T>
0186          int calculate_calls_for_arguments(const T args) {
0187             int calls = 0;
0188             typename std::list<map_entry<exact_tuple_type, int> >::iterator calls_it
0189                = args_to_calls.begin();
0190             for(; calls_it != args_to_calls.end(); calls_it++) {
0191                if(args == calls_it->key) {
0192                   calls += calls_it->val;
0193                }
0194             }
0195             return calls;
0196          }
0197 
0198          M4_IF($1, `dynamic_vfunction_progress<R>& when(M4_ENUM_BINARY_PARAMS($1, const matcher::Matcher<A, >& a))
0199          {
0200             const fuzzy_tuple_type args = fuzzy_tuple_type(M4_ENUM_PARAMS($1, a));
0201             typename std::list<map_entry<fuzzy_tuple_type, action_queue_type> >::iterator match
0202                = std::find(fuzzy_matches.begin(), fuzzy_matches.end(), args);
0203             if(match == fuzzy_matches.end())
0204             {
0205                fuzzy_matches.push_back(map_entry<fuzzy_tuple_type, action_queue_type>(args, action_queue_type()));
0206                match = --fuzzy_matches.end();
0207             }
0208             this->calls = calculate_calls_for_arguments(args);
0209             this->stubbing_progress = &(match->val);
0210             return *this;
0211          }',)
0212 
0213          dynamic_vfunction_progress<R>& when(M4_ENUM_BINARY_PARAMS($1, A, a))
0214          {
0215             const exact_tuple_type args = exact_tuple_type(M4_ENUM_PARAMS($1, a));
0216             typename std::list<map_entry<exact_tuple_type, action_queue_type> >::iterator match
0217                = std::find(exact_matches.begin(), exact_matches.end(), args);
0218             if(match == exact_matches.end())
0219             {
0220                exact_matches.push_back(map_entry<exact_tuple_type, action_queue_type>(args, action_queue_type()));
0221                match = --exact_matches.end();
0222             }
0223             this->calls = calculate_calls_for_arguments(args);
0224             this->stubbing_progress = &(match->val);
0225             return *this;
0226          }
0227 
0228          R invoke(M4_ENUM_BINARY_PARAMS($1, A, a))
0229          {
0230             const exact_tuple_type args = exact_tuple_type(M4_ENUM_PARAMS($1, a));
0231 
0232             typename std::list<map_entry<exact_tuple_type, int> >::iterator calls_it
0233                = std::find(args_to_calls.begin(), args_to_calls.end(), args);
0234             if(calls_it == args_to_calls.end()) {
0235                args_to_calls.push_back(map_entry<exact_tuple_type, int>(args, 1));
0236             } else {
0237                (calls_it->val)++;
0238             }
0239 
0240             action_queue_type* actions = 0;
0241             typename std::list<map_entry<exact_tuple_type, action_queue_type> >::iterator exact_match
0242                = std::find(exact_matches.begin(), exact_matches.end(), args);
0243             if(exact_match != exact_matches.end())
0244                { actions = &(exact_match->val); }
0245             if(!actions)
0246             {
0247                typename std::list<map_entry<fuzzy_tuple_type, action_queue_type> >::iterator fuzzy_match
0248                   = std::find(fuzzy_matches.begin(), fuzzy_matches.end(), args);
0249                if(fuzzy_match == fuzzy_matches.end())
0250                    { throw partial_implementation_exception(); }
0251                actions = &(fuzzy_match->val);
0252             }
0253             action_type action = actions->front();
0254             if(actions->size() > 1)
0255                { actions->pop_front(); }
0256             return action->invoke();
0257          }
0258       };
0259 ')dnl
0260 dnl add one to max arity so we generate 0 argument case
0261 M4_REPEAT(eval(MOCKITOPP_MAX_VIRTUAL_FUNCTION_ARITY + 1), `DEFINE_DYNAMIC_VFUNCTION')dnl
0262    } // namespace detail
0263 } // namespace mockitopp
0264 
0265 #endif //__MOCKITOPP_DYNAMIC_VFUNCTION_HPP__