Search code examples
c++catch-unit-test

String convertion of function expression with parameters in Catch assertion


If I run the following test witch Catch

bool eq(int x, int y) {
    return x == y;
}

TEST_CASE("operator vs. function call") {
    int x = 1;
    int y = 2;
    CHECK(x == y);
    CHECK(eq(x, y));
}

I get the following output

/path/to/MyTest.cpp:8: Failure:
  CHECK(x == y)
with expansion:
  1 == 2

/path/to/MyTest.cpp:9: Failure:
  CHECK(eq(x, y))
with expansion:
  false

Why can Catch convert x and y to strings in the operator expression x == y but not in the function call expression eq(x, y)? Is it somehow possible to stringify the function call expression in a similar way to get output like this:

/path/to/MyTest.cpp:9: Failure:
  CHECK(eq(x, y))
with expansion:
  eq(1, 2)

Solution

  • The interesting part is this:

    #define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \
        do { \
            Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
            try { \
                CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
                ( __catchResult <= expr ).endExpression(); \
                CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
            } \
            catch( ... ) { \
                __catchResult.useActiveException( resultDisposition ); \
            } \
            INTERNAL_CATCH_REACT( __catchResult ) \
        } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
        // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
    

    where expr is x == y or eq(x, y).

    How it works for x == y:
    __catchResult <= expr results in __catchResult <= x == y which is equivalent to (__catchResult <= x) == y due to the lower precedence of <= compared to == (see C++ operator precedence). __catchResult <= x wraps x in an object with an == operator, which y gets passed to. That's how expr gets destructured. All the rest is easy to imagine.

    Why it does not work for eq(x, y):

    __catchResult <= expr results in __catchResult <= eq(x, y) and eq(x, y) is evaluated first (no destructuring).

    For the same reason it does not work if the expression is surrounded by parentheses. For example, (x == y) would lead to __catchResult <= (x == y) in which case x == y is evaluated first (no destructuring). That's why CHECK((x == y)); causes output

    /path/to/MyTest.cpp:8: Failure:
      CHECK((x == y))
    with expansion:
      false