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__