问题
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 SafeMatcherCast
s:
#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*
toconst 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