Help save net neutrality! Learn more.
Close

#4 ASSERT_EQUALS support for different types

open-postponed
nobody
5
2002-07-12
2001-10-17
Dale King
No

Below is a problem my SQA engineer was having using
CPPUNIT_ASSERT_EQUAL with a numeric literal and a long
variable. I show how to eliminate this problem, but I
am submitting this bug report to see if maybe
something could be done to eliminate this potential
source of confusion. It is probably a common
occurrence for people to not be correct about int
versus long literals.

One way perhaps is to use 2 different types for the
template. There is no real requirement that the 2 be
exactly the same type, just that an == comparison is
valid, which it will be between and int and a long.

------------------
> I just got my simple versiontest example running. I
still
> couldn't get CPPUNIT_ASSERT_EQUAL to work. I think
there's
> some sort of type cast problem. For example:
>
> CPPUNIT_ASSERT_EQUAL( 255,submin );
>
> produces the following preprocessor/compiler error
>
> H:\testversion.cpp(31) : error C2664: 'void __cdecl
> CppUnit::TestAssert::assertEquals
(double,double,double,long,cl
> ass std::basic_string<char,struct
> std::char_traits<char>,class std::allocator<char>
>)' :
> cannot convert parameter 4 from 'char [91]' to 'long'
> This conversion requires a reinterpret_cast,
a
> C-style cast or function-style cast
> Error executing cl.exe.
>
> Not sure why... Let me know if you have any ideas.

That is because what you are testing are longs and the
macros are calling a function that is overloaded to
have two different sets of parameters. One is a
template function, the other is for double. The
template version (which is the one you want in this
case) expects the two parameters to be the same type.
It may seem strange, but what you have there are two
different types. In C and C++ there is a difference
between int literals and long literals. What you have
done is give it an int literal and a long variable. It
says they are not the same so decides that you do not
want the template version and instead tries to use the
double version, but it doesn't have the right
parameters for that so you get a compiler error.

To get it to use the correct method you need to make
the values have the same type. There are 3 ways to do
that. The easiest way is simply to use a long literal
as in:

CPPUNIT_ASSERT_EQUAL( 255L, submin );

You can store the expected value in a variable as in
the following to guarantee it is long type.

long expected = 255;
CPPUNIT_ASSERT_EQUAL( expected,submin );

Or cast the second parameter to int so they are both
int:

CPPUNIT_ASSERT_EQUAL( 255, (int)submin );

But I will report this as a bug to the cppunit project.

Discussion

  • Steven Robbins

    Steven Robbins - 2001-10-19

    Logged In: YES
    user_id=130415

    This has come up on the cppunit-devel mailing list. I'm not
    sure that we've decided what to do about it yet, but have a
    look there for opinions and do let us know yours.

    -smr99

     
  • Dale King

    Dale King - 2001-10-19

    Logged In: YES
    user_id=130378

    My proposal is to split the template method to have 2 class
    template parameters. In my proposal I will use Te and Ta
    for the expected type and the actual type. Replace them
    with better names.

    This entails doing the same to the assertion_traits
    template. In that case I would split out the toString stuff
    from the equals test as follows:

    template <class Te, class Ta>
    struct assertion_traits
    {
    static bool equal( const Te& x, const Ta& y )
    {
    return x == y;
    }
    };

    template <class T>
    struct assertion_tostring
    {
    static std::string toString( const T& x )
    {
    OStringStream ost;
    ost << x;
    return ost.str();
    }
    };

    Then you change the template method as follows:

    template <class Te, class Ta>
    void assertEquals( const Te& expected,
    const Ta& actual,
    long lineNumber =
    Exception::UNKNOWNLINENUMBER,
    std::string fileName =
    Exception::UNKNOWNFILENAME )
    {
    if ( !assertion_traits<Te,Ta>::equal
    (expected,actual) )
    {
    assertNotEqualImplementation(
    assertion_tostring<Te>::toString(expected),

    assertion_tostring<Ta>::toString(actual),
    lineNumber,
    fileName );
    }
    }

    You also have to change the toString stuff in the double
    version.

    That seems to solve the issue.

     
  • Dale King

    Dale King - 2001-10-19

    Logged In: YES
    user_id=130378

    My proposal is to split the template method to have 2 class
    template parameters. In my proposal I will use Te and Ta
    for the expected type and the actual type. Replace them
    with better names.

    This entails doing the same to the assertion_traits
    template. In that case I would split out the toString stuff
    from the equals test as follows:

    template <class Te, class Ta>
    struct assertion_traits
    {
    static bool equal( const Te& x, const Ta& y )
    {
    return x == y;
    }
    };

    template <class T>
    struct assertion_tostring
    {
    static std::string toString( const T& x )
    {
    OStringStream ost;
    ost << x;
    return ost.str();
    }
    };

    Then you change the template method as follows:

    template <class Te, class Ta>
    void assertEquals( const Te& expected,
    const Ta& actual,
    long lineNumber =
    Exception::UNKNOWNLINENUMBER,
    std::string fileName =
    Exception::UNKNOWNFILENAME )
    {
    if ( !assertion_traits<Te,Ta>::equal
    (expected,actual) )
    {
    assertNotEqualImplementation(
    assertion_tostring<Te>::toString(expected),

    assertion_tostring<Ta>::toString(actual),
    lineNumber,
    fileName );
    }
    }

    You also have to change the toString stuff in the double
    version.

    That seems to solve the issue.

     
  • Baptiste Lepilleur

    • labels: 104122 -->
    • milestone: 102323 -->
    • summary: ASSERT_EQUALS and numeric constants --> ASSERT_EQUALS support for different types
    • status: open --> open-postponed
     
  • Baptiste Lepilleur

    • labels: --> System - Assertions
     
  • Dom Lachowicz

    Dom Lachowicz - 2008-10-01

    My company is using the following minimal patch. IMO, you want to separate the "comparable" trait from the "string-able" trait. It's really annoying that you can't ASSERT_EQUAL(std::string, const char*) or ASSERT_EQUAL(int, enum) without needing to do cast or constructor.

    Index: include/cppunit/TestAssert.h

    --- include/cppunit/TestAssert.h (revision 62889)
    +++ include/cppunit/TestAssert.h (working copy)
    @@ -35,15 +35,15 @@
    * };
    * \endcode
    */
    -template <class T>
    +template <class T1, class T2=T1>
    struct assertion_traits
    {
    - static bool equal( const T& x, const T& y )
    + static bool equal( const T1& x, const T2& y )
    {
    return x == y;
    }

    - static std::string toString( const T& x )
    + static std::string toString( const T1& x )
    {
    OStringStream ost;
    ost << x;
    @@ -90,16 +90,16 @@
    * Use CPPUNIT_ASSERT_EQUAL instead of this function.
    * \sa assertion_traits, Asserter::failNotEqual().
    */
    -template <class T>
    -void assertEquals( const T& expected,
    - const T& actual,
    +template <class T1, class T2>
    +void assertEquals( const T1& expected,
    + const T2& actual,
    SourceLine sourceLine,
    const std::string &message )
    {
    - if ( !assertion_traits<T>::equal(expected,actual) ) // lazy toString conversion...
    + if ( !assertion_traits<T1,T2>::equal(expected,actual) ) // lazy toString conversion...
    {
    - Asserter::failNotEqual( assertion_traits<T>::toString(expected),
    - assertion_traits<T>::toString(actual),
    + Asserter::failNotEqual( assertion_traits<T1, T2>::toString(expected),
    + assertion_traits<T2, T1>::toString(actual),
    sourceLine,
    message );
    }

     

Log in to post a comment.