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
31namespace boost {
32namespace test_tools {
33namespace assertion {
34
35// ************************************************************************** //
36// ************* selectors for specialized comparizon routines ************** //
37// ************************************************************************** //
38
39template<typename T>
40struct specialized_compare : public mpl::false_ {};
41
42template <typename T>
43struct is_c_array : public mpl::false_ {};
44
45template<typename T, std::size_t N>
46struct is_c_array<T [N]> : public mpl::true_ {};
47
48template<typename T, std::size_t N>
49struct is_c_array<T (&)[N]> : public mpl::true_ {};
50
51#define BOOST_TEST_SPECIALIZED_COLLECTION_COMPARE(Col) \
52namespace boost { namespace test_tools { namespace assertion { \
53template<> \
54struct specialized_compare<Col> : public mpl::true_ {}; \
55}}} \
56/**/
57
58// ************************************************************************** //
59// ************** lexicographic_compare ************** //
60// ************************************************************************** //
61
62namespace op {
63
64template <typename OP, bool can_be_equal, bool prefer_shorter,
65 typename Lhs, typename Rhs>
66inline
67typename 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
71lexicographic_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
129template <typename OP, bool can_be_equal, bool prefer_shorter,
130 typename Lhs, typename Rhs>
131inline
132typename boost::enable_if_c<
133 (unit_test::is_cstring<Lhs>::value || unit_test::is_cstring<Rhs>::value),
134 assertion_result>::type
135lexicographic_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
151template <typename OP, typename Lhs, typename Rhs>
152inline
153typename 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
157element_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
193template <typename OP, typename Lhs, typename Rhs>
194inline
195typename boost::enable_if_c<
196 (unit_test::is_cstring<Lhs>::value || unit_test::is_cstring<Rhs>::value),
197 assertion_result>::type
198element_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
213template <typename OP, typename Lhs, typename Rhs>
214inline assertion_result
215non_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
247template<typename OP>
248struct cctraits;
249
250template<typename Lhs, typename Rhs>
251struct cctraits<op::EQ<Lhs, Rhs> > {
252 typedef specialized_compare<Lhs> is_specialized;
253};
254
255template<typename Lhs, typename Rhs>
256struct cctraits<op::NE<Lhs, Rhs> > {
257 typedef specialized_compare<Lhs> is_specialized;
258};
259
260template<typename Lhs, typename Rhs>
261struct 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
268template<typename Lhs, typename Rhs>
269struct 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
276template<typename Lhs, typename Rhs>
277struct 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
284template<typename Lhs, typename Rhs>
285struct 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
297template <typename Lhs, typename Rhs, typename L, typename R>
298inline assertion_result
299compare_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
306template <typename Lhs, typename Rhs, typename L, typename R>
307inline assertion_result
308compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::EQ<L, R> >*, mpl::false_ )
309{
310 return lhs == rhs;
311}
312
313//____________________________________________________________________________//
314
315template <typename Lhs, typename Rhs, typename L, typename R>
316inline assertion_result
317compare_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
324template <typename Lhs, typename Rhs, typename L, typename R>
325inline assertion_result
326compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::NE<L, R> >*, mpl::false_ )
327{
328 return lhs != rhs;
329}
330
331//____________________________________________________________________________//
332
333template <typename OP, typename Lhs, typename Rhs>
334inline assertion_result
335lexicographic_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
342template <typename Lhs, typename Rhs, typename OP>
343inline assertion_result
344compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<OP>*, mpl::true_ )
345{
346 return lexicographic_compare<OP>( lhs, rhs );
347}
348
349//____________________________________________________________________________//
350
351template <typename Lhs, typename Rhs, typename L, typename R>
352inline assertion_result
353compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::LT<L, R> >*, mpl::false_ )
354{
355 return lhs < rhs;
356}
357
358//____________________________________________________________________________//
359
360template <typename Lhs, typename Rhs, typename L, typename R>
361inline assertion_result
362compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::LE<L, R> >*, mpl::false_ )
363{
364 return lhs <= rhs;
365}
366
367//____________________________________________________________________________//
368
369template <typename Lhs, typename Rhs, typename L, typename R>
370inline assertion_result
371compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::GT<L, R> >*, mpl::false_ )
372{
373 return lhs > rhs;
374}
375
376//____________________________________________________________________________//
377
378template <typename Lhs, typename Rhs, typename L, typename R>
379inline assertion_result
380compare_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 ) \
392template<typename Lhs,typename Rhs> \
393struct 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> { \
398public: \
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
446BOOST_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
459

source code of include/boost/test/tools/collection_comparison_op.hpp