2016-04-29 2 views
1

Предположат, что у меня есть макет функция с подписью следующегоGTEST/gmock согласовань для подпоследовательности между парой итераторов

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

Я хочу написать EXPECT_CALL, который имеет эффект от использования ContainerEq, ElementsAre или любого другого контейнер-контейнер в последовательности [begin, end). В идеале, существует нечто вроде набора «Range», например:

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

Есть ли такая вещь? Как я могу создать тот, который позволяет использовать все различные совпадения контейнеров для их использования без их перезаписи?

+0

Есть ли способ сделать Googlemock matchers применимо к 'повышению :: range'? Это может обеспечить необходимый мост ... – SimonD

ответ

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]

Преодолеть недостатки этой цели rnal копирование диапазона в контейнер - вам нужно придумать легкий контейнер - как этот:

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 некоторые вспомогательные функции, чтобы сделать его проще использовать этот класс:

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

Создание копии - это единственное решение, с которым я смог придумать - и мне это не понравилось по тем же причинам, о которых вы говорили в комментариях ... возможно, используя ' reference_wrapper' или подобное поможет? – SimonD

+0

См. Мое обновление. – PiotrNycz

+0

Вау, быстрая работа @PiotrNycz! Я попытался использовать boost :: range, но сдался после того, как не смог расшифровать ошибку компиляции, которую я получил ... Это выглядит хорошо ... Я попробую! Благодарю. – SimonD

Смежные вопросы