Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matching C-style array passed as void* with GMock

Tags:

c++

googlemock

I'm trying to mock such function:

int write(int fd, const void* buffer, size_t size, bool something)

I want to check the correctness of data passed with buffer (first and last argument are not important for the test). There are few nice matchers to help us with that, namely ElementsAreArray. If the pointer was e.g. char*, then it's simple:

EXPECT_CALL(myMock, write(_, NotNull(), expectedSize, _)
    .With(Args<1,2>(ElementsAreArray(dummyArray));

Problem: void* can't be dereferenced. Thus, it's can't be matched with ElementsAreArray.

I tried the following:

EXPECT_CALL(myMock, write(_, NotNull(), expectedSize, _)
    .With(Args<1,2>(MatcherCast<::testing::tuple<const char*, size_t>>(ElementsAreArray(dummyArray)));

but it fails static_assert within MatcherCast - T_must_be_implicitly_convertible_to_U

It is possible to write own matcher to avoid that, but it feels clumsy. The below one works, but I prefer to avoid writing my own matchers:

MATCHER_P2(EqualToArray, compareArray, n, "")
{
    const char *arr = static_cast<const char*>(arg);
    for (size_t i = 0; i < n; ++i)
    {
        if (arr[i] != compareArray[i])
        {
            return false;
        }
    }
    return true;
}

EXPECT_CALL(myMock, write(_, EqualToArray(dummyArray, expectedSize), expectedSize, _);

EDIT: I'm sorry, I probably haven't made myself clear.
I understand that casting from void* to any other pointer type is not much of problem. But this requires us to have a function or user defined matcher, for example like the one I've written, and I'm trying to avoid having user defined matchers, if it is possible to use already defined GMock matchers.

So, the more specific question is:
Is it possible to cast void* to a char* within EXPECT_CALL macro?
Or another words:
Is it possible to make the following snippet work without changing ElementsAreArray() to user-defined matcher:

EXPECT_CALL(myMock, write(_, NotNull(), expectedSize, _)
    .With(Args<1,2>(ElementsAreArray(dummyArray));
like image 598
Yksisarvinen Avatar asked Nov 08 '22 10:11

Yksisarvinen


1 Answers

Edit: I found a way to do this without a custom matcher, following the information in this answer. It involves creating an intermediate class and using 3 (count 'em) nested SafeMatcherCasts:

#include <tuple>

#include <gmock/gmock.h>

// This is a matcher like ElementsAreArray, but it allows you to match against a void *.
template <typename T>
testing::Matcher<std::tuple<const void*, size_t>> elementsAreArrayVoidPointer(
    const T* ptr, size_t size) {
  class TupleConverter : public std::tuple<const T*, size_t> {
  public:
    TupleConverter(const std::tuple<const void*, size_t>& t)
        : std::tuple<const T*, size_t>(static_cast<const T*>(std::get<0>(t)), std::get<1>(t)) {}
  };

  return testing::SafeMatcherCast<std::tuple<const void*, size_t>>(
      testing::SafeMatcherCast<TupleConverter>(
          testing::SafeMatcherCast<std::tuple<const T*, size_t>>(
              testing::ElementsAreArray(ptr, size))));
}

You can use it as follows:

EXPECT_CALL(mock_cstdio, fwrite(_, 1, _, _))
    .With(Args<0, 2>(elementsAreArrayVoidPointer(my_char_p, my_size)));

Previous Answer:

I don't think this is possible.

From googlemock's cookbook:

... MatcherCast works as long as you can static_cast type T to type U.

In this context, T is tuple<void*, size_t> (what you're trying to match) and U is tuple<char*, size_t> (what your ElementsAreArray matcher accepts).

As discussed in this question, tuple<void*, size_t> to tuple<char*, size_t> is not a valid static_cast. (Even though void* to char* is a valid static_cast!)

So I think in this case you need to write a custom matcher.

Note: The T_must_be_implicitly_convertible_to_U message is a red-herring. You see that because googlemock also tries to use SafeMatcherCast and fails, as expected. This is the real error (from the template instantiation that we wish would work, but doesn't):

external/gtest/googlemock/include/gmock/gmock-matchers.h:577:73: error: invalid static_cast from type 'std::tuple<void*, long unsigned int>' to type 'const std::tuple<char*, long unsigned int>&'
       return source_matcher_.MatchAndExplain(static_cast<U>(x), listener);
like image 185
Kerrick Staley Avatar answered Nov 14 '22 21:11

Kerrick Staley