Line data Source code
1 : // (C) Copyright Gennadiy Rozental 2001.
2 : // Distributed under the Boost Software License, Version 1.0.
3 : // (See accompanying file LICENSE_1_0.txt or copy at
4 : // http://www.boost.org/LICENSE_1_0.txt)
5 :
6 : // See http://www.boost.org/libs/test for the library home page.
7 : //
8 : //!@file
9 : //!@brief Collection comparison with enhanced reporting
10 : // ***************************************************************************
11 :
12 : #ifndef BOOST_TEST_TOOLS_COLLECTION_COMPARISON_OP_HPP_050815GER
13 : #define BOOST_TEST_TOOLS_COLLECTION_COMPARISON_OP_HPP_050815GER
14 :
15 : // Boost.Test
16 : #include <boost/test/tools/assertion.hpp>
17 :
18 : #include <boost/test/utils/is_forward_iterable.hpp>
19 : #include <boost/test/utils/is_cstring.hpp>
20 :
21 : // Boost
22 : #include <boost/mpl/bool.hpp>
23 : #include <boost/mpl/if.hpp>
24 : #include <boost/utility/enable_if.hpp>
25 : #include <boost/type_traits/decay.hpp>
26 :
27 : #include <boost/test/detail/suppress_warnings.hpp>
28 :
29 : //____________________________________________________________________________//
30 :
31 : namespace boost {
32 : namespace test_tools {
33 : namespace assertion {
34 :
35 : // ************************************************************************** //
36 : // ************* selectors for specialized comparizon routines ************** //
37 : // ************************************************************************** //
38 :
39 : template<typename T>
40 : struct specialized_compare : public mpl::false_ {};
41 :
42 : template <typename T>
43 : struct is_c_array : public mpl::false_ {};
44 :
45 : template<typename T, std::size_t N>
46 : struct is_c_array<T [N]> : public mpl::true_ {};
47 :
48 : template<typename T, std::size_t N>
49 : struct is_c_array<T (&)[N]> : public mpl::true_ {};
50 :
51 : #define BOOST_TEST_SPECIALIZED_COLLECTION_COMPARE(Col) \
52 : namespace boost { namespace test_tools { namespace assertion { \
53 : template<> \
54 : struct specialized_compare<Col> : public mpl::true_ {}; \
55 : }}} \
56 : /**/
57 :
58 : // ************************************************************************** //
59 : // ************** lexicographic_compare ************** //
60 : // ************************************************************************** //
61 :
62 : namespace op {
63 :
64 : template <typename OP, bool can_be_equal, bool prefer_shorter,
65 : typename Lhs, typename Rhs>
66 : inline
67 : typename boost::enable_if_c<
68 : unit_test::is_forward_iterable<Lhs>::value && !unit_test::is_cstring<Lhs>::value
69 : && unit_test::is_forward_iterable<Rhs>::value && !unit_test::is_cstring<Rhs>::value,
70 : assertion_result>::type
71 : lexicographic_compare( Lhs const& lhs, Rhs const& rhs )
72 : {
73 : assertion_result ar( true );
74 :
75 : typedef unit_test::bt_iterator_traits<Lhs> t_Lhs_iterator;
76 : typedef unit_test::bt_iterator_traits<Rhs> t_Rhs_iterator;
77 :
78 : typename t_Lhs_iterator::const_iterator first1 = t_Lhs_iterator::begin(lhs);
79 : typename t_Rhs_iterator::const_iterator first2 = t_Rhs_iterator::begin(rhs);
80 : typename t_Lhs_iterator::const_iterator last1 = t_Lhs_iterator::end(lhs);
81 : typename t_Rhs_iterator::const_iterator last2 = t_Rhs_iterator::end(rhs);
82 : std::size_t pos = 0;
83 :
84 : for( ; (first1 != last1) && (first2 != last2); ++first1, ++first2, ++pos ) {
85 : assertion_result const& element_ar = OP::eval(*first1, *first2);
86 : if( !can_be_equal && element_ar )
87 : return ar; // a < b
88 :
89 : assertion_result const& reverse_ar = OP::eval(*first2, *first1);
90 : if( element_ar && !reverse_ar )
91 : return ar; // a<=b and !(b<=a) => a < b => return true
92 :
93 : if( element_ar || !reverse_ar ) {
94 : continue; // (a<=b and b<=a) or (!(a<b) and !(b<a)) => a == b => keep looking
95 : }
96 :
97 : // !(a<=b) and b<=a => b < a => return false
98 : ar = false;
99 : ar.message() << "\nFailure at position " << pos << ":";
100 : ar.message() << "\n - condition [" << tt_detail::print_helper(*first1) << OP::forward() << tt_detail::print_helper(*first2) << "] is false";
101 : if(!element_ar.has_empty_message())
102 : ar.message() << ": " << element_ar.message();
103 : ar.message() << "\n - inverse condition [" << tt_detail::print_helper(*first2) << OP::forward() << tt_detail::print_helper(*first1) << "] is true";
104 : if(!reverse_ar.has_empty_message())
105 : ar.message() << ": " << reverse_ar.message();
106 : return ar;
107 : }
108 :
109 : if( first1 != last1 ) {
110 : if( prefer_shorter ) {
111 : ar = false;
112 : ar.message() << "\nFirst collection has extra trailing elements.";
113 : }
114 : }
115 : else if( first2 != last2 ) {
116 : if( !prefer_shorter ) {
117 : ar = false;
118 : ar.message() << "\nSecond collection has extra trailing elements.";
119 : }
120 : }
121 : else if( !can_be_equal ) {
122 : ar = false;
123 : ar.message() << "\nCollections appear to be equal.";
124 : }
125 :
126 : return ar;
127 : }
128 :
129 : template <typename OP, bool can_be_equal, bool prefer_shorter,
130 : typename Lhs, typename Rhs>
131 : inline
132 : typename boost::enable_if_c<
133 : (unit_test::is_cstring<Lhs>::value || unit_test::is_cstring<Rhs>::value),
134 : assertion_result>::type
135 : lexicographic_compare( Lhs const& lhs, Rhs const& rhs )
136 : {
137 : typedef typename unit_test::deduce_cstring_transform<Lhs>::type lhs_char_type;
138 : typedef typename unit_test::deduce_cstring_transform<Rhs>::type rhs_char_type;
139 :
140 : return lexicographic_compare<OP, can_be_equal, prefer_shorter>(
141 : lhs_char_type(lhs),
142 : rhs_char_type(rhs));
143 : }
144 :
145 : //____________________________________________________________________________//
146 :
147 : // ************************************************************************** //
148 : // ************** equality_compare ************** //
149 : // ************************************************************************** //
150 :
151 : template <typename OP, typename Lhs, typename Rhs>
152 : inline
153 : typename boost::enable_if_c<
154 : unit_test::is_forward_iterable<Lhs>::value && !unit_test::is_cstring<Lhs>::value
155 : && unit_test::is_forward_iterable<Rhs>::value && !unit_test::is_cstring<Rhs>::value,
156 : assertion_result>::type
157 : element_compare( Lhs const& lhs, Rhs const& rhs )
158 : {
159 : typedef unit_test::bt_iterator_traits<Lhs> t_Lhs_iterator;
160 : typedef unit_test::bt_iterator_traits<Rhs> t_Rhs_iterator;
161 :
162 : assertion_result ar( true );
163 :
164 : if( t_Lhs_iterator::size(lhs) != t_Rhs_iterator::size(rhs) ) {
165 : ar = false;
166 : ar.message() << "\nCollections size mismatch: " << t_Lhs_iterator::size(lhs) << " != " << t_Rhs_iterator::size(rhs);
167 : return ar;
168 : }
169 :
170 : typename t_Lhs_iterator::const_iterator left = t_Lhs_iterator::begin(lhs);
171 : typename t_Rhs_iterator::const_iterator right = t_Rhs_iterator::begin(rhs);
172 : std::size_t pos = 0;
173 :
174 : for( ; pos < t_Lhs_iterator::size(lhs); ++left, ++right, ++pos ) {
175 : assertion_result const element_ar = OP::eval( *left, *right );
176 : if( element_ar )
177 : continue;
178 :
179 : ar = false;
180 : ar.message() << "\n - mismatch at position " << pos << ": ["
181 : << tt_detail::print_helper(*left)
182 : << OP::forward()
183 : << tt_detail::print_helper(*right)
184 : << "] is false";
185 : if(!element_ar.has_empty_message())
186 : ar.message() << ": " << element_ar.message();
187 : }
188 :
189 : return ar;
190 : }
191 :
192 : // In case string comparison is branching here
193 : template <typename OP, typename Lhs, typename Rhs>
194 : inline
195 : typename boost::enable_if_c<
196 : (unit_test::is_cstring<Lhs>::value || unit_test::is_cstring<Rhs>::value),
197 : assertion_result>::type
198 : element_compare( Lhs const& lhs, Rhs const& rhs )
199 : {
200 : typedef typename unit_test::deduce_cstring_transform<Lhs>::type lhs_char_type;
201 : typedef typename unit_test::deduce_cstring_transform<Rhs>::type rhs_char_type;
202 :
203 : return element_compare<OP>(lhs_char_type(lhs),
204 : rhs_char_type(rhs));
205 : }
206 :
207 : //____________________________________________________________________________//
208 :
209 : // ************************************************************************** //
210 : // ************** non_equality_compare ************** //
211 : // ************************************************************************** //
212 :
213 : template <typename OP, typename Lhs, typename Rhs>
214 : inline assertion_result
215 : non_equality_compare( Lhs const& lhs, Rhs const& rhs )
216 : {
217 : typedef unit_test::bt_iterator_traits<Lhs> t_Lhs_iterator;
218 : typedef unit_test::bt_iterator_traits<Rhs> t_Rhs_iterator;
219 :
220 : assertion_result ar( true );
221 :
222 : if( t_Lhs_iterator::size(lhs) != t_Rhs_iterator::size(rhs) )
223 : return ar;
224 :
225 : typename t_Lhs_iterator::const_iterator left = t_Lhs_iterator::begin(lhs);
226 : typename t_Rhs_iterator::const_iterator right = t_Rhs_iterator::begin(rhs);
227 : typename t_Lhs_iterator::const_iterator end = t_Lhs_iterator::end(lhs);
228 :
229 : for( ; left != end; ++left, ++right ) {
230 : if( OP::eval( *left, *right ) )
231 : return ar;
232 : }
233 :
234 : ar = false;
235 : ar.message() << "\nCollections appear to be equal";
236 :
237 : return ar;
238 : }
239 :
240 : //____________________________________________________________________________//
241 :
242 : // ************************************************************************** //
243 : // ************** cctraits ************** //
244 : // ************************************************************************** //
245 : // set of collection comparison traits per comparison OP
246 :
247 : template<typename OP>
248 : struct cctraits;
249 :
250 : template<typename Lhs, typename Rhs>
251 : struct cctraits<op::EQ<Lhs, Rhs> > {
252 : typedef specialized_compare<Lhs> is_specialized;
253 : };
254 :
255 : template<typename Lhs, typename Rhs>
256 : struct cctraits<op::NE<Lhs, Rhs> > {
257 : typedef specialized_compare<Lhs> is_specialized;
258 : };
259 :
260 : template<typename Lhs, typename Rhs>
261 : struct cctraits<op::LT<Lhs, Rhs> > {
262 : static const bool can_be_equal = false;
263 : static const bool prefer_short = true;
264 :
265 : typedef specialized_compare<Lhs> is_specialized;
266 : };
267 :
268 : template<typename Lhs, typename Rhs>
269 : struct cctraits<op::LE<Lhs, Rhs> > {
270 : static const bool can_be_equal = true;
271 : static const bool prefer_short = true;
272 :
273 : typedef specialized_compare<Lhs> is_specialized;
274 : };
275 :
276 : template<typename Lhs, typename Rhs>
277 : struct cctraits<op::GT<Lhs, Rhs> > {
278 : static const bool can_be_equal = false;
279 : static const bool prefer_short = false;
280 :
281 : typedef specialized_compare<Lhs> is_specialized;
282 : };
283 :
284 : template<typename Lhs, typename Rhs>
285 : struct cctraits<op::GE<Lhs, Rhs> > {
286 : static const bool can_be_equal = true;
287 : static const bool prefer_short = false;
288 :
289 : typedef specialized_compare<Lhs> is_specialized;
290 : };
291 :
292 : // ************************************************************************** //
293 : // ************** compare_collections ************** //
294 : // ************************************************************************** //
295 : // Overloaded set of functions dispatching to specific implementation of comparison
296 :
297 : template <typename Lhs, typename Rhs, typename L, typename R>
298 : inline assertion_result
299 : compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::EQ<L, R> >*, mpl::true_ )
300 : {
301 : return assertion::op::element_compare<op::EQ<L, R> >( lhs, rhs );
302 : }
303 :
304 : //____________________________________________________________________________//
305 :
306 : template <typename Lhs, typename Rhs, typename L, typename R>
307 : inline assertion_result
308 8 : compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::EQ<L, R> >*, mpl::false_ )
309 : {
310 8 : return lhs == rhs;
311 : }
312 :
313 : //____________________________________________________________________________//
314 :
315 : template <typename Lhs, typename Rhs, typename L, typename R>
316 : inline assertion_result
317 : compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::NE<L, R> >*, mpl::true_ )
318 : {
319 : return assertion::op::non_equality_compare<op::NE<L, R> >( lhs, rhs );
320 : }
321 :
322 : //____________________________________________________________________________//
323 :
324 : template <typename Lhs, typename Rhs, typename L, typename R>
325 : inline assertion_result
326 : compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::NE<L, R> >*, mpl::false_ )
327 : {
328 : return lhs != rhs;
329 : }
330 :
331 : //____________________________________________________________________________//
332 :
333 : template <typename OP, typename Lhs, typename Rhs>
334 : inline assertion_result
335 : lexicographic_compare( Lhs const& lhs, Rhs const& rhs )
336 : {
337 : return assertion::op::lexicographic_compare<OP, cctraits<OP>::can_be_equal, cctraits<OP>::prefer_short>( lhs, rhs );
338 : }
339 :
340 : //____________________________________________________________________________//
341 :
342 : template <typename Lhs, typename Rhs, typename OP>
343 : inline assertion_result
344 : compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<OP>*, mpl::true_ )
345 : {
346 : return lexicographic_compare<OP>( lhs, rhs );
347 : }
348 :
349 : //____________________________________________________________________________//
350 :
351 : template <typename Lhs, typename Rhs, typename L, typename R>
352 : inline assertion_result
353 : compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::LT<L, R> >*, mpl::false_ )
354 : {
355 : return lhs < rhs;
356 : }
357 :
358 : //____________________________________________________________________________//
359 :
360 : template <typename Lhs, typename Rhs, typename L, typename R>
361 : inline assertion_result
362 : compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::LE<L, R> >*, mpl::false_ )
363 : {
364 : return lhs <= rhs;
365 : }
366 :
367 : //____________________________________________________________________________//
368 :
369 : template <typename Lhs, typename Rhs, typename L, typename R>
370 : inline assertion_result
371 : compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::GT<L, R> >*, mpl::false_ )
372 : {
373 : return lhs > rhs;
374 : }
375 :
376 : //____________________________________________________________________________//
377 :
378 : template <typename Lhs, typename Rhs, typename L, typename R>
379 : inline assertion_result
380 : compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::GE<L, R> >*, mpl::false_ )
381 : {
382 : return lhs >= rhs;
383 : }
384 :
385 : //____________________________________________________________________________//
386 :
387 : // ************************************************************************** //
388 : // ********* specialization of comparison operators for collections ********* //
389 : // ************************************************************************** //
390 :
391 : #define DEFINE_COLLECTION_COMPARISON( oper, name, rev, name_inverse ) \
392 : template<typename Lhs,typename Rhs> \
393 : struct name<Lhs,Rhs,typename boost::enable_if_c< \
394 : unit_test::is_forward_iterable<Lhs>::value \
395 : && !unit_test::is_cstring_comparable<Lhs>::value \
396 : && unit_test::is_forward_iterable<Rhs>::value \
397 : && !unit_test::is_cstring_comparable<Rhs>::value>::type> { \
398 : public: \
399 : typedef assertion_result result_type; \
400 : typedef name_inverse<Lhs, Rhs> inverse; \
401 : typedef unit_test::bt_iterator_traits<Lhs> t_Lhs_iterator_helper; \
402 : typedef unit_test::bt_iterator_traits<Rhs> t_Rhs_iterator_helper; \
403 : \
404 : typedef name<Lhs, Rhs> OP; \
405 : \
406 : typedef typename \
407 : mpl::if_c< \
408 : mpl::or_< \
409 : typename is_c_array<Lhs>::type, \
410 : typename is_c_array<Rhs>::type \
411 : >::value, \
412 : mpl::true_, \
413 : typename \
414 : mpl::if_c<is_same<typename decay<Lhs>::type, \
415 : typename decay<Rhs>::type>::value, \
416 : typename cctraits<OP>::is_specialized, \
417 : mpl::false_>::type \
418 : >::type is_specialized; \
419 : \
420 : typedef name<typename t_Lhs_iterator_helper::value_type, \
421 : typename t_Rhs_iterator_helper::value_type \
422 : > elem_op; \
423 : \
424 : static assertion_result \
425 : eval( Lhs const& lhs, Rhs const& rhs) \
426 : { \
427 : return assertion::op::compare_collections( lhs, rhs, \
428 : (boost::type<elem_op>*)0, \
429 : is_specialized() ); \
430 : } \
431 : \
432 : template<typename PrevExprType> \
433 : static void \
434 : report( std::ostream&, \
435 : PrevExprType const&, \
436 : Rhs const& ) {} \
437 : \
438 : static char const* forward() \
439 : { return " " #oper " "; } \
440 : static char const* revert() \
441 : { return " " #rev " "; } \
442 : \
443 : }; \
444 : /**/
445 :
446 8 : BOOST_TEST_FOR_EACH_COMP_OP( DEFINE_COLLECTION_COMPARISON )
447 : #undef DEFINE_COLLECTION_COMPARISON
448 :
449 : //____________________________________________________________________________//
450 :
451 : } // namespace op
452 : } // namespace assertion
453 : } // namespace test_tools
454 : } // namespace boost
455 :
456 : #include <boost/test/detail/enable_warnings.hpp>
457 :
458 : #endif // BOOST_TEST_TOOLS_COLLECTION_COMPARISON_OP_HPP_050815GER
|