Comparison of arrays in google test?
Asked Answered
G

10

118

I am looking to compare two arrays in google test. In UnitTest++ this is done through CHECK_ARRAY_EQUAL. How do you do it in google test?

Genitalia answered 22/9, 2009 at 15:12 Comment(1)
Question was asked in 2009, the solution is quite simple nowadays (see my answer https://mcmap.net/q/186798/-comparison-of-arrays-in-google-test)Loehr
C
166

I would really suggest looking at Google C++ Mocking Framework. Even if you don't want to mock anything, it allows you to write rather complicated assertions with ease.

For example

//checks that vector v is {5, 10, 15}
ASSERT_THAT(v, ElementsAre(5, 10, 15));

//checks that map m only have elements 1 => 10, 2 => 20
ASSERT_THAT(m, ElementsAre(Pair(1, 10), Pair(2, 20)));

//checks that in vector v all the elements are greater than 10 and less than 20
ASSERT_THAT(v, Each(AllOf(Gt(10), Lt(20))));

//checks that vector v consist of 
//   5, number greater than 10, anything.
ASSERT_THAT(v, ElementsAre(5, Gt(10), _));

There's plenty of matchers for every possible situations, and you can combine them to achieve almost anything.

Did I tell you that ElementsAre needs only iterators and size() method on a class to work? So it not only works with any container from STL but with custom containers also.

Google Mock claims to be almost as portable as Google Test and frankly I don't see why you wouldn't use it. It is just purely awesome.

Cultivable answered 9/5, 2010 at 14:53 Comment(9)
I do use google mock. And I agree that it is awesome. I never expected to see something like it for C++.Genitalia
ElementsAreArray is better to compare arrays, since ElementsAre has a limit of 10 elements.Carrillo
Note that you might want to use EXPECT_THAT instead of ASSERT_THAT in tests.Lelia
what if the Elements are std::string? i got a linker error testing::Matcher<std::string const&>::Matcher(char const*) not definedKowalski
As BЈовић mentioned ElementsAreArray here is an example for use of that: EXPECT_THAT(v, ElementsAreArray(u)); which I have had more use of than the current examples.Armalla
Is those a part of gmock project?Or just Gtest project?Ergograph
@TsakiroglouFotis still in gmock: github.com/google/googletest/blob/… I don't understand what that has to do with mocking :-)Millwater
Documentation moved here sometime.Sedan
And matcher documentation moved here.Rim
R
25

If you just need to check if the arrays are equal, then the brute force also works :

int arr1[10];
int arr2[10];

// initialize arr1 and arr2

EXPECT_TRUE( 0 == std::memcmp( arr1, arr2, sizeof( arr1 ) ) );

However, this doesn't tell you which element differs.

Robertroberta answered 17/12, 2012 at 12:23 Comment(0)
F
25
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";

for (int i = 0; i < x.size(); ++i) {
  EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
}

Source

Fredfreda answered 10/10, 2015 at 21:43 Comment(2)
I kinda like this. It doesn't require copying the data to an stl container, and it's just fairly simple. Wrapping this in a macro for a common type of array comparison (like a vector or matrix), is simply done and gets the job done.Ilan
While this may be "simple", it requires multiple lines of code, which you may end up duplicating many times, and the output format is less than ideal when the assertions fail. Matchers exist for a reason, despite their limtiations and learning curve.Stafford
B
22

If you want to compare a c-style array pointer to an array using Google Mock, you can go through std::vector. For example:

uint8_t expect[] = {1, 2, 3, 42};
uint8_t * buffer = expect;
uint32_t buffer_size = sizeof(expect) / sizeof(expect[0]);
ASSERT_THAT(std::vector<uint8_t>(buffer, buffer + buffer_size), 
            ::testing::ElementsAreArray(expect));

Google Mock's ElementsAreArray also accepts pointer and length which allow comparison of two c-style array pointers. For example:

ASSERT_THAT(std::vector<uint8_t>(buffer, buffer + buffer_size), 
            ::testing::ElementsAreArray(buffer, buffer_size));

I spent far too long trying to piece this together. Thanks to this StackOverflow post for the reminder on std::vector iterator initialization. Note that this method will copy the buffer array elements into the std::vector before the comparison.

Bazaar answered 10/9, 2013 at 14:16 Comment(2)
If the bug in the code being tested happens to be that buffer_size, which a value returned from the code under test, gets incorrectly set to (size_t)-1, which is not an uncommon error, then the vector constructor will try to make a very large vector! The test program might be killed with a resource limit or out of memory error, or just plain crash, rather than the test assertion failing. With C++20, using std::span instead of vector should prevent this, since it doesn't need to copy the buffer into a new container.Wolfie
Something similar could be achieved by reinterpret casting the actualData to std::array<type, size> pointer and dereferencing the ptr. in the assert. Something like: gist.github.com/daantimmer/…Involution
C
11

I had the exact same question, so I wrote a couple of macros that do comparisons between two generic containers. It's extensible to ANY container that has const_iterator, begin, and end. If it fails, it will display a verbose message of where the array went wrong and will do so for every element that fails; it will make sure they're the same length; and the location in your code that it reports as failing is the same line where you call EXPECT_ITERABLE_EQ( std::vector< double >, a, b).

//! Using the google test framework, check all elements of two containers
#define EXPECT_ITERABLE_BASE( PREDICATE, REFTYPE, TARTYPE, ref, target) \
    { \
    const REFTYPE& ref_(ref); \
    const TARTYPE& target_(target); \
    REFTYPE::const_iterator refIter = ref_.begin(); \
    TARTYPE::const_iterator tarIter = target_.begin(); \
    unsigned int i = 0; \
    while(refIter != ref_.end()) { \
        if ( tarIter == target_.end() ) { \
            ADD_FAILURE() << #target " has a smaller length than " #ref ; \
            break; \
        } \
        PREDICATE(* refIter, * tarIter) \
            << "Containers " #ref  " (refIter) and " #target " (tarIter)" \
               " differ at index " << i; \
        ++refIter; ++tarIter; ++i; \
    } \
    EXPECT_TRUE( tarIter == target_.end() ) \
        << #ref " has a smaller length than " #target ; \
    }

//! Check that all elements of two same-type containers are equal
#define EXPECT_ITERABLE_EQ( TYPE, ref, target) \
    EXPECT_ITERABLE_BASE( EXPECT_EQ, TYPE, TYPE, ref, target )

//! Check that all elements of two different-type containers are equal
#define EXPECT_ITERABLE_EQ2( REFTYPE, TARTYPE, ref, target) \
    EXPECT_ITERABLE_BASE( EXPECT_EQ, REFTYPE, TARTYPE, ref, target )

//! Check that all elements of two same-type containers of doubles are equal
#define EXPECT_ITERABLE_DOUBLE_EQ( TYPE, ref, target) \
    EXPECT_ITERABLE_BASE( EXPECT_DOUBLE_EQ, TYPE, TYPE, ref, target )

Hope this works for you (and that you actually check this answer two months after your question was submitted).

Clausius answered 5/11, 2009 at 16:3 Comment(4)
That's a great approach! Maybe you could provide this to google so they add it to the framework?Genitalia
They said (code.google.com/p/googletest/issues/detail?id=231) that they discourage adding macros, and this functionality is available to some extent in the Google Mock framework.Clausius
Excellent macro! Suggested small improvement: replace smaller length than with smaller length (" << i << ") than, two places.Operation
Thanks @Technophile! With googlemock these kinds of checks can be constructed more easily, but I still perfer the customizability of googletest. A more advanced version of this macro is available inside the Macros.hh/i.hh files in github.com/celeritas-project/celeritas/blob/master/test/gtest/… if you are curious :)Clausius
A
6

