schorsch312
schorsch312

Reputation: 5714

CATCH unit testing for C++ compare std::array

I like to use catch for my c++ unit tests. My goal is to compare std::array and std::vector. I created the this failing example.

#define CATCH_CONFIG_MAIN
#include "catch.hpp"

TEST_CASE("Vector") {
    std::vector<double> direction = {0.1, 0.3, 0.4};
    std::vector<double> false_direction = {0.1, 0.0, 0.4};
    REQUIRE(direction == false_direction);
}

TEST_CASE("Array") {
    std::array<double, 3> direction = {0.1, 0.3, 0.4};
    std::array<double, 3> false_direction = {0.1, 0.0, 0.4};
    REQUIRE(direction == false_direction);
}

The output of this test is for the check of std::vector

REQUIRE( direction == false_direction ) with expansion: { 0.1, 0.3, 0.4 } == { 0.1, 0.0, 0.4 }

and for std::array

REQUIRE( direction == false_direction ) with expansion: {?} == {?}

What can I do to display the actual and expected value? I like to have the very same display in a violated REQUIRE condition for std::array as for std::vector.

I use the latest version of catch (v1.10.0).

Upvotes: 4

Views: 4180

Answers (3)

Xarn
Xarn

Reputation: 3560

Fundamentally this is a question of how a type is stringified, and for that there is always the documentation.

The abridged version is that there is a simple algorithm

  1. Check for specialization of Catch::StringMaker for given type. If exists, use it.

  2. Check for operator<< overload for given type. If exists, use it.

  3. Use "{?}".

Until recently, Catch provided a specialization for std::vector out of the box, but not for std::array, because std::array was part of C++11 and generally less used. Since version 2.1.0 Catch instead checks whether the type provides a container-like interface, specifically, responds to begin(T) and end(T). This provides an automatic stringification for many different types, including std::vector, std::array, but also static arrays.

Upvotes: 4

schorsch312
schorsch312

Reputation: 5714

I traced the problem down the the toString method within the catch header. It is missing the overload for std::array, std::vector has already be instantiated. I will commit this change to the catch project.

// already exists in the catch header
template<typename T, typename Allocator>
std::string toString( std::vector<T,Allocator> const& v ) {
    return Detail::rangeToString( v.begin(), v.end() );
}

// my modification in the catch header
template<typename T, std::size_t _Nm>
std::string toString( std::array<T, _Nm> const& v ) {
    return Detail::rangeToString( v.begin(), v.end() );
}

Upvotes: 1

Daniel Trugman
Daniel Trugman

Reputation: 8511

I didn't check Catch's source code to see how exactly they implement the REQUIRE clause, and why it won't work but vector does. But here is a workaround:

#define COMPARE_ARRAYS(lhs, rhs) compareArrays(Catch::getResultCapture().getCurrentTestName(), __LINE__, lhs, rhs)

template < typename T, size_t N >
void compareArrays(const std::string & test, unsigned line, std::array<T, N> lhs, std::array<T, N> rhs) {
  std::vector<T> lv(lhs.begin(), lhs.end());
  std::vector<T> rv(rhs.begin(), rhs.end());
  INFO("Test case [" << test << "] failed at line " << line); // Reported only if REQUIRE fails
  REQUIRE(lv == rv);
}

TEST_CASE("Array") {
    std::array<double, 3> direction = {0.1, 0.3, 0.4};

    std::array<double, 3> true_direction = {0.1, 0.3, 0.4};
    COMPARE_ARRAYS(direction, true_direction);

    std::array<double, 3> false_direction = {0.1, 0.0, 0.4};
    COMPARE_ARRAYS(direction, false_direction);
}

Upvotes: 1

Related Questions