Matching arrays

5,238 views
Skip to first unread message

Martin Svensson

unread,
Aug 6, 2010, 6:50:54 AM8/6/10
to Google C++ Mocking Framework
Hello,

How can I match a C-array argument against a C-array value without
using a custom matcher?
I have tried ElementsAre/ElementsAreArrray and ContainerEq, but they
don't seem to support arrays for the actual value (only for the
expected value).

Kindly
Martin Svensson

Vlad Losev

unread,
Aug 6, 2010, 11:34:22 AM8/6/10
to Google C++ Mocking Framework
Hi Martin,

On Fri, Aug 6, 2010 at 3:50 AM, Martin Svensson <martin.k...@netinsight.net> wrote:
Hello,

How can I match a C-array argument against a C-array value without
using a custom matcher?

The Eq matcher will match anything that has operator== defined. You can try it with Boost array. But it won't give you the breakdown of differences like ElementsAre will.
 
I have tried ElementsAre/ElementsAreArrray and ContainerEq, but they
don't seem to support arrays for the actual value (only for the
expected value).

The arguments of ElementsAre (and elements of ElementsAreArrray) can be matchers. That means you can nest them:

ElementsAre(ElementsAre(3, 4), ElementsAre(5, 6))
 

Kindly
Martin Svensson

HTH,
Vlad

Zhanyong Wan (λx.x x)

unread,
Aug 6, 2010, 12:51:46 PM8/6/10
to Martin Svensson, Google C++ Mocking Framework
Could you post the actual function signature of your mock function?
It's unclear what you mean exactly by "C-array" (a pointer and a size?
an array passed by reference? an array passed by pointer?).

On Fri, Aug 6, 2010 at 3:50 AM, Martin Svensson
<martin.k...@netinsight.net> wrote:

--
Zhanyong

Zhanyong Wan (λx.x x)

unread,
Aug 9, 2010, 1:54:22 PM8/9/10
to Martin K. Svensson, Google C++ Mocking Framework
(adding back the mailing list)

On Sun, Aug 8, 2010 at 11:14 PM, Martin K. Svensson
<martin.k...@netinsight.net> wrote:
> Very sorry. This is what I want to do, but it doesn't work for me:
>
> ---
> MOCK_METHOD1(method, void (array_t actual_data));
> ---
> typedef unsigned char array_t[3];

This doesn't work, as the compiler treats the function signature

void method(array_t actual_data);

as

void method(unsigned char actual_data[3]);

which is the same as

void method(unsigned char * actual_data);

as C++ ignores the size of an array argument and treats it as a pointer.

With that, the following code will compile, but can do something bad
at run time:

unsigned char data[2] = ...;
....method(data);

You should change the signature to pass the array by a reference, by a
pointer, or as a pointer and a size.

> array_t const expected_data = { 1, 2, 3 };
> EXPECT_CALL(object, method(ElementsAreArray(expected_data)));
> ---
>
> The following is interesting too:
>
> ---
> MOCK_METHOD1(method, void (unsigned char const * actual_elements, size_t
> num_elements));
> ---
> unsigned char const expected_elements * = { 1, 2, 3 };

This doesn't compile as '*' is mis-placed.

