#include "catch.hpp"
namespace Catch {
template <typename T> bool approximately_equal(const T& lhs, const T& rhs, double epsilon, double scale);
template <typename T> bool approximately_equal(std::vector<T> const& lhs, std::vector<T> const& rhs, double epsilon, double scale) { if (lhs.size() != rhs.size()) return false;
bool are_equal = true; for (auto i = 0u; i < lhs.size(); ++i) { are_equal = are_equal && approximately_equal(rhs[i], lhs[i], epsilon, scale); if (!are_equal) break; } return are_equal; }
template <> inline bool approximately_equal(double const& lhs, double const& rhs, double epsilon, double scale) { // Thanks to Richard Harris for his help refining this formula return fabs(lhs - rhs) < epsilon * (scale + (std::max)(fabs(lhs), fabs(rhs))); }
template <typename T> class Approx { public: explicit Approx(const T& value) : m_epsilon(std::numeric_limits<float>::epsilon() * 100) , m_scale(1.0) , m_value(value) {}
Approx(Approx const& other) : m_epsilon(other.m_epsilon) , m_scale(other.m_scale) , m_value(other.m_value) {}
static Approx custom() { return Approx(T()); }
Approx operator()(const T& value) { Approx approx(value); approx.epsilon(m_epsilon); approx.scale(m_scale); return approx; }
friend bool operator == (const T& lhs, Approx const& rhs) { return approximately_equal(lhs, rhs.m_value, rhs.m_epsilon, rhs.m_scale); }
friend bool operator == (Approx const& lhs, const T& rhs) { return operator==(rhs, lhs); }
friend bool operator != (const T& lhs, Approx const& rhs) { return !operator==(lhs, rhs); }
friend bool operator != (Approx const& lhs, const T& rhs) { return !operator==(rhs, lhs); }
Approx& epsilon(double newEpsilon) { m_epsilon = newEpsilon; return *this; }
Approx& scale(double newScale) { m_scale = newScale; return *this; }
std::string toString() const { std::ostringstream oss; oss << "Approx( " << Catch::toString(m_value) << " )"; return oss.str(); }
private: double m_epsilon; double m_scale; T m_value; };} // end namespace Catch
namespace Catch {
template<> inline std::string toString<Catch::Approx<std::vector<double> > >(Catch::Approx<std::vector<double> > const& value) { return value.toString(); }
template<> inline std::string toString<Catch::Approx<double> >(Catch::Approx<double> const& value) { return value.toString(); }}
namespace{ template <typename T> Catch::Approx<T> approx(T const& t) { return Catch::Approx<T>(t); }
template <typename T> Catch::Approx<T> approx(T const& t, double epsilon) { auto a = Catch::Approx<T>(t); a.epsilon(epsilon); return a; }}std::vector<double> v1 = { 1, 2, 3 };std::vector<double> v2 = { 1, 2.1, 3 };
REQUIRE(v1 == approx(v2, 1.0e-3));REQUIRE_THAT( v1, ContainsApproximately( v2 ).withEpsilon( 1.0e-3 ) );struct ContainsApproximately : Catch::Matchers::Impl::MatcherImpl<ContainsApproximately, std::vector<float> > { ContainsApproximately(std::vector<float> const& v) : m_data(v) {}
ContainsApproximately(ContainsApproximately const& other) : m_data(other.m_data) , m_scale(other.m_scale) , m_epsilon(other.m_epsilon) , m_index_first_unequal_value(other.m_index_first_unequal_value) , m_first_unequal_value(other.m_first_unequal_value) {}
virtual ~ContainsApproximately() = default;
virtual bool match(std::vector<float> const& expr) const override { for (auto i = 0u; i < m_data.size(); ++i) { if (!approximately_equal(expr[i], m_data[i])) { m_first_unequal_value = expr[i]; m_index_first_unequal_value = i; return false; } } return true; }
virtual std::string toString() const override { return "Values are not approximately equal at index " + std::to_string(m_index_first_unequal_value) + ", expected " +
std::to_string(m_data[m_index_first_unequal_value]) + ", found " + std::to_string(m_first_unequal_value) + "."; }
private:
bool approximately_equal(double const& lhs, double const& rhs) const { // Thanks to Richard Harris for his help refining this formula return fabs(lhs - rhs) < m_epsilon * (m_scale + (std::max)(fabs(lhs), fabs(rhs))); }
std::vector<float> m_data; double m_scale = 1.0; const double m_epsilon = 1e-3; mutable std::size_t m_index_first_unequal_value = 0; mutable float m_first_unequal_value = std::numeric_limits<float>::quiet_NaN();};TEST_CASE("Vectors differ at index 1"){ std::vector<float> v1{ 1.0f, 2.0f, 3.0f }, v2{ 1.0f, 2.1f, 3.0f }; REQUIRE_THAT(v1, ContainsApproximately(v2));}FAILED: REQUIRE_THAT( v1, ContainsApproximately(v2) )with expansion: { 1.0f, 2.0f, 3.0f } Values are not approximately equal at index 0, expected 1.000000, found nan.Enter code here...#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ try { \ __catchResult \ .setLhs( Catch::toString( arg ) ) \ .setOp( "matches" ) \ .setResultType( (matcher).match( arg ) ); \ std::string matcherAsString = (matcher).toString(); \ __catchResult.setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ); \ __catchResult.captureExpression(); \ } catch( ... ) { \ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() )
TEST_CASE("Vectors differ at index 1"){ std::vector<float> v1{ 1.0f, 2.0f, 3.0f }, v2{ 1.0f, 2.1f, 3.0f };
auto matcher = ContainsApproximately(v2); REQUIRE_THAT(v1, matcher);}FAILED: REQUIRE_THAT( v1, matcher ) with expansion: { 1.0f, 2.0f, 3.0f } Values are not approximately equal at index 1, expected 2.100000, found 2.000000. #define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \
do { \
Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
auto matcher_copy(matcher.clone()); \
try { \
__catchResult \
.setLhs( Catch::toString( arg ) ) \
.setOp( "matches" ) \
.setResultType( (matcher_copy)->match( arg ) ); \
std::string matcherAsString = (matcher_copy)->toString(); \
__catchResult.setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ); \
__catchResult.captureExpression(); \
} catch( ... ) { \
__catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
} \
INTERNAL_CATCH_REACT( __catchResult ) \
} while( Catch::alwaysFalse() )