Home > Enterprise >  GoogleTest comparing std::vectors of different sizes
GoogleTest comparing std::vectors of different sizes

Time:04-20

I wrote a class to help me compare two std::vectors that won't necessarily have the same length. The class itself is working fine, but GoogleTest isn't using the == operator, so using EXPECT_EQ(foo, bar) fails while EXPECT_TRUE(foo == bar) works:

#include <vector>

#include <gtest/gtest.h>

template<class T>
class MyVector: public std::vector<T> {
public:

    MyVector(std::initializer_list<T> il)
        : std::vector<T>(il){}

    bool operator== (const std::vector<T>& other)
    {
        // Need to static_cast so we don't call this function recursively
        if( this->size() < other.size() ) {
            return (
                static_cast<std::vector<T>&>(*this) == std::vector<T>(other.begin(), other.begin() this->size())
            );
        } else if( this->size() > other.size() ) {
            return (
                std::vector<T>(this->begin(), this->begin() other.size()) == other
            );
        }
        return (static_cast<std::vector<T>&>(*this) == other);
    }
};

TEST(SOME_TEST, TestMyVector) {
    MyVector<int> foo {1, 2, 3, 4};
    std::vector<int> bar {1, 2, 3, 4, 5};

    EXPECT_TRUE(foo == bar);

    EXPECT_TRUE(foo == (std::vector<int>{1, 2, 3, 4}));
    EXPECT_TRUE(foo == (std::vector<int>{1, 2, 3}));
    EXPECT_TRUE(foo == (std::vector<int>{1, 2, 3, 4, 5}));

    EXPECT_EQ(foo, bar);
}

When I run the above test, I get:

[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from SOME_TEST
[ RUN      ] SOME_TEST.TestMyVector
./my_test.cpp:38: Failure
Expected equality of these values:
  foo
    Which is: { 1, 2, 3, 4 }
  bar
    Which is: { 1, 2, 3, 4, 5 }
[  FAILED  ] SOME_TEST.TestMyVector (0 ms)
[----------] 1 test from SOME_TEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] SOME_TEST.TestMyVector

 1 FAILED TEST

How can I tell GTest to use my class, or what function do I need to write so that EXPECT_EQ works?

Thanks!

CodePudding user response:

Drew Dormann in comments is right. Your operator == requires left-hand side operand to be non-const, so when GTest passes arguments as const T&, compiler cannot select your operator and falls back to operator==(std::vector, std::vector).

Making your operator const-correct makes the test pass:

bool operator== (const std::vector<T>& other) const
{
    // Need to static_cast so we don't call this function recursively
    if( this->size() < other.size() ) {
        return (
            static_cast<const std::vector<T>&>(*this) == std::vector<T>(other.begin(), other.begin() this->size())
        );
    } else if( this->size() > other.size() ) {
        return (
            std::vector<T>(this->begin(), this->begin() other.size()) == other
        );
    }
    return (static_cast<const std::vector<T>&>(*this) == other);
}

However, this is not the only problem you will encounter, starting from the fact that bar == foo is different than foo == bar and that inheriting from standard containers is bad. Consider replacing this with a free function (see it online):

#include <algorithm>

template<class T>
bool begin_eq(const std::vector<T>& lhs, const std::vector<T>& rhs)
{
    // copy-free
    auto [i1, i2] = std::mismatch(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
    bool they_are_equal = i1 == lhs.end() && i2 == rhs.end();
    bool one_reached_end = i1 != lhs.end() || i2 != rhs.end();
    return they_are_equal || one_reached_end;

    //could be also done with C  20 views
} 

CodePudding user response:

Thanks for the comments, everyone.

@yksisarvinen, thank you so much for suggesting a non-copy comparison. Unfortunately the begin_eq function was failing for EXPECT_FALSE(begin_eq(foo, std::vector<int>{1, 2, 3, 0})); because in this case i2 != rhs.end().

You and Nathan Pierson are correct that a free function is much better, I was over-complicating things by overloading the == operator.

My current code is:

#include <algorithm>

#include <gtest/gtest.h>

template<class T>
bool begin_eq(const std::vector<T>& lhs, const std::vector<T>& rhs)
{
    // copy-free
    auto [i1, i2] = std::mismatch(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
    bool they_are_equal = i1 == lhs.end() && i2 == rhs.end();
    bool one_reached_end = i1 != lhs.end() || i2 != rhs.end();
    return they_are_equal || one_reached_end;

    //could be also done with C  20 views
}

template<typename T>
bool another_begin_eq(const std::vector<T>& lhs, const std::vector<T>& rhs)
{
    // copy-free
    auto lhs_it = lhs.begin();
    auto rhs_it = rhs.begin();
    size_t size = (lhs.size() <= rhs.size() ? lhs.size() : rhs.size());
    for(size_t i = 0; i < size; i  ) {
        if( *lhs_it   != *rhs_it  ) return false;
    }
    return true;
} 

TEST(SOME_TEST, TestMyVector) {
    std::vector<int> foo {1, 2, 3, 4};
    std::vector<int> bar {1, 2, 3, 4, 5};

    EXPECT_FALSE(foo == bar);

    EXPECT_TRUE(begin_eq(foo, bar));

    EXPECT_TRUE(begin_eq(foo, std::vector<int>{1, 2, 3, 4}));
    EXPECT_TRUE(begin_eq(foo, std::vector<int>{1, 2, 3}));
    EXPECT_TRUE(begin_eq(foo, std::vector<int>{1, 2, 3, 4, 5}));

    EXPECT_FALSE(another_begin_eq(foo, std::vector<int>{1, 2, 3, 0}));
    EXPECT_FALSE(another_begin_eq(foo, std::vector<int>{1, 2, 0}));
    EXPECT_FALSE(another_begin_eq(foo, std::vector<int>{1, 2, 3, 0, 5}));

    EXPECT_FALSE(begin_eq(foo, std::vector<int>{1, 2, 3, 0}));
    EXPECT_FALSE(begin_eq(foo, std::vector<int>{1, 2, 0}));
    EXPECT_FALSE(begin_eq(foo, std::vector<int>{1, 2, 3, 0, 5}));

    EXPECT_NE(foo, bar);
}

Which yields:

[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from SOME_TEST
[ RUN      ] SOME_TEST.TestMyVector
/untether/workspace/imAIgine/runtimeExports/libuntetherprocessing/tests/test_my_vector.cpp:46: Failure
Value of: begin_eq(foo, std::vector<int>{1, 2, 3, 0})
  Actual: true
Expected: false
/untether/workspace/imAIgine/runtimeExports/libuntetherprocessing/tests/test_my_vector.cpp:47: Failure
Value of: begin_eq(foo, std::vector<int>{1, 2, 0})
  Actual: true
Expected: false
/untether/workspace/imAIgine/runtimeExports/libuntetherprocessing/tests/test_my_vector.cpp:48: Failure
Value of: begin_eq(foo, std::vector<int>{1, 2, 3, 0, 5})
  Actual: true
Expected: false
[  FAILED  ] SOME_TEST.TestMyVector (0 ms)
[----------] 1 test from SOME_TEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] SOME_TEST.TestMyVector
  • Related