I used a classic loop through all elements. You can use SCOPED_TRACE to read out in which iteration the array elements differ. This provides you with additional information compared to some other approaches and is easy to read.

for (int idx=0; idx<ui16DataSize; idx++)
{
    SCOPED_TRACE(idx); //write to the console in which iteration the error occurred
    ASSERT_EQ(array1[idx],array2[idx]);
}
Abernon answered 3/6, 2015 at 8:37 Comment(0)
S
5

I ran into a similar problem with comparing arrays in google test.

Since I needed comparison with basic void* and char* (for low-level code testing), I don't think either Google Mock (which I'm also using in the project) or Seth's great macro could help me in the particular situation. I wrote the following macro:

#define EXPECT_ARRAY_EQ(TARTYPE, reference, actual, element_count) \
    {\
    TARTYPE* reference_ = static_cast<TARTYPE *> (reference); \
    TARTYPE* actual_ = static_cast<TARTYPE *> (actual); \
    for(int cmp_i = 0; cmp_i < element_count; cmp_i++ ){\
      EXPECT_EQ(reference_[cmp_i], actual_[cmp_i]);\
    }\
    }

The casts are there to make the macro usable when comparing void* to other stuff:

  void* retrieved = ptr->getData();
  EXPECT_EQ(6, ptr->getSize());
  EXPECT_ARRAY_EQ(char, "data53", retrieved, 6)

