gtest / gmock匹配器,用于一对迭代器之间的子序列

时间:2016-04-29 13:45:38

标签: c++ iterator googletest googlemock

假设我有一个带有签名的模拟函数,如下所示

class MyMock
{
  MOCK_METHOD4( f, void( X, Iterator begin, Iterator end, Y ) );
};

我想写一个EXPECT_CALL,它可以在序列{​​{1}}上使用ContainerEqElementsAre或任何其他容器匹配器。理想情况下类似于' Range'匹配器将存在,例如:

[begin, end)

有这样的事吗?我怎样才能使用允许使用所有各种容器匹配而不重写它们?

1 个答案:

答案 0 :(得分:1)

此类IteratorRange在大多数情况下都有效 - 限制请参阅匹配器主体:

MATCHER_P(IteratorRange, param, "")
{
    auto begin = get<0>(arg);
    auto end = get<1>(arg);

    // these two lines might be weak point of this matcher implementation...
    // I mean:
    //   1. constructing vector might be not supported 
    //       e.g. object are not copyable)
    //   2. copying objects from range might "change"  the tested code behavior
    //       e.g. copy is badly implemented
    //   3. it is for sure "performance" degradation 
    using value_type = typename std::iterator_traits<decltype(begin)>::value_type;
    std::vector<value_type> range{begin, end};

    Matcher<decltype(range)> matcher = param;
    return matcher.MatchAndExplain(range, result_listener);
}

可以像这样使用:

TEST(A,A)
{
    int a,b,c;
    std::vector<int> values{  a,b,c };
    MyMock m;
    EXPECT_CALL( m, f(_,_,_,_) ).With(Args<1,2>(IteratorRange(ContainerEq(values))));
    m.f(X{}, values.begin(), values.end(), Y{});
}

<强> [UPDATE]

克服了内部将范围复制到容器中的缺点 - 你需要发明一个轻容器 - 就像这样:

template <typename Iterator>
class RangeView
{
public:
    using iterator = Iterator;
    using const_iterator = Iterator;
    using value_type = typename std::iterator_traits<Iterator>::value_type;

    RangeView(Iterator begin, Iterator end) : beginIter(begin), endIter(end) {}

    iterator begin() const { return beginIter; }
    iterator end() const { return endIter; }
    std::size_t size() const { return std::distance(beginIter, endIter); }

    bool operator == (const RangeView& other) const
    {
        return size() == other.size() && std::equal(beginIter, endIter, other.beginIter);
    }

private:
    Iterator beginIter, endIter;
};

加上一些辅助函数,以便更容易使用此类:

template <typename Iterator>
void PrintTo(RangeView<Iterator> const& range, std::ostream* os)
{
    *os << "{";
    for (auto&& e: range) *os << "[" << PrintToString(e) << "]";
    *os << "}";
}

template <typename Iterator>
RangeView<Iterator> makeRangeView(Iterator begin, Iterator end)
{
    return RangeView<Iterator>(begin, end);
}

template <typename Container>
auto makeRangeView(Container const& container)
{
    return makeRangeView(std::begin(container), std::end(container));
}

请注意,如果您在项目中使用boost - 您可以使用range from boost - 您不需要“重新发明轮子”。

然后 - 可以定义光匹配器而没有这些缺点:

MATCHER_P(IteratorRangeLight, param, "")
{
    auto begin = get<0>(arg);
    auto end = get<1>(arg);
    auto range = makeRangeView(begin, end);

    Matcher<decltype(range)> matcher = param;
    return matcher.MatchAndExplain(range, result_listener);
}

然后 - 像这样使用它:

TEST(A,A)
{
    int a=1,b=2,c=3;
    std::vector<int> values{  a,b,c };
    MyMock m;
    EXPECT_CALL( m, f(_,_,_,_) ).
With(Args<1,2>(IteratorRangeLight(ContainerEq(makeRangeView(values)))));
//                                            ^^^^^^^^^^^^^
// note that you need to use  makeRangeView also within the matcher
    m.f(X{}, values.begin(), values.end(), Y{});
}