2016-04-29 2 views
1

Supposons que j'ai une fonction simulée avec une signature de la manière suivantegtest/gmock matcher de séquence entre deux itérateurs

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

Je veux écrire un EXPECT_CALL qui a pour effet d'utiliser ContainerEq, ElementsAre ou tout autre conteneur Matcher sur la séquence [begin, end). Idéalement, il devrait exister quelque chose comme un adaptateur de gamme, par exemple:

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

Y a-t-il une telle chose? Comment est-ce que je pourrais en faire un qui permette d'utiliser toutes les différentes correspondances de conteneurs à utiliser sans les réécrire?

+0

est-il un moyen de faire le Googlemock Matchers applicable à 'boost :: gamme'? Cela pourrait fournir le pont nécessaire ... – SimonD

Répondre

1

Un tel IteratorRange fonctionnera dans la plupart des cas - pour limiter l'air dans le corps matcher:

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

Il peut être utilisé comme ceci:

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]

Le surmonter les inconvénients de cette inte rn copie d'une plage dans le récipient - vous avez besoin d'inventer le récipient de lumière - comme celui-ci:

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 une fonction d'aide pour le rendre plus facile à utiliser cette classe:

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

Notez que si vous utilisez boost dans votre projet - vous pouvez utiliser range from boost - vous n'avez pas besoin de "réinventer la roue".

Puis - matcher la lumière peut être définie sans ces inconvénients:

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

Puis - l'utiliser comme ceci:

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

Faire une copie est la seule solution que j'ai pu trouver - et je ne l'ai pas aimé pour les mêmes raisons que celles que vous mentionnez dans les lignes commentées ... peut-être en utilisant ' reference_wrapper' ou similaire aiderait? – SimonD

+0

Voir ma mise à jour. – PiotrNycz

+0

Whoa, travail rapide @PiotrNycz! J'avais essayé d'utiliser boost :: range mais j'ai abandonné après ne pas être capable de déchiffrer l'erreur de compilation que j'ai eue ... Ça a l'air bien ... Je vais essayer! Merci. – SimonD