mirror of
https://github.com/google/googletest.git
synced 2025-04-10 07:07:55 +00:00
Deduce value type in container matchers
Avoid directly using the member `value_type` of a container, and deduce the value type from the iterator instead (using `std::iterator_traits`). This allows to use container matchers on containers that do not expose their value type as a member type.
This commit is contained in:
parent
e40661d89b
commit
994965666a
4 changed files with 111 additions and 19 deletions
|
@ -2404,8 +2404,7 @@ class BeginEndDistanceIsMatcher {
|
|||
// elements in the containers (which don't properly matter to sets, but can
|
||||
// occur if the containers are vectors or lists, for example).
|
||||
//
|
||||
// Uses the container's const_iterator, value_type, operator ==,
|
||||
// begin(), and end().
|
||||
// Uses the container's const_iterator, operator ==, begin(), and end().
|
||||
template <typename Container>
|
||||
class ContainerEqMatcher {
|
||||
public:
|
||||
|
@ -2492,6 +2491,11 @@ struct LessComparator {
|
|||
}
|
||||
};
|
||||
|
||||
// Trait to deduce the value type of a container
|
||||
template <typename Container>
|
||||
using ValueType = typename std::iterator_traits<
|
||||
decltype(std::declval<Container>().begin())>::value_type;
|
||||
|
||||
// Implements WhenSortedBy(comparator, container_matcher).
|
||||
template <typename Comparator, typename ContainerMatcher>
|
||||
class WhenSortedByMatcher {
|
||||
|
@ -2516,8 +2520,7 @@ class WhenSortedByMatcher {
|
|||
// Transforms std::pair<const Key, Value> into std::pair<Key, Value>
|
||||
// so that we can match associative containers.
|
||||
typedef
|
||||
typename RemoveConstFromKey<typename LhsStlContainer::value_type>::type
|
||||
LhsValue;
|
||||
typename RemoveConstFromKey<ValueType<LhsStlContainer>>::type LhsValue;
|
||||
|
||||
Impl(const Comparator& comparator, const ContainerMatcher& matcher)
|
||||
: comparator_(comparator), matcher_(matcher) {}
|
||||
|
@ -2583,7 +2586,7 @@ class PointwiseMatcher {
|
|||
public:
|
||||
typedef internal::StlContainerView<RhsContainer> RhsView;
|
||||
typedef typename RhsView::type RhsStlContainer;
|
||||
typedef typename RhsStlContainer::value_type RhsValue;
|
||||
typedef ValueType<RhsStlContainer> RhsValue;
|
||||
|
||||
static_assert(!std::is_const<RhsContainer>::value,
|
||||
"RhsContainer type must not be const");
|
||||
|
@ -2613,7 +2616,7 @@ class PointwiseMatcher {
|
|||
LhsView;
|
||||
typedef typename LhsView::type LhsStlContainer;
|
||||
typedef typename LhsView::const_reference LhsStlContainerReference;
|
||||
typedef typename LhsStlContainer::value_type LhsValue;
|
||||
typedef ValueType<LhsStlContainer> LhsValue;
|
||||
// We pass the LHS value and the RHS value to the inner matcher by
|
||||
// reference, as they may be expensive to copy. We must use tuple
|
||||
// instead of pair here, as a pair cannot hold references (C++ 98,
|
||||
|
@ -2699,7 +2702,7 @@ class QuantifierMatcherImpl : public MatcherInterface<Container> {
|
|||
typedef StlContainerView<RawContainer> View;
|
||||
typedef typename View::type StlContainer;
|
||||
typedef typename View::const_reference StlContainerReference;
|
||||
typedef typename StlContainer::value_type Element;
|
||||
typedef ValueType<StlContainer> Element;
|
||||
|
||||
template <typename InnerMatcher>
|
||||
explicit QuantifierMatcherImpl(InnerMatcher inner_matcher)
|
||||
|
@ -3370,7 +3373,7 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
|
|||
typedef internal::StlContainerView<RawContainer> View;
|
||||
typedef typename View::type StlContainer;
|
||||
typedef typename View::const_reference StlContainerReference;
|
||||
typedef typename StlContainer::value_type Element;
|
||||
typedef ValueType<StlContainer> Element;
|
||||
|
||||
// Constructs the matcher from a sequence of element values or
|
||||
// element matchers.
|
||||
|
@ -3616,7 +3619,7 @@ class UnorderedElementsAreMatcherImpl
|
|||
typedef internal::StlContainerView<RawContainer> View;
|
||||
typedef typename View::type StlContainer;
|
||||
typedef typename View::const_reference StlContainerReference;
|
||||
typedef typename StlContainer::value_type Element;
|
||||
typedef ValueType<StlContainer> Element;
|
||||
|
||||
template <typename InputIter>
|
||||
UnorderedElementsAreMatcherImpl(UnorderedMatcherRequire::Flags matcher_flags,
|
||||
|
@ -3705,7 +3708,7 @@ class UnorderedElementsAreMatcher {
|
|||
operator Matcher<Container>() const {
|
||||
typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
|
||||
typedef typename internal::StlContainerView<RawContainer>::type View;
|
||||
typedef typename View::value_type Element;
|
||||
typedef ValueType<View> Element;
|
||||
typedef ::std::vector<Matcher<const Element&>> MatcherVec;
|
||||
MatcherVec matchers;
|
||||
matchers.reserve(::std::tuple_size<MatcherTuple>::value);
|
||||
|
@ -3736,7 +3739,7 @@ class ElementsAreMatcher {
|
|||
|
||||
typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
|
||||
typedef typename internal::StlContainerView<RawContainer>::type View;
|
||||
typedef typename View::value_type Element;
|
||||
typedef ValueType<View> Element;
|
||||
typedef ::std::vector<Matcher<const Element&>> MatcherVec;
|
||||
MatcherVec matchers;
|
||||
matchers.reserve(::std::tuple_size<MatcherTuple>::value);
|
||||
|
@ -4215,7 +4218,7 @@ inline internal::UnorderedElementsAreArrayMatcher<T> UnorderedElementsAreArray(
|
|||
|
||||
template <typename Container>
|
||||
inline internal::UnorderedElementsAreArrayMatcher<
|
||||
typename Container::value_type>
|
||||
internal::ValueType<Container>>
|
||||
UnorderedElementsAreArray(const Container& container) {
|
||||
return UnorderedElementsAreArray(container.begin(), container.end());
|
||||
}
|
||||
|
@ -4761,15 +4764,15 @@ template <typename Tuple2Matcher, typename RhsContainer>
|
|||
inline internal::UnorderedElementsAreArrayMatcher<
|
||||
typename internal::BoundSecondMatcher<
|
||||
Tuple2Matcher,
|
||||
typename internal::StlContainerView<
|
||||
typename std::remove_const<RhsContainer>::type>::type::value_type>>
|
||||
internal::ValueType<typename internal::StlContainerView<
|
||||
typename std::remove_const<RhsContainer>::type>::type>>>
|
||||
UnorderedPointwise(const Tuple2Matcher& tuple2_matcher,
|
||||
const RhsContainer& rhs_container) {
|
||||
// RhsView allows the same code to handle RhsContainer being a
|
||||
// STL-style container and it being a native C-style array.
|
||||
typedef typename internal::StlContainerView<RhsContainer> RhsView;
|
||||
typedef typename RhsView::type RhsStlContainer;
|
||||
typedef typename RhsStlContainer::value_type Second;
|
||||
typedef internal::ValueType<RhsStlContainer> Second;
|
||||
const RhsStlContainer& rhs_stl_container =
|
||||
RhsView::ConstReference(rhs_container);
|
||||
|
||||
|
@ -4879,7 +4882,7 @@ inline internal::UnorderedElementsAreArrayMatcher<T> IsSupersetOf(
|
|||
|
||||
template <typename Container>
|
||||
inline internal::UnorderedElementsAreArrayMatcher<
|
||||
typename Container::value_type>
|
||||
internal::ValueType<Container>>
|
||||
IsSupersetOf(const Container& container) {
|
||||
return IsSupersetOf(container.begin(), container.end());
|
||||
}
|
||||
|
@ -4936,7 +4939,7 @@ inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf(
|
|||
|
||||
template <typename Container>
|
||||
inline internal::UnorderedElementsAreArrayMatcher<
|
||||
typename Container::value_type>
|
||||
internal::ValueType<Container>>
|
||||
IsSubsetOf(const Container& container) {
|
||||
return IsSubsetOf(container.begin(), container.end());
|
||||
}
|
||||
|
@ -5184,13 +5187,13 @@ inline internal::AllOfArrayMatcher<T> AllOfArray(const T (&array)[N]) {
|
|||
}
|
||||
|
||||
template <typename Container>
|
||||
inline internal::AnyOfArrayMatcher<typename Container::value_type> AnyOfArray(
|
||||
inline internal::AnyOfArrayMatcher<internal::ValueType<Container>> AnyOfArray(
|
||||
const Container& container) {
|
||||
return AnyOfArray(container.begin(), container.end());
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
inline internal::AllOfArrayMatcher<typename Container::value_type> AllOfArray(
|
||||
inline internal::AllOfArrayMatcher<internal::ValueType<Container>> AllOfArray(
|
||||
const Container& container) {
|
||||
return AllOfArray(container.begin(), container.end());
|
||||
}
|
||||
|
|
|
@ -1640,6 +1640,11 @@ TEST(IsSupersetOfTest, WorksWithMoveOnly) {
|
|||
helper.Call(MakeUniquePtrs({2}));
|
||||
}
|
||||
|
||||
TEST(IsSupersetOfTest, DeducesValueType) {
|
||||
const ContainerWithoutValueType container{1, 2, 3};
|
||||
EXPECT_THAT(container, IsSupersetOf(ContainerWithoutValueType{1, 2}));
|
||||
}
|
||||
|
||||
TEST(IsSubsetOfTest, WorksForNativeArray) {
|
||||
const int subset[] = {1, 4};
|
||||
const int superset[] = {1, 2, 4};
|
||||
|
@ -1768,6 +1773,11 @@ TEST(IsSubsetOfTest, WorksWithMoveOnly) {
|
|||
helper.Call(MakeUniquePtrs({2}));
|
||||
}
|
||||
|
||||
TEST(IsSubsetOfTest, DeducesValueType) {
|
||||
const ContainerWithoutValueType container{1, 2, 3};
|
||||
EXPECT_THAT(container, IsSubsetOf(ContainerWithoutValueType{1, 2, 3, 4}));
|
||||
}
|
||||
|
||||
// Tests using ElementsAre() and ElementsAreArray() with stream-like
|
||||
// "containers".
|
||||
|
||||
|
@ -1917,6 +1927,11 @@ TEST(UnorderedElementsAreArrayTest, WorksWithMoveOnly) {
|
|||
helper.Call(MakeUniquePtrs({2, 1}));
|
||||
}
|
||||
|
||||
TEST(UnorderedElementsAreArrayTest, DeducesValueType) {
|
||||
const ContainerWithoutValueType container{1, 2, 3};
|
||||
EXPECT_THAT(container, UnorderedElementsAreArray(container));
|
||||
}
|
||||
|
||||
class UnorderedElementsAreTest : public testing::Test {
|
||||
protected:
|
||||
typedef std::vector<int> IntVec;
|
||||
|
@ -2127,6 +2142,11 @@ TEST_F(UnorderedElementsAreTest, DescribeNegation) {
|
|||
" - element #2 is equal to 345"));
|
||||
}
|
||||
|
||||
TEST_F(UnorderedElementsAreTest, DeducesValueType) {
|
||||
const ContainerWithoutValueType container{1, 2, 3};
|
||||
EXPECT_THAT(container, UnorderedElementsAre(1, 2, 3));
|
||||
}
|
||||
|
||||
// Tests Each().
|
||||
|
||||
INSTANTIATE_GTEST_MATCHER_TEST_P(EachTest);
|
||||
|
@ -2221,6 +2241,11 @@ TEST(EachTest, WorksWithMoveOnly) {
|
|||
helper.Call(MakeUniquePtrs({1, 2}));
|
||||
}
|
||||
|
||||
TEST(EachTest, DeducesValueType) {
|
||||
const ContainerWithoutValueType container{1, 2, 3};
|
||||
EXPECT_THAT(container, Each(Gt(0)));
|
||||
}
|
||||
|
||||
// For testing Pointwise().
|
||||
class IsHalfOfMatcher {
|
||||
public:
|
||||
|
@ -2280,6 +2305,11 @@ TEST(PointwiseTest, MakesCopyOfRhs) {
|
|||
EXPECT_THAT(lhs, m);
|
||||
}
|
||||
|
||||
TEST(PointwiseTest, DeducesValueType) {
|
||||
const ContainerWithoutValueType container{1, 2, 3};
|
||||
EXPECT_THAT(container, Pointwise(Eq(), container));
|
||||
}
|
||||
|
||||
TEST(PointwiseTest, WorksForLhsNativeArray) {
|
||||
const int lhs[] = {1, 2, 3};
|
||||
vector<int> rhs;
|
||||
|
@ -2486,6 +2516,11 @@ TEST(UnorderedPointwiseTest, WorksWithMoveOnly) {
|
|||
helper.Call(MakeUniquePtrs({2, 1}));
|
||||
}
|
||||
|
||||
TEST(UnorderedPointwiseTest, DeducesValueType) {
|
||||
const ContainerWithoutValueType container{1, 2, 3};
|
||||
EXPECT_THAT(container, UnorderedPointwise(Eq(), container));
|
||||
}
|
||||
|
||||
TEST(PointeeTest, WorksOnMoveOnlyType) {
|
||||
std::unique_ptr<int> p(new int(3));
|
||||
EXPECT_THAT(p, Pointee(Eq(3)));
|
||||
|
@ -2855,6 +2890,11 @@ TEST(ElementsAreTest, MakesCopyOfArguments) {
|
|||
EXPECT_THAT(array2, Not(polymorphic_matcher));
|
||||
}
|
||||
|
||||
TEST(ElementsAreTest, DeducesValueType) {
|
||||
const ContainerWithoutValueType container{1, 2, 3};
|
||||
EXPECT_THAT(container, ElementsAre(1, 2, 3));
|
||||
}
|
||||
|
||||
// Tests for ElementsAreArray(). Since ElementsAreArray() shares most
|
||||
// of the implementation with ElementsAre(), we don't test it as
|
||||
// thoroughly here.
|
||||
|
@ -2996,6 +3036,11 @@ TEST(ElementsAreArrayTest, SourceLifeSpan) {
|
|||
EXPECT_THAT(test_vector, Not(matcher_maker));
|
||||
}
|
||||
|
||||
TEST(ElementsAreArrayTest, DeducesValueType) {
|
||||
const ContainerWithoutValueType container{1, 2, 3};
|
||||
EXPECT_THAT(container, ElementsAreArray(container));
|
||||
}
|
||||
|
||||
// Tests Contains().
|
||||
|
||||
INSTANTIATE_GTEST_MATCHER_TEST_P(ContainsTest);
|
||||
|
@ -3130,6 +3175,11 @@ TEST(ContainsTest, WorksForTwoDimensionalNativeArray) {
|
|||
EXPECT_THAT(a, Contains(Not(Contains(5))));
|
||||
}
|
||||
|
||||
TEST(ContainsTest, DeducesValueType) {
|
||||
const ContainerWithoutValueType container{1, 2, 3};
|
||||
EXPECT_THAT(container, Contains(1));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace gmock_matchers_test
|
||||
} // namespace testing
|
||||
|
|
|
@ -279,6 +279,13 @@ TYPED_TEST(ContainerEqTest, DuplicateDifference) {
|
|||
}
|
||||
#endif // GTEST_HAS_TYPED_TEST
|
||||
|
||||
TEST(ContainerEqTest, DeducesValueType)
|
||||
{
|
||||
const ContainerWithoutValueType lhs{1, 2, 3};
|
||||
const ContainerWithoutValueType rhs{1, 2, 3};
|
||||
EXPECT_THAT(lhs, ContainerEq(rhs));
|
||||
}
|
||||
|
||||
// Tests that multiple missing values are reported.
|
||||
// Using just vector here, so order is predictable.
|
||||
TEST(ContainerEqExtraTest, MultipleValuesMissing) {
|
||||
|
@ -1512,6 +1519,12 @@ TEST(AllOfArrayTest, Matchers) {
|
|||
EXPECT_THAT(1, AllOfArray({Ge(0), Ge(1)}));
|
||||
}
|
||||
|
||||
TEST(AllOfArrayTest, DeducesValueType)
|
||||
{
|
||||
const ContainerWithoutValueType container{1, 2, 3};
|
||||
EXPECT_THAT(0, Not(AllOfArray(container)));
|
||||
}
|
||||
|
||||
INSTANTIATE_GTEST_MATCHER_TEST_P(AnyOfArrayTest);
|
||||
|
||||
TEST(AnyOfArrayTest, BasicForms) {
|
||||
|
@ -1601,6 +1614,12 @@ TEST_P(AnyOfArrayTestP, ExplainsMatchResultCorrectly) {
|
|||
Explain(g2, 2));
|
||||
}
|
||||
|
||||
TEST(AnyOfArrayTest, DeducesValueType)
|
||||
{
|
||||
const ContainerWithoutValueType container{1, 2, 3};
|
||||
EXPECT_THAT(1, AnyOfArray(container));
|
||||
}
|
||||
|
||||
MATCHER(IsNotNull, "") { return arg != nullptr; }
|
||||
|
||||
// Verifies that a matcher defined using MATCHER() can work on
|
||||
|
|
|
@ -186,6 +186,26 @@ std::string Explain(const MatcherType& m, const Value& x) {
|
|||
return listener.str();
|
||||
}
|
||||
|
||||
// A minimal container that does not expose value_type
|
||||
class ContainerWithoutValueType {
|
||||
using UnderlyingContainer = std::vector<int>;
|
||||
|
||||
public:
|
||||
ContainerWithoutValueType(std::initializer_list<int> const args) : _v{args} {}
|
||||
|
||||
UnderlyingContainer::const_iterator begin() const { return _v.begin(); }
|
||||
UnderlyingContainer::const_iterator end() const { return _v.end(); }
|
||||
UnderlyingContainer::size_type size() const { return _v.size(); }
|
||||
|
||||
friend bool operator==(ContainerWithoutValueType const& lhs,
|
||||
ContainerWithoutValueType const& rhs) {
|
||||
return lhs._v == rhs._v;
|
||||
}
|
||||
|
||||
private:
|
||||
UnderlyingContainer _v;
|
||||
};
|
||||
|
||||
} // namespace gmock_matchers_test
|
||||
} // namespace testing
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue