2

First, I am new to googletest framework so be kind.

I have a function

void setID(const int id)
{
    ID = id;
}

where ID is a global unsigned int. (Yes, globals are bad, I am just trying to figure what I am doing.)

My unit test looks like this

TEST_F(TempTests, SetId)
{
    // Arrange
    int id = -99;

    // Act
    setId(id);

    // Assert
    EXPECT_EQ(id, ID);
}

The problem is my unit test always passes and I need it to fail as ID should have been a signed int not an unsigned int. If I hadn't visually caught the error the unit test would have passed and it could have caused errors later on.

To make sure that this doesn't happen in the future it would be best if the unit test comparison failed in this case.

I have tried static casting id and ID to signed and unsigned ints in various ways. I have tried doing EXPECT_TRUE(id == ID) with static casting the variables to signed and unsigned ints in various ways. But in all of these cases the result is a passing test.

So how can I get gtest to compare the signed value of id and unsigned value of ID so that the test will fail because id will be -99 and ID will be 4294967197?

Fred
  • 1,054
  • 1
  • 12
  • 32
  • Why not just check to see if `id` is below 0? – NathanOliver Jan 04 '16 at 19:41
  • 1
    I don't understand your question. How does that address what the value of `ID` is? – Fred Jan 04 '16 at 19:43
  • 2
    How about adding additional expectations that check if both type are signed or unsigned (by using type_traits, decltype or templates)? – mkk Jan 04 '16 at 21:11
  • Good idea, I will look into this – Fred Jan 05 '16 at 14:49
  • 2
    Turn on the compiler warning(s) that warn about comparisons of signed to unsigned values. Treat the warnings as bugs, and fix the code. – Adrian McCarthy Jan 05 '16 at 18:19
  • I agree about fixing the code which is why I wanted the unit test to reflect it. I wasn't aware that I could treat warnings of only signed to unsigned values as errors, but treat the rest of the warnings as warnings. I agree that it best to treat all warning as errors, but that isn't an option at this time. – Fred Jan 06 '16 at 15:48

2 Answers2

2

The compiler will need to convert the types to be equal. I recommend reading this related answer.

You might be able to create a custom googletest comparator. Even if not, you can at the very least use something similar like such:

#include <iostream>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <typeinfo>

template <class T>
class SignedUnsignedIntCompare final /* final for non-virtual dtor, remember to make dtor virtual if you need to inherit */ {
public:
    const T & v;
    SignedUnsignedIntCompare(const T & v) : v(v) {}
    SignedUnsignedIntCompare(SignedUnsignedIntCompare && move_ctor) = default;
    SignedUnsignedIntCompare(const SignedUnsignedIntCompare & copy_ctor) = default;
    SignedUnsignedIntCompare & operator=(SignedUnsignedIntCompare && move_assign) = default;
    SignedUnsignedIntCompare & operator=(const SignedUnsignedIntCompare & copy_assign) = default;
    ~SignedUnsignedIntCompare() = default;

    template <class TT>
    bool operator==(const TT & i) const {
        if ( std::numeric_limits<T>::is_signed != std::numeric_limits<TT>::is_signed ) {
            return ((v == i) && (T(v) <= std::numeric_limits<TT>::max()) && (TT(i) <= std::numeric_limits<T>::max()));
        }
        return (v == i);
    }
};

typedef SignedUnsignedIntCompare<int> SignedIntCompare;
typedef SignedUnsignedIntCompare<unsigned> UnsignedIntCompare;

int main() {
    int i = -99;
    unsigned int u = i;

    std::cout << (i == u) << " vs " << (SignedIntCompare(i) == u) << std::endl;

    return 0;
}

At that point, you can then use EXPECT_TRUE or similar boolean checks, like such:

TEST(foo, bar) {
    int id = -99;
    setId(id);
    EXPECT_TRUE(SignedUnsignedIntCompare<decltype(ID)>(ID) == id);
}
Community
  • 1
  • 1
inetknght
  • 4,300
  • 1
  • 26
  • 52
1

So I'm not sure how to give credit, but I ended up using a combination of inetknght's and mkk's suggestions.

TEST_F(TempTests, SetId)
{
    // Arrange
    int id = -99;

    // Act
    setId(id);

   // Assert
   EXPECT_TRUE(std::numeric_limits<decltype(id)>::is_signed == std::numeric_limits<decltype(ID)>::is_signed);
   EXPECT_EQ(id, ID);
}

Per inetknght's suggestion, by checking the signed types I am able to fail the test as the types are not both signed. And per mkk's suggestion by using decltype I can get the declared types of the variables without modifying the unit test in the future when the type of ID is corrected. When the type of ID is corrected then the test passes.

Edit

Per Adrian McCarthy's suggestion I have also added -Werror=conversion to my compiler flags.

Fred
  • 1,054
  • 1
  • 12
  • 32