> size_t const num_elements = 3u;
> EXPECT_CALL(object, method(NotNull(), _)
> .With(AllArgs(ElementsAreArray(expected_data, num_elements)));
> ---
>
> /Martin

Martin K. Svensson

unread,
Aug 11, 2010, 2:44:07 AM8/11/10
to Zhanyong Wan (λx.x, Google C++ Mocking Framework
Thanks, but I still don't quite understand.
I get:

---
gmock-matchers.h: In member function 'testing::internal::ElementsAreArrayMatcher<T>::operator testing::Matcher<T>() const [with Container = const u_int8_t*, T = unsigned char]':
test.cpp:30: instantiated from here
gmock-matchers.h:2333: error: 'const u_int8_t*' is not a class, struct, or union type
---

Changing the parameter to a regular element pointer does not seem to
help. Neither did changing it to an array reference or a pointer to an
array.

/Martin

P.S. The misplaced '*' was just a typo in the mail, and 'unsigned char'
is actually 'u_int8_t' if it makes any difference.

Zhanyong Wan (λx.x x)

unread,
Aug 11, 2010, 3:05:47 AM8/11/10
to Martin K. Svensson, Google C++ Mocking Framework
On Tue, Aug 10, 2010 at 11:44 PM, Martin K. Svensson
<martin.k...@netinsight.net> wrote:
> Changing the parameter to a regular element pointer does not seem to
> help. Neither did changing it to an array reference or a pointer to an
> array.

Please post the actual code you wrote.

--
Zhanyong

Martin K. Svensson

unread,
Aug 11, 2010, 3:30:41 AM8/11/10
to Zhanyong Wan (λx.x, Google C++ Mocking Framework
Easier said than done since I've changed it around so many times, and
there's a huge context.
The code is currently (got it working with a custom matcher):

---
#define HEAL_ADDR_SIZE 20
typedef u_int8_t heal_address[HEAL_ADDR_SIZE];
[...]
extern "C"
int dsync_select(enum sync_source src, const heal_address ifAddr, int new_RS, u_int8_t rsId)
{
assert(theDSyncInstance != NULL);
return theDSyncInstance->dsync_select(src, ifAddr, new_RS, rsId);
}
[...]
MOCK_METHOD4(dsync_select, int (enum sync_source src, const heal_address ifAddr, int new_RS, u_int8_t rsId));
[...]
MATCHER_P(IsHEALAddress, address, "")
{
return (0 == std::memcmp(arg, address, HEAL_ADDR_SIZE));
}
[...]
heal_address const ifAddr = { 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, 15u ,16u, 17u, 18u, 19u, 20u };

EXPECT_CALL(simulator, dsync_select(DSYNC_IF, IsHEALAddress(ifAddr), 2, 3u))
.WillOnce(Return(0))
.RetiresOnSaturation();

ASSERT_EQ(0, theDSyncInstance->dsync_select(DSYNC_IF, ifAddr, 2, 3u));
---

/Martin

Zhanyong Wan (λx.x x)

unread,
Aug 12, 2010, 2:52:18 AM8/12/10
to Martin K. Svensson, Google C++ Mocking Framework
On Wed, Aug 11, 2010 at 12:30 AM, Martin K. Svensson
<martin.k...@netinsight.net> wrote:
> Easier said than done since I've changed it around so many times, and
> there's a huge context.
> The code is currently (got it working with a custom matcher):
>
> ---
> #define HEAL_ADDR_SIZE 20
> typedef u_int8_t heal_address[HEAL_ADDR_SIZE];
> [...]
> extern "C"
> int dsync_select(enum sync_source src, const heal_address ifAddr, int new_RS, u_int8_t rsId)

As I said earlier, const heal_address here is treated as const
u_int8_t*. The array size is lost. Therefore ElementsAre() doesn't
have enough information to work with.

> {
>  assert(theDSyncInstance != NULL);
>  return theDSyncInstance->dsync_select(src, ifAddr, new_RS, rsId);
> }
> [...]
>  MOCK_METHOD4(dsync_select, int (enum sync_source src, const heal_address ifAddr, int new_RS, u_int8_t rsId));
> [...]
> MATCHER_P(IsHEALAddress, address, "")
> {
>  return (0 == std::memcmp(arg, address, HEAL_ADDR_SIZE));
> }
> [...]
>  heal_address const ifAddr = { 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, 15u ,16u, 17u, 18u, 19u, 20u };
>
>  EXPECT_CALL(simulator, dsync_select(DSYNC_IF, IsHEALAddress(ifAddr), 2, 3u))
>  .WillOnce(Return(0))
>  .RetiresOnSaturation();
>
>  ASSERT_EQ(0, theDSyncInstance->dsync_select(DSYNC_IF, ifAddr, 2, 3u));

You said that passing the array by reference didn't work, and I was
asking about that code...

Anyway, it should work. I just tried the following myself and it works fine:

class MockArrayFunc {
public:
// Array passed by reference.
MOCK_METHOD1(ArrayFunc1, void(const int (&array)[3]));
// Array passed by pointer and size.
MOCK_METHOD2(ArrayFunc2, void(const int* ptr, int size));
};

TEST(ElementsAreTest, Works) {
MockArrayFunc f;
int array[3] = { 1, 2, 3 };
int array2[3] = { 2, 3, 4 };

EXPECT_CALL(f, ArrayFunc1(ElementsAreArray(array)));
EXPECT_CALL(f, ArrayFunc1(ElementsAre(2, 3, 4)));
EXPECT_CALL(f, ArrayFunc2(_, _)).With(ElementsAreArray(array));
EXPECT_CALL(f, ArrayFunc2(_, _)).With(ElementsAre(2, 3, 4));

f.ArrayFunc1(array);
f.ArrayFunc1(array2);
f.ArrayFunc2(array, 3);
f.ArrayFunc2(array2, 3);
}

Also, you can always look for examples in gmock's own tests. Go to
the test/ directory, open file gmock-generated-matchers_test.cc, look
for "NativeArray".

> ---
>
> /Martin
>
>
> On Wed, 2010-08-11 at 00:05 -0700, Zhanyong Wan (λx.x x) wrote:
>> On Tue, Aug 10, 2010 at 11:44 PM, Martin K. Svensson
>> <martin.k...@netinsight.net> wrote:
>> > Changing the parameter to a regular element pointer does not seem to
>> > help. Neither did changing it to an array reference or a pointer to an
>> > array.
>>
>> Please post the actual code you wrote.
>>
>
>

--
Zhanyong

Martin K. Svensson

unread,
Aug 12, 2010, 3:55:41 AM8/12/10
to Zhanyong Wan (λx.x, Google C++ Mocking Framework
Well, I must have tried 10 different variants, but this is one of them:

MOCK_METHOD4(dsync_select, int (enum sync_source src, const heal_address & ifAddr, int new_RS, u_int8_t rsId));

The code that calls this is a C-stub that forwards the call to the mock
(which complicates things here). The C-interface is not possible for me
to change.

int dsync_select(enum sync_source src, const heal_address ifAddr, int new_RS, u_int8_t rsId)

{


return theDSyncInstance->dsync_select(src, ifAddr, new_RS, rsId);
}

Compiling the stub gives:

dsync_stub.cpp: In function 'int dsync_select(sync_source, const u_int8_t*, int, u_int8_t)':
dsync_stub.cpp:41: error: no matching function for call to 'DXLStub::DSyncInterface::dsync_select(sync_source&, const u_int8_t*&, int&, u_int8_t&)'
dsync_interface.h:23: note: candidates are: virtual int DXLStub::DSyncInterface::dsync_select(sync_source, const u_int8_t (&)[20], int, u_int8_t)

If I change the mock to:

MOCK_METHOD4(dsync_select, int (enum sync_source src, const unsigned char * ifAddr, int new_RS, u_int8_t rsId));

,then I can compile the stub ok, but then I get:

gmock-matchers.h: In member function 'testing::internal::ElementsAreArrayMatcher<T>::operator testing::Matcher<T>() const [with Container = const unsigned char*, T = unsigned char]':


test.cpp:30: instantiated from here

gmock-matchers.h:2219: error: 'const unsigned char*' is not a class, struct, or union type

in the test code:

heal_address const ifAddr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ,16, 17, 18, 19, 20 };
EXPECT_CALL(simulator, dsync_select(DSYNC_IF, ElementsAreArray(ifAddr), 2, 3u))
.WillOnce(Return(0))
.RetiresOnSaturation();

/Martin

Zhanyong Wan (λx.x x)

unread,
Aug 13, 2010, 1:53:14 AM8/13/10
to Martin K. Svensson, Google C++ Mocking Framework
On Thu, Aug 12, 2010 at 12:55 AM, Martin K. Svensson
<martin.k...@netinsight.net> wrote:
> Well, I must have tried 10 different variants, but this is one of them:
>
>  MOCK_METHOD4(dsync_select, int (enum sync_source src, const heal_address & ifAddr, int new_RS, u_int8_t rsId));
>
> The code that calls this is a C-stub that forwards the call to the mock
> (which complicates things here). The C-interface is not possible for me
> to change.
>
>  int dsync_select(enum sync_source src, const heal_address ifAddr, int new_RS, u_int8_t rsId)
>  {
>    return theDSyncInstance->dsync_select(src, ifAddr, new_RS, rsId);

The bug is in your C stub. Specifically, the above line. The type of
ifAddr is actually const uint32_t*. You cannot implicitly convert it
to a const heal_address&. You have to explicitly cast it using
static_cast.

>  }
>
> Compiling the stub gives:
>
>  dsync_stub.cpp: In function 'int dsync_select(sync_source, const u_int8_t*, int, u_int8_t)':
>  dsync_stub.cpp:41: error: no matching function for call to 'DXLStub::DSyncInterface::dsync_select(sync_source&, const u_int8_t*&, int&, u_int8_t&)'
>  dsync_interface.h:23: note: candidates are: virtual int DXLStub::DSyncInterface::dsync_select(sync_source, const u_int8_t (&)[20], int, u_int8_t)
>
> If I change the mock to:
>
>  MOCK_METHOD4(dsync_select, int (enum sync_source src, const unsigned char * ifAddr, int new_RS, u_int8_t rsId));

Don't do this. The size information isn't present in the API, so no
matcher could possibly know how big the buffer is, unless you write a
custom matcher that hard codes the size.

--
Zhanyong

Martin K. Svensson

unread,
Aug 13, 2010, 2:13:50 AM8/13/10
to Zhanyong Wan (λx.x, Google C++ Mocking Framework
On Thu, 2010-08-12 at 22:53 -0700, Zhanyong Wan (λx.x x) wrote:
> On Thu, Aug 12, 2010 at 12:55 AM, Martin K. Svensson
> <martin.k...@netinsight.net> wrote:
> > Well, I must have tried 10 different variants, but this is one of them:
> >
> > MOCK_METHOD4(dsync_select, int (enum sync_source src, const heal_address & ifAddr, int new_RS, u_int8_t rsId));
> >
> > The code that calls this is a C-stub that forwards the call to the mock
> > (which complicates things here). The C-interface is not possible for me
> > to change.
> >
> > int dsync_select(enum sync_source src, const heal_address ifAddr, int new_RS, u_int8_t rsId)
> > {
> > return theDSyncInstance->dsync_select(src, ifAddr, new_RS, rsId);
>
> The bug is in your C stub. Specifically, the above line. The type of
> ifAddr is actually const uint32_t*. You cannot implicitly convert it
> to a const heal_address&. You have to explicitly cast it using
> static_cast.

D'oh! Yeah, that's what it says... (or u_int8_t at least). Apparently,
I'm blind... or analphabetic. :-)
But anyway, fixing that, it isn't the central problem. See below.

> > }
> >
> > Compiling the stub gives:
> >
> > dsync_stub.cpp: In function 'int dsync_select(sync_source, const u_int8_t*, int, u_int8_t)':
> > dsync_stub.cpp:41: error: no matching function for call to 'DXLStub::DSyncInterface::dsync_select(sync_source&, const u_int8_t*&, int&, u_int8_t&)'
> > dsync_interface.h:23: note: candidates are: virtual int DXLStub::DSyncInterface::dsync_select(sync_source, const u_int8_t (&)[20], int, u_int8_t)
> >
> > If I change the mock to:
> >
> > MOCK_METHOD4(dsync_select, int (enum sync_source src, const unsigned char * ifAddr, int new_RS, u_int8_t rsId));
>
> Don't do this. The size information isn't present in the API, so no
> matcher could possibly know how big the buffer is, unless you write a
> custom matcher that hard codes the size.

Actually, it can, because the test case specifies it below, either
through the size of the matched array, or through a parameter to the
matcher. Anyway, it's just the mock I'm changing, not the API, so the
caller shouldn't make that mistake.

> >
> > ,then I can compile the stub ok, but then I get:
> >
> > gmock-matchers.h: In member function 'testing::internal::ElementsAreArrayMatcher<T>::operator testing::Matcher<T>() const [with Container = const unsigned char*, T = unsigned char]':
> > test.cpp:30: instantiated from here
> > gmock-matchers.h:2219: error: 'const unsigned char*' is not a class, struct, or union type
> >
> > in the test code:
> >
> > heal_address const ifAddr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ,16, 17, 18, 19, 20 };
> > EXPECT_CALL(simulator, dsync_select(DSYNC_IF, ElementsAreArray(ifAddr), 2, 3u))
> > .WillOnce(Return(0))
> > .RetiresOnSaturation();

I still get this problem. By the way, thanks for all your help.

Zhanyong Wan (λx.x x)

unread,
Aug 13, 2010, 2:30:41 AM8/13/10
to Martin K. Svensson, Google C++ Mocking Framework

It cannot really. You can tell ElementsAre() and friends how big the
array is *expected* to be. You cannot tell the matcher how big the
array *actually* is.

> Anyway, it's just the mock I'm changing, not the API, so the
> caller shouldn't make that mistake.
>
>> >
>> > ,then I can compile the stub ok, but then I get:
>> >
>> >  gmock-matchers.h: In member function 'testing::internal::ElementsAreArrayMatcher<T>::operator testing::Matcher<T>() const [with Container = const unsigned char*, T = unsigned char]':
>> >  test.cpp:30:   instantiated from here
>> >  gmock-matchers.h:2219: error: 'const unsigned char*' is not a class, struct, or union type
>> >
>> > in the test code:
>> >
>> >  heal_address const ifAddr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ,16, 17, 18, 19, 20 };
>> >  EXPECT_CALL(simulator, dsync_select(DSYNC_IF, ElementsAreArray(ifAddr), 2, 3u))
>> >  .WillOnce(Return(0))
>> >  .RetiresOnSaturation();
>
> I still get this problem.

Please try the example code I posted yesterday.

--
Zhanyong

Martin K. Svensson

unread,
Aug 13, 2010, 2:52:38 AM8/13/10
to Zhanyong Wan (λx.x, Google C++ Mocking Framework
Finally, this works! I had to play around with casts a bit to get it to
work with a reference:

C interface:


#define HEAL_ADDR_SIZE 20
typedef u_int8_t heal_address[HEAL_ADDR_SIZE];

int dsync_select(enum sync_source src, const heal_address ifAddr, int new_RS, u_int8_t rsId);

C stub:


int dsync_select(enum sync_source src, const heal_address ifAddr, int new_RS, u_int8_t rsId)
{

return theDSyncInstance->dsync_select(src, *reinterpret_cast <const u_int8_t (*) [20]> (ifAddr), new_RS, rsId);
}

C++ mock:
MOCK_METHOD4(dsync_select, int (enum sync_source src, const u_int8_t (& ifAddr) [HEAL_ADDR_SIZE], int new_RS, u_int8_t rsId));

C++ test:


heal_address const ifAddr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ,16, 17, 18, 19, 20 };

EXPECT_CALL(theDSyncSimulator, dsync_select(DSYNC_IF, ElementsAreArray(ifAddr), 2, 3u))
.WillOnce(Return(0)).RetiresOnSaturation();

Thank you very much, mr. Wan. But in conclusion, I think this is way too
much work to accomplish something that really should be simple. I doubt
that I am the only person running into this problem, although most
people probably give up faster and just make a custom matcher.

/Martin

Zhanyong Wan (λx.x x)

unread,
Aug 13, 2010, 3:28:40 AM8/13/10
to Martin K. Svensson, Google C++ Mocking Framework
On Thu, Aug 12, 2010 at 11:52 PM, Martin K. Svensson
<martin.k...@netinsight.net> wrote:
> Finally, this works! I had to play around with casts a bit to get it to
> work with a reference:
>
> C interface:
>  #define HEAL_ADDR_SIZE 20
>  typedef u_int8_t heal_address[HEAL_ADDR_SIZE];
>  int dsync_select(enum sync_source src, const heal_address ifAddr, int new_RS, u_int8_t rsId);
>
> C stub:
>  int dsync_select(enum sync_source src, const heal_address ifAddr, int new_RS, u_int8_t rsId)
>  {
>    return theDSyncInstance->dsync_select(src, *reinterpret_cast <const u_int8_t (*) [20]> (ifAddr), new_RS, rsId);
>  }
>
> C++ mock:
>  MOCK_METHOD4(dsync_select, int (enum sync_source src, const u_int8_t (& ifAddr) [HEAL_ADDR_SIZE], int new_RS, u_int8_t rsId));
>
> C++ test:
>  heal_address const ifAddr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ,16, 17, 18, 19, 20 };
>  EXPECT_CALL(theDSyncSimulator, dsync_select(DSYNC_IF, ElementsAreArray(ifAddr), 2, 3u))
>  .WillOnce(Return(0)).RetiresOnSaturation();
>
> Thank you very much, mr. Wan. But in conclusion, I think this is way too
> much work to accomplish something that really should be simple. I doubt

I agree. C++ should have died a painful death many years ago.

--
Zhanyong

Reply all
Reply to author
Forward
0 new messages