Tobias in the comments suggested casting void* to char* and using EXPECT_STREQ, a macro I somehow missed before - which looks like a better alternative.

Schoen answered 10/3, 2012 at 17:24 Comment(3)
I would prefer casting the void* to a char* and using EXPECT_STREQ. Wouldn't that work as well?Genitalia
One of the reasons I posted my answer was because I hoped someone would suggest a better alternative. It seems you did, Tobias :)Schoen
EXPECT_STREQ does not work for arbitrary arrays that contain zero elements. I'd still vote for @Schoen 's solution.Anstus
F
4

Below is an assertion I wrote to compare [fragments of] two floating point arrays:

/* See
http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
for thorough information about comparing floating point values.
For this particular application we know that the value range is -1 to 1 (audio signal),
so we can compare to absolute delta of 1/2^22 which is the smallest representable value in
a 22-bit recording.
*/
const float FLOAT_INEQUALITY_TOLERANCE = float(1.0 / (1 << 22));


template <class T>
::testing::AssertionResult AreFloatingPointArraysEqual(
                                const T* const expected,
                                const T* const actual,
                                unsigned long length)
{
    ::testing::AssertionResult result = ::testing::AssertionFailure();
    int errorsFound = 0;
    const char* separator = " ";
    for (unsigned long index = 0; index < length; index++)
    {
        if (fabs(expected[index] - actual[index]) > FLOAT_INEQUALITY_TOLERANCE)
        {
            if (errorsFound == 0)
            {
                result << "Differences found:";
            }
            if (errorsFound < 3)
            {
                result << separator
                        << expected[index] << " != " << actual[index]
                        << " @ " << index;
                separator = ", ";
            }
            errorsFound++;
        }
    }
    if (errorsFound > 0)
    {
        result << separator << errorsFound << " differences in total";
        return result;
    }
    return ::testing::AssertionSuccess();
}

Usage within the Google Testing Framework is this:

EXPECT_TRUE(AreFloatingPointArraysEqual(expectedArray, actualArray, lengthToCompare));

In case of an error, something like the following output is produced:

..\MyLibraryTestMain.cpp:145: Failure
Value of: AreFloatingPointArraysEqual(expectedArray, actualArray, lengthToCompare)
  Actual: false (Differences found: 0.86119759082794189 != 0.86119747161865234 @ 14, -0.5552707314491272 != -0.55527061223983765 @ 24, 0.047732405364513397 != 0.04773232713341713 @ 36, 339 differences in total)
Expected: true

For thorough discussion on comparing floating point values in general, please see this.

Frum answered 28/10, 2012 at 19:29 Comment(0)
L
2

With today's version you just have to declare the operator<< for your enum class type, but in the same namespace as the enum (see https://github.com/google/googletest/blob/main/docs/advanced.md#teaching-googletest-how-to-print-your-values)

namespace Foo
{
    enum class Bar
    {
        Hello,
        Goodbye,
    };

    // In the same namespace !!
    inline std::ostream& operator<<(std::ostream& os, const Bar& v)
    {
        switch (v)
        {
            case Bar::Hello: os << "Hello"; break;
            case Bar::Goodbye: os << "Goodbye"; break;
        }
        return os;
    }
}

TEST(Suite, Demo1)
{
    using namespace Foo;

    vector<Bar> vec1 = { Bar::Hello, Bar::Goodbye };
    vector<Bar> vec2 = { Bar::Goodbye };

    ASSERT_EQ(vec1, vec2);
}
test.cpp(106): error: Expected equality of these values:
  vec1
    Which is: { Hello, Goodbye }
  vec2
    Which is: { Goodbye }
[  FAILED  ] Suite.Demo1 (1 ms)

`` 
Loehr answered 20/1, 2022 at 8:1 Comment(0)
A
0

What I do is make a list-initialization ready print of the vectors and compare the two strings.

Something like this:

std::stringstream expected;
expected_triangles << expected_vector;

std::stringstream output;
o_triangles << output_vector;

EXPECT_EQ(o_triangles.str(), expected_triangles.str());

For this to work, you need to define:

///
/// \brief operator << print a vector in a list-initialization friendly format
/// \param out
/// \param point
/// \return
///
template <typename T>
std::ostream &operator<<(std::ostream &out, std::vector<T> const &vector) 

{
    out << "{";

    if (!vector.empty())
    {
        out << vector[0];
    }

    for (size_t i = 1; i < vector.size(); i++)
    {
        out << ", " << vector[i];
    }

    out << "}";

    return out;
}

It's very convenient also to create test cases from live data, since you just need to log the data and then use it to initialize your test array.

Armington answered 27/9, 2021 at 14:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.