// (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 //____________________________________________________________________________// namespace boost { namespace test_tools { namespace assertion { // ************************************************************************** // // ************* selectors for specialized comparizon routines ************** // // ************************************************************************** // template struct specialized_compare : public mpl::false_ {}; #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_forward_iterable::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 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_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 ) { typedef typename unit_test::deduce_cstring::type lhs_char_type; typedef typename unit_test::deduce_cstring::type rhs_char_type; return lexicographic_compare( boost::unit_test::basic_cstring(lhs), boost::unit_test::basic_cstring(rhs)); } //____________________________________________________________________________// // ************************************************************************** // // ************** equality_compare ************** // // ************************************************************************** // template inline typename boost::enable_if_c< unit_test::is_forward_iterable::value && unit_test::is_forward_iterable::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 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 typename unit_test::deduce_cstring::type lhs_char_type; typedef typename unit_test::deduce_cstring::type rhs_char_type; return element_compare(boost::unit_test::basic_cstring(lhs), boost::unit_test::basic_cstring(rhs)); } //____________________________________________________________________________// // ************************************************************************** // // ************** non_equality_compare ************** // // ************************************************************************** // template 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 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::value \ && unit_test::is_forward_iterable::value \ && !unit_test::is_cstring::value>::type> { \ public: \ typedef assertion_result result_type; \ \ typedef name OP; \ typedef typename \ mpl::if_c::type, \ typename decay::type>::value, \ typename cctraits::is_specialized, \ mpl::false_>::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