420 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			420 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | //  (C) Copyright Gennadiy Rozental 2001. | ||
|  | //  Distributed under the Boost Software License, Version 1.0. | ||
|  | //  (See accompanying file LICENSE_1_0.txt or copy at | ||
|  | //  http://www.boost.org/LICENSE_1_0.txt) | ||
|  | 
 | ||
|  | //  See http://www.boost.org/libs/test for the library home page. | ||
|  | // | ||
|  | //!@file | ||
|  | //!@brief Collection comparison with enhanced reporting | ||
|  | // *************************************************************************** | ||
|  | 
 | ||
|  | #ifndef BOOST_TEST_TOOLS_COLLECTION_COMPARISON_OP_HPP_050815GER | ||
|  | #define BOOST_TEST_TOOLS_COLLECTION_COMPARISON_OP_HPP_050815GER | ||
|  | 
 | ||
|  | // Boost.Test | ||
|  | #include <boost/test/tools/assertion.hpp> | ||
|  | 
 | ||
|  | #include <boost/test/utils/is_forward_iterable.hpp> | ||
|  | #include <boost/test/utils/is_cstring.hpp> | ||
|  | 
 | ||
|  | // Boost | ||
|  | #include <boost/mpl/bool.hpp> | ||
|  | #include <boost/utility/enable_if.hpp> | ||
|  | #include <boost/type_traits/decay.hpp> | ||
|  | 
 | ||
|  | #include <boost/test/detail/suppress_warnings.hpp> | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | namespace boost { | ||
|  | namespace test_tools { | ||
|  | namespace assertion { | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // ************* selectors for specialized comparizon routines ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | template<typename T> | ||
|  | struct specialized_compare : public mpl::false_ {}; | ||
|  | 
 | ||
|  | #define BOOST_TEST_SPECIALIZED_COLLECTION_COMPARE(Col)          \ | ||
|  | namespace boost { namespace test_tools { namespace assertion {  \ | ||
|  | template<>                                                      \ | ||
|  | struct specialized_compare<Col> : public mpl::true_ {};         \ | ||
|  | }}}                                                             \ | ||
|  | /**/ | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************            lexicographic_compare             ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | namespace op { | ||
|  | 
 | ||
|  | template <typename OP, bool can_be_equal, bool prefer_shorter, | ||
|  |           typename Lhs, typename Rhs> | ||
|  | inline | ||
|  | typename boost::enable_if_c< | ||
|  |     unit_test::is_forward_iterable<Lhs>::value && unit_test::is_forward_iterable<Rhs>::value, | ||
|  |     assertion_result>::type | ||
|  | lexicographic_compare( Lhs const& lhs, Rhs const& rhs ) | ||
|  | { | ||
|  |     assertion_result ar( true ); | ||
|  | 
 | ||
|  |     typename Lhs::const_iterator first1 = lhs.begin(); | ||
|  |     typename Rhs::const_iterator first2 = rhs.begin(); | ||
|  |     typename Lhs::const_iterator last1  = lhs.end(); | ||
|  |     typename Rhs::const_iterator last2  = rhs.end(); | ||
|  |     std::size_t                  pos    = 0; | ||
|  | 
 | ||
|  |     for( ; (first1 != last1) && (first2 != last2); ++first1, ++first2, ++pos ) { | ||
|  |         assertion_result const& element_ar = OP::eval(*first1, *first2); | ||
|  |         if( !can_be_equal && element_ar ) | ||
|  |             return ar; // a < b | ||
|  | 
 | ||
|  |         assertion_result const& reverse_ar = OP::eval(*first2, *first1); | ||
|  |         if( element_ar && !reverse_ar ) | ||
|  |             return ar; // a<=b and !(b<=a) => a < b => return true | ||
|  | 
 | ||
|  |         if( element_ar || !reverse_ar ) | ||
|  |             continue; // (a<=b and b<=a) or (!(a<b) and !(b<a)) => a == b => keep looking | ||
|  | 
 | ||
|  |         // !(a<=b) and b<=a => b < a => return false | ||
|  |         ar = false; | ||
|  |         ar.message() << "\nFailure at position " << pos << ": " | ||
|  |                      << tt_detail::print_helper(*first1) | ||
|  |                      << OP::revert() | ||
|  |                      << tt_detail::print_helper(*first2) | ||
|  |                      << ". " << element_ar.message(); | ||
|  |         return ar; | ||
|  |     } | ||
|  | 
 | ||
|  |     if( first1 != last1 ) { | ||
|  |         if( prefer_shorter ) { | ||
|  |             ar = false; | ||
|  |             ar.message() << "\nFirst collection has extra trailing elements."; | ||
|  |         } | ||
|  |     } | ||
|  |     else if( first2 != last2 ) { | ||
|  |         if( !prefer_shorter ) { | ||
|  |             ar = false; | ||
|  |             ar.message() << "\nSecond collection has extra trailing elements."; | ||
|  |         } | ||
|  |     } | ||
|  |     else if( !can_be_equal ) { | ||
|  |         ar = false; | ||
|  |         ar.message() << "\nCollections appear to be equal."; | ||
|  |     } | ||
|  | 
 | ||
|  |     return ar; | ||
|  | } | ||
|  | 
 | ||
|  | template <typename OP, bool can_be_equal, bool prefer_shorter, | ||
|  |           typename Lhs, typename Rhs> | ||
|  | inline | ||
|  | typename boost::enable_if_c< | ||
|  |     (!unit_test::is_forward_iterable<Lhs>::value && unit_test::is_cstring<Lhs>::value) || | ||
|  |     (!unit_test::is_forward_iterable<Rhs>::value && unit_test::is_cstring<Rhs>::value), | ||
|  |     assertion_result>::type | ||
|  | lexicographic_compare( Lhs const& lhs, Rhs const& rhs ) | ||
|  | { | ||
|  |     typedef typename unit_test::deduce_cstring<Lhs>::type lhs_char_type; | ||
|  |     typedef typename unit_test::deduce_cstring<Rhs>::type rhs_char_type; | ||
|  | 
 | ||
|  |     return lexicographic_compare<OP, can_be_equal, prefer_shorter>( | ||
|  |         boost::unit_test::basic_cstring<lhs_char_type>(lhs), | ||
|  |         boost::unit_test::basic_cstring<rhs_char_type>(rhs)); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************               equality_compare               ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | template <typename OP, typename Lhs, typename Rhs> | ||
|  | inline | ||
|  | typename boost::enable_if_c< | ||
|  |     unit_test::is_forward_iterable<Lhs>::value && unit_test::is_forward_iterable<Rhs>::value, | ||
|  |     assertion_result>::type | ||
|  | element_compare( Lhs const& lhs, Rhs const& rhs ) | ||
|  | { | ||
|  |     assertion_result ar( true ); | ||
|  | 
 | ||
|  |     if( lhs.size() != rhs.size() ) { | ||
|  |         ar = false; | ||
|  |         ar.message() << "\nCollections size mismatch: " << lhs.size() << " != " << rhs.size(); | ||
|  |         return ar; | ||
|  |     } | ||
|  | 
 | ||
|  |     typename Lhs::const_iterator left  = lhs.begin(); | ||
|  |     typename Rhs::const_iterator right = rhs.begin(); | ||
|  |     std::size_t                  pos   = 0; | ||
|  | 
 | ||
|  |     for( ; pos < lhs.size(); ++left, ++right, ++pos ) { | ||
|  |         assertion_result const element_ar = OP::eval( *left, *right ); | ||
|  |         if( element_ar ) | ||
|  |             continue; | ||
|  | 
 | ||
|  |         ar = false; | ||
|  |         ar.message() << "\nMismatch at position " << pos << ": " | ||
|  |                      << tt_detail::print_helper(*left) | ||
|  |                      << OP::revert() | ||
|  |                      << tt_detail::print_helper(*right) | ||
|  |                      << ". " << element_ar.message(); | ||
|  |     } | ||
|  | 
 | ||
|  |     return ar; | ||
|  | } | ||
|  | 
 | ||
|  | // In case string comparison is branching here | ||
|  | template <typename OP, typename Lhs, typename Rhs> | ||
|  | inline | ||
|  | typename boost::enable_if_c< | ||
|  |     (!unit_test::is_forward_iterable<Lhs>::value && unit_test::is_cstring<Lhs>::value) || | ||
|  |     (!unit_test::is_forward_iterable<Rhs>::value && unit_test::is_cstring<Rhs>::value), | ||
|  |     assertion_result>::type | ||
|  | element_compare( Lhs const& lhs, Rhs const& rhs ) | ||
|  | { | ||
|  |     typedef typename unit_test::deduce_cstring<Lhs>::type lhs_char_type; | ||
|  |     typedef typename unit_test::deduce_cstring<Rhs>::type rhs_char_type; | ||
|  | 
 | ||
|  |     return element_compare<OP>(boost::unit_test::basic_cstring<lhs_char_type>(lhs), | ||
|  |                                boost::unit_test::basic_cstring<rhs_char_type>(rhs)); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************             non_equality_compare             ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | template <typename OP, typename Lhs, typename Rhs> | ||
|  | inline assertion_result | ||
|  | non_equality_compare( Lhs const& lhs, Rhs const& rhs ) | ||
|  | { | ||
|  |     assertion_result ar( true ); | ||
|  | 
 | ||
|  |     if( lhs.size() != rhs.size() ) | ||
|  |         return ar; | ||
|  | 
 | ||
|  |     typename Lhs::const_iterator left  = lhs.begin(); | ||
|  |     typename Rhs::const_iterator right = rhs.begin(); | ||
|  |     typename Lhs::const_iterator end   = lhs.end(); | ||
|  | 
 | ||
|  |     for( ; left != end; ++left, ++right ) { | ||
|  |         if( OP::eval( *left, *right ) ) | ||
|  |             return ar; | ||
|  |     } | ||
|  | 
 | ||
|  |     ar = false; | ||
|  |     ar.message() << "\nCollections appear to be equal"; | ||
|  | 
 | ||
|  |     return ar; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************                   cctraits                   ************** // | ||
|  | // ************************************************************************** // | ||
|  | // set of collection comparison traits per comparison OP | ||
|  | 
 | ||
|  | template<typename OP> | ||
|  | struct cctraits; | ||
|  | 
 | ||
|  | template<typename Lhs, typename Rhs> | ||
|  | struct cctraits<op::EQ<Lhs, Rhs> > { | ||
|  |     typedef specialized_compare<Lhs> is_specialized; | ||
|  | }; | ||
|  | 
 | ||
|  | template<typename Lhs, typename Rhs> | ||
|  | struct cctraits<op::NE<Lhs, Rhs> > { | ||
|  |     typedef specialized_compare<Lhs> is_specialized; | ||
|  | }; | ||
|  | 
 | ||
|  | template<typename Lhs, typename Rhs> | ||
|  | struct cctraits<op::LT<Lhs, Rhs> > { | ||
|  |     static const bool can_be_equal = false; | ||
|  |     static const bool prefer_short = true; | ||
|  | 
 | ||
|  |     typedef specialized_compare<Lhs> is_specialized; | ||
|  | }; | ||
|  | 
 | ||
|  | template<typename Lhs, typename Rhs> | ||
|  | struct cctraits<op::LE<Lhs, Rhs> > { | ||
|  |     static const bool can_be_equal = true; | ||
|  |     static const bool prefer_short = true; | ||
|  | 
 | ||
|  |     typedef specialized_compare<Lhs> is_specialized; | ||
|  | }; | ||
|  | 
 | ||
|  | template<typename Lhs, typename Rhs> | ||
|  | struct cctraits<op::GT<Lhs, Rhs> > { | ||
|  |     static const bool can_be_equal = false; | ||
|  |     static const bool prefer_short = false; | ||
|  | 
 | ||
|  |     typedef specialized_compare<Lhs> is_specialized; | ||
|  | }; | ||
|  | 
 | ||
|  | template<typename Lhs, typename Rhs> | ||
|  | struct cctraits<op::GE<Lhs, Rhs> > { | ||
|  |     static const bool can_be_equal = true; | ||
|  |     static const bool prefer_short = false; | ||
|  | 
 | ||
|  |     typedef specialized_compare<Lhs> is_specialized; | ||
|  | }; | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************              compare_collections             ************** // | ||
|  | // ************************************************************************** // | ||
|  | // Overloaded set of functions dispatching to specific implementation of comparison | ||
|  | 
 | ||
|  | template <typename Lhs, typename Rhs, typename L, typename R> | ||
|  | inline assertion_result | ||
|  | compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::EQ<L, R> >*, mpl::true_ ) | ||
|  | { | ||
|  |     return assertion::op::element_compare<op::EQ<L, R> >( lhs, rhs ); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | template <typename Lhs, typename Rhs, typename L, typename R> | ||
|  | inline assertion_result | ||
|  | compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::EQ<L, R> >*, mpl::false_ ) | ||
|  | { | ||
|  |     return lhs == rhs; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | template <typename Lhs, typename Rhs, typename L, typename R> | ||
|  | inline assertion_result | ||
|  | compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::NE<L, R> >*, mpl::true_ ) | ||
|  | { | ||
|  |     return assertion::op::non_equality_compare<op::NE<L, R> >( lhs, rhs ); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | template <typename Lhs, typename Rhs, typename L, typename R> | ||
|  | inline assertion_result | ||
|  | compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::NE<L, R> >*, mpl::false_ ) | ||
|  | { | ||
|  |     return lhs != rhs; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | template <typename OP, typename Lhs, typename Rhs> | ||
|  | inline assertion_result | ||
|  | lexicographic_compare( Lhs const& lhs, Rhs const& rhs ) | ||
|  | { | ||
|  |     return assertion::op::lexicographic_compare<OP, cctraits<OP>::can_be_equal, cctraits<OP>::prefer_short>( lhs, rhs ); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | template <typename Lhs, typename Rhs, typename OP> | ||
|  | inline assertion_result | ||
|  | compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<OP>*, mpl::true_ ) | ||
|  | { | ||
|  |     return lexicographic_compare<OP>( lhs, rhs ); | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | template <typename Lhs, typename Rhs, typename L, typename R> | ||
|  | inline assertion_result | ||
|  | compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::LT<L, R> >*, mpl::false_ ) | ||
|  | { | ||
|  |     return lhs < rhs; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | template <typename Lhs, typename Rhs, typename L, typename R> | ||
|  | inline assertion_result | ||
|  | compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::LE<L, R> >*, mpl::false_ ) | ||
|  | { | ||
|  |     return lhs <= rhs; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | template <typename Lhs, typename Rhs, typename L, typename R> | ||
|  | inline assertion_result | ||
|  | compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::GT<L, R> >*, mpl::false_ ) | ||
|  | { | ||
|  |     return lhs > rhs; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | template <typename Lhs, typename Rhs, typename L, typename R> | ||
|  | inline assertion_result | ||
|  | compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type<op::GE<L, R> >*, mpl::false_ ) | ||
|  | { | ||
|  |     return lhs >= rhs; | ||
|  | } | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // ********* specialization of comparison operators for collections ********* // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | #define DEFINE_COLLECTION_COMPARISON( oper, name, rev )             \ | ||
|  | template<typename Lhs,typename Rhs>                                 \ | ||
|  | struct name<Lhs,Rhs,typename boost::enable_if_c<                    \ | ||
|  |     unit_test::is_forward_iterable<Lhs>::value \ | ||
|  |     &&   !unit_test::is_cstring<Lhs>::value \ | ||
|  |     && unit_test::is_forward_iterable<Rhs>::value \ | ||
|  |     &&   !unit_test::is_cstring<Rhs>::value>::type> {            \ | ||
|  | public:                                                             \ | ||
|  |     typedef assertion_result result_type;                           \ | ||
|  |                                                                     \ | ||
|  |     typedef name<Lhs, Rhs> OP;                                      \ | ||
|  |     typedef typename                                                \ | ||
|  |         mpl::if_c<is_same<typename decay<Lhs>::type,                \ | ||
|  |                           typename decay<Rhs>::type>::value,        \ | ||
|  |                   typename cctraits<OP>::is_specialized,            \ | ||
|  |                   mpl::false_>::type is_specialized;                \ | ||
|  |                                                                     \ | ||
|  |     typedef name<typename Lhs::value_type,                          \ | ||
|  |                  typename Rhs::value_type> elem_op;                 \ | ||
|  |                                                                     \ | ||
|  |     static assertion_result                                         \ | ||
|  |     eval( Lhs const& lhs, Rhs const& rhs)                           \ | ||
|  |     {                                                               \ | ||
|  |         return assertion::op::compare_collections( lhs, rhs,        \ | ||
|  |             (boost::type<elem_op>*)0,                               \ | ||
|  |             is_specialized() );                                     \ | ||
|  |     }                                                               \ | ||
|  |                                                                     \ | ||
|  |     template<typename PrevExprType>                                 \ | ||
|  |     static void                                                     \ | ||
|  |     report( std::ostream&,                                          \ | ||
|  |             PrevExprType const&,                                    \ | ||
|  |             Rhs const& ) {}                                         \ | ||
|  |                                                                     \ | ||
|  |     static char const* revert()                                     \ | ||
|  |     { return " " #rev " "; }                                        \ | ||
|  |                                                                     \ | ||
|  | };                                                                  \ | ||
|  | /**/ | ||
|  | 
 | ||
|  | BOOST_TEST_FOR_EACH_COMP_OP( DEFINE_COLLECTION_COMPARISON ) | ||
|  | #undef DEFINE_COLLECTION_COMPARISON | ||
|  | 
 | ||
|  | //____________________________________________________________________________// | ||
|  | 
 | ||
|  | } // namespace op | ||
|  | } // namespace assertion | ||
|  | } // namespace test_tools | ||
|  | } // namespace boost | ||
|  | 
 | ||
|  | #include <boost/test/detail/enable_warnings.hpp> | ||
|  | 
 | ||
|  | #endif // BOOST_TEST_TOOLS_COLLECTION_COMPARISON_OP_HPP_050815GER |