9

Is there any possibility in Catch C++ Unit test framework to compare std::vectors that are floating point type based? I know that I can compare size of both containers and each element (using Approx) but this is messy.

Comparison of integral types vector works properly.

Now, I must use such construction

REQUIRE(computed.size() == expected.size());
for (size_t i = 0; i < computed.size(); ++i)
    REQUIRE(computed[i] == Approx(expected[i]));

But I would like to use one liner (it works for integral types):

REQUIRE(computed == expected);
dlmeetei
  • 9,905
  • 3
  • 31
  • 38
miqelm
  • 354
  • 4
  • 13
  • 1
    What is the problem with exact comparison? – n. m. could be an AI Dec 15 '16 at 10:04
  • 2
    Rounding errors – miqelm Dec 15 '16 at 10:07
  • 1
    If your rounding errors are inconsistent between runs, your unit test better report that. – n. m. could be an AI Dec 15 '16 at 10:12
  • Expected results are loaded from text file and they are compared to calculated ones, so here's the difference. – miqelm Dec 15 '16 at 10:23
  • How do you create the file with expected results? Calculate them with a different method? Then the difference between the "expected" and "actual" results should stay exactly the same forever. This means you can verify it once, and just replace expected results with verified actual results. – n. m. could be an AI Dec 15 '16 at 10:31
  • Expected values are generated in different language and saved to text form, so not ideal floating point value representation is saved to file. Due to this fact, I am not able to compare exact values. There is mismatch in values about 1e-6. – miqelm Dec 15 '16 at 10:40
  • why is comparing elements messy? it's just one `std::equal` call with a proper predicate for approximation. It compares the sizes for you too – Ap31 Dec 15 '16 at 10:53
  • See code snippets I add by editing the question. Catch framework has functionality of proper comparison floating point values, but it seems to not have comparison for floating poing containers. – miqelm Dec 15 '16 at 11:02
  • 6
    @miqelm yeah but as I said, `std::equal` can relieve you of all the messiness except the actual approximation which is kinda unavoidable: `REQUIRE(std::equal(computed.begin(),computed.end(),expected.begin(),expected.end(), [](float x, float y)->bool { return x==Approx(y); }));` – Ap31 Dec 15 '16 at 11:06
  • Ok, that's nice solution, not very pretty, but works. – miqelm Dec 15 '16 at 11:29
  • 1
    yaeah, hopefully someday we'll have standart ranges:) As for the lambda, it's probably better to just define a generic function somewhere and just pass it to `std::equal`, will save some space too – Ap31 Dec 15 '16 at 11:43

3 Answers3

6

The construction below is provided by Catch2 itself. No need to roll your own.

 REQUIRE_THAT( computed, Catch::Approx(expected).epsilon(1.e-5) );
Etuka Onono
  • 61
  • 2
  • 3
5

You can write

CHECK_THAT(actual, EqualsApprox(expected));

using this custom matcher:

#include <vector>
#include <functional>

#include <catch.hpp>

template<typename T, typename Compare>
struct CompareMatcher
        : Catch::Matchers::Impl::MatcherBase<std::vector<T>, std::vector<T> > {

    CompareMatcher(const std::vector<T> &comparator, const Compare &compare)
            : m_comparator(comparator),
              m_compare(compare) {}

    bool match(const std::vector<T> &v) const CATCH_OVERRIDE {
        if (m_comparator.size() != v.size()) {
            return false;
        }
        for (size_t i = 0; i < v.size(); ++i) {
            if (!m_compare(m_comparator[i], v[i])) {
                return false;
            }
        }
        return true;
    }

    virtual std::string describe() const CATCH_OVERRIDE {
        return "Equals: " + Catch::toString(m_comparator);
    }

    const std::vector<T> &m_comparator;
    Compare const &m_compare;
};

template<typename T, typename C>
CompareMatcher<T, C>
Compare(const std::vector<T> &comparator, const C &compare) {
    return CompareMatcher<T, C>(comparator, compare);
}

auto EqualsApprox(const std::vector<double> &comparator) {
    return Compare(comparator, [=](double actual, double expected) {
        return actual == Approx(expected);
    });
}

TEST_CASE("example", "[]") {
    SECTION("passes") {
        std::vector<double> actual {0, 1.00001};
        std::vector<double> expected {0, 1};
        CHECK_THAT(actual, EqualsApprox(expected));
    }
    SECTION("fails") {
        std::vector<double> actual {0, 1.0001};
        std::vector<double> expected {0, 1};
        CHECK_THAT(actual, EqualsApprox(expected));
    }
}
maiermic
  • 4,764
  • 6
  • 38
  • 77
1

I had the same need and I decided to use the simple macro:

#define CHECK_VEC_EQUAL(x, y) \
    REQUIRE(x.size() == y.size()); \
    for (size_t i = 0; i < x.size(); ++i) { \
            if (x[i] != Approx(y[i])) { \
                    REQUIRE(x[i] == Approx(y[i])); \
            } \
    }

This macro can be used as a one-liner and each successful comparison only counts as a single assertion.

DzedCPT
  • 133
  • 6