Matching C-style array passed as void* with GMock

烈酒焚心 提交于 2019-12-10 14:17:24

问题


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

回答1:


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



回答2:


The question is: how can we cast const void* to const char*?

You can do this quite simply the normal way assuming you know that buffer is in fact a const char* of course.

const char* cstring = static_cast<const char *>(buffer);

This works because you can cast void * to anything you want and use it (again assuming you know the underlying type). It is irrelevant that the void is a const pointer once you cast it to a different type. It's a good idea to keep the const part because it's a promise that the data won't be changed.



来源:https://stackoverflow.com/questions/48769662/matching-c-style-array-passed-as-void-with-gmock

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!