// (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 #include #include // Boost #include #include #include #include #include //____________________________________________________________________________// namespace boost { namespace test_tools { namespace assertion { // ************************************************************************** // // ************* selectors for specialized comparizon routines ************** // // ************************************************************************** // template struct specialized_compare : public mpl::false_ {}; template struct is_c_array : public mpl::false_ {}; template struct is_c_array : public mpl::true_ {}; template struct is_c_array : public mpl::true_ {}; #define BOOST_TEST_SPECIALIZED_COLLECTION_COMPARE(Col) \ namespace boost { namespace test_tools { namespace assertion { \ template<> \ struct specialized_compare : public mpl::true_ {}; \ }}} \ /**/ // ************************************************************************** // // ************** lexicographic_compare ************** // // ************************************************************************** // namespace op { template inline typename boost::enable_if_c< unit_test::is_forward_iterable::value && !unit_test::is_cstring::value && unit_test::is_forward_iterable::value && !unit_test::is_cstring::value, assertion_result>::type lexicographic_compare( Lhs const& lhs, Rhs const& rhs ) { assertion_result ar( true ); typedef unit_test::bt_iterator_traits t_Lhs_iterator; typedef unit_test::bt_iterator_traits t_Rhs_iterator; typename t_Lhs_iterator::const_iterator first1 = t_Lhs_iterator::begin(lhs); typename t_Rhs_iterator::const_iterator first2 = t_Rhs_iterator::begin(rhs); typename t_Lhs_iterator::const_iterator last1 = t_Lhs_iterator::end(lhs); typename t_Rhs_iterator::const_iterator last2 = t_Rhs_iterator::end(rhs); 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 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 inline typename boost::enable_if_c< (unit_test::is_cstring::value || unit_test::is_cstring::value), assertion_result>::type lexicographic_compare( Lhs const& lhs, Rhs const& rhs ) { typedef typename unit_test::deduce_cstring_transform::type lhs_char_type; typedef typename unit_test::deduce_cstring_transform::type rhs_char_type; return lexicographic_compare( lhs_char_type(lhs), rhs_char_type(rhs)); } //____________________________________________________________________________// // ************************************************************************** // // ************** equality_compare ************** // // ************************************************************************** // template inline typename boost::enable_if_c< unit_test::is_forward_iterable::value && !unit_test::is_cstring::value && unit_test::is_forward_iterable::value && !unit_test::is_cstring::value, assertion_result>::type element_compare( Lhs const& lhs, Rhs const& rhs ) { typedef unit_test::bt_iterator_traits t_Lhs_iterator; typedef unit_test::bt_iterator_traits t_Rhs_iterator; assertion_result ar( true ); if( t_Lhs_iterator::size(lhs) != t_Rhs_iterator::size(rhs) ) { ar = false; ar.message() << "\nCollections size mismatch: " << t_Lhs_iterator::size(lhs) << " != " << t_Rhs_iterator::size(rhs); return ar; } typename t_Lhs_iterator::const_iterator left = t_Lhs_iterator::begin(lhs); typename t_Rhs_iterator::const_iterator right = t_Rhs_iterator::begin(rhs); std::size_t pos = 0; for( ; pos < t_Lhs_iterator::size(lhs); ++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 inline typename boost::enable_if_c< (unit_test::is_cstring::value || unit_test::is_cstring::value), assertion_result>::type element_compare( Lhs const& lhs, Rhs const& rhs ) { typedef typename unit_test::deduce_cstring_transform::type lhs_char_type; typedef typename unit_test::deduce_cstring_transform::type rhs_char_type; return element_compare(lhs_char_type(lhs), rhs_char_type(rhs)); } //____________________________________________________________________________// // ************************************************************************** // // ************** non_equality_compare ************** // // ************************************************************************** // template inline assertion_result non_equality_compare( Lhs const& lhs, Rhs const& rhs ) { typedef unit_test::bt_iterator_traits t_Lhs_iterator; typedef unit_test::bt_iterator_traits t_Rhs_iterator; assertion_result ar( true ); if( t_Lhs_iterator::size(lhs) != t_Rhs_iterator::size(rhs) ) return ar; typename t_Lhs_iterator::const_iterator left = t_Lhs_iterator::begin(lhs); typename t_Rhs_iterator::const_iterator right = t_Rhs_iterator::begin(rhs); typename t_Lhs_iterator::const_iterator end = t_Lhs_iterator::end(lhs); 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 struct cctraits; template struct cctraits > { typedef specialized_compare is_specialized; }; template struct cctraits > { typedef specialized_compare is_specialized; }; template struct cctraits > { static const bool can_be_equal = false; static const bool prefer_short = true; typedef specialized_compare is_specialized; }; template struct cctraits > { static const bool can_be_equal = true; static const bool prefer_short = true; typedef specialized_compare is_specialized; }; template struct cctraits > { static const bool can_be_equal = false; static const bool prefer_short = false; typedef specialized_compare is_specialized; }; template struct cctraits > { static const bool can_be_equal = true; static const bool prefer_short = false; typedef specialized_compare is_specialized; }; // ************************************************************************** // // ************** compare_collections ************** // // ************************************************************************** // // Overloaded set of functions dispatching to specific implementation of comparison template inline assertion_result compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type >*, mpl::true_ ) { return assertion::op::element_compare >( lhs, rhs ); } //____________________________________________________________________________// template inline assertion_result compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type >*, mpl::false_ ) { return lhs == rhs; } //____________________________________________________________________________// template inline assertion_result compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type >*, mpl::true_ ) { return assertion::op::non_equality_compare >( lhs, rhs ); } //____________________________________________________________________________// template inline assertion_result compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type >*, mpl::false_ ) { return lhs != rhs; } //____________________________________________________________________________// template inline assertion_result lexicographic_compare( Lhs const& lhs, Rhs const& rhs ) { return assertion::op::lexicographic_compare::can_be_equal, cctraits::prefer_short>( lhs, rhs ); } //____________________________________________________________________________// template inline assertion_result compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type*, mpl::true_ ) { return lexicographic_compare( lhs, rhs ); } //____________________________________________________________________________// template inline assertion_result compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type >*, mpl::false_ ) { return lhs < rhs; } //____________________________________________________________________________// template inline assertion_result compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type >*, mpl::false_ ) { return lhs <= rhs; } //____________________________________________________________________________// template inline assertion_result compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type >*, mpl::false_ ) { return lhs > rhs; } //____________________________________________________________________________// template inline assertion_result compare_collections( Lhs const& lhs, Rhs const& rhs, boost::type >*, mpl::false_ ) { return lhs >= rhs; } //____________________________________________________________________________// // ************************************************************************** // // ********* specialization of comparison operators for collections ********* // // ************************************************************************** // #define DEFINE_COLLECTION_COMPARISON( oper, name, rev ) \ template \ struct name::value \ && !unit_test::is_cstring_comparable::value \ && unit_test::is_forward_iterable::value \ && !unit_test::is_cstring_comparable::value>::type> { \ public: \ typedef assertion_result result_type; \ typedef unit_test::bt_iterator_traits t_Lhs_iterator_helper; \ typedef unit_test::bt_iterator_traits t_Rhs_iterator_helper; \ \ typedef name OP; \ \ typedef typename \ mpl::if_c< \ mpl::or_< \ typename is_c_array::type, \ typename is_c_array::type \ >::value, \ mpl::true_, \ typename \ mpl::if_c::type, \ typename decay::type>::value, \ typename cctraits::is_specialized, \ mpl::false_>::type \ >::type is_specialized; \ \ typedef name elem_op; \ \ static assertion_result \ eval( Lhs const& lhs, Rhs const& rhs) \ { \ return assertion::op::compare_collections( lhs, rhs, \ (boost::type*)0, \ is_specialized() ); \ } \ \ template \ 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 #endif // BOOST_TEST_TOOLS_COLLECTION_COMPARISON_OP_HPP_050815GER