2016-04-29 17 views
1

Angenommen I mit einer Signatur eine mock Funktion haben, wieGtest/gmock Matcher für Subsequenz zwischen einem Paar von Iteratoren

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

folgt ich eine EXPECT_CALL schreiben möchten, dass die Wirkung der Verwendung von ContainerEq, ElementsAre oder andere hat Container Matcher auf der Sequenz [begin, end). Idealerweise würde so etwas wie ein 'Range'-Matcher existieren, z. B .:

MyMock m; 
EXPECT_CALL(m, f(_,_,_,_)).With(Args<1,2>(Range(ElementsAre(a,b,c))); 

Gibt es so etwas? Wie kann ich einen erstellen, der es ermöglicht, alle verschiedenen Containerübereinstimmungen zu verwenden, ohne sie neu zu schreiben?

+0

Gibt es eine Möglichkeit, die Googlemock zu machen Matcher für 'boost :: Reichweite? Das könnte die notwendige Brücke liefern ... – SimonD

Antwort

1

Solche IteratorRange wird in den meisten Fällen funktionieren - zur Begrenzung Blick in Matcher Körper:

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]

:

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); 
} 

Es kann wie folgt verwendet werden

Die Überwindung der Nachteile dieser Inte rnal Kopieren eines Bereichs in den Behälter - Sie brauchen das Licht Behälter zu erfinden - wie diese:

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; 
}; 

plus einige Hilfsfunktion zu erleichtern, diese Klasse zu verwenden:

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)); 
} 

Beachten Sie, wenn Sie Verwenden Sie Boost in Ihrem Projekt - Sie können range from boost verwenden - Sie müssen "das Rad nicht neu erfinden".

Dann - Licht Matcher ohne diese Nachteile definiert werden:

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); 
} 

Dann - es ist wie folgt verwenden:

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{}); 
} 
+0

Eine Kopie zu erstellen ist die einzige Lösung, die ich mir vorstellen konnte - und ich mochte sie nicht aus den gleichen Gründen, wie Sie in den kommentierten Zeilen erwähnt haben ... vielleicht mit ' reference_wrapper' oder ähnliches würde helfen? – SimonD

+0

Siehe mein Update. – PiotrNycz

+0

Whoa, schnelle Arbeit @PiotrNycz! Ich hatte versucht, boost :: range zu verwenden, gab aber auf, nachdem ich den Kompilierfehler, den ich bekam, nicht entziffern konnte ... Das sieht gut aus ... Ich werde es versuchen! Vielen Dank. – SimonD