Add ULP and margin matcher

Closes #1074
This commit is contained in:
Martin Hořeňovský 2017-11-10 18:14:42 +01:00
parent 24e6d5fa33
commit 0b1f1b1003
11 changed files with 797 additions and 8 deletions

View File

@ -140,6 +140,7 @@ set(INTERNAL_HEADERS
${HEADER_DIR}/internal/catch_leak_detector.h ${HEADER_DIR}/internal/catch_leak_detector.h
${HEADER_DIR}/internal/catch_list.h ${HEADER_DIR}/internal/catch_list.h
${HEADER_DIR}/internal/catch_matchers.h ${HEADER_DIR}/internal/catch_matchers.h
${HEADER_DIR}/internal/catch_matchers_floating.h
${HEADER_DIR}/internal/catch_matchers_string.h ${HEADER_DIR}/internal/catch_matchers_string.h
${HEADER_DIR}/internal/catch_matchers_vector.h ${HEADER_DIR}/internal/catch_matchers_vector.h
${HEADER_DIR}/internal/catch_message.h ${HEADER_DIR}/internal/catch_message.h
@ -206,6 +207,7 @@ set(IMPL_SOURCES
${HEADER_DIR}/internal/catch_list.cpp ${HEADER_DIR}/internal/catch_list.cpp
${HEADER_DIR}/internal/catch_leak_detector.cpp ${HEADER_DIR}/internal/catch_leak_detector.cpp
${HEADER_DIR}/internal/catch_matchers.cpp ${HEADER_DIR}/internal/catch_matchers.cpp
${HEADER_DIR}/internal/catch_matchers_floating.cpp
${HEADER_DIR}/internal/catch_matchers_string.cpp ${HEADER_DIR}/internal/catch_matchers_string.cpp
${HEADER_DIR}/internal/catch_message.cpp ${HEADER_DIR}/internal/catch_message.cpp
${HEADER_DIR}/internal/catch_registry_hub.cpp ${HEADER_DIR}/internal/catch_registry_hub.cpp

View File

@ -36,10 +36,19 @@ REQUIRE_THAT( str,
``` ```
## Built in matchers ## Built in matchers
Currently Catch has some string matchers and some vector matchers. They are in the `Catch::Matchers` and `Catch` namespaces. Catch currently provides some matchers, they are in the `Catch::Matchers` and `Catch` namespaces.
### String matchers
The string matchers are `StartsWith`, `EndsWith`, `Contains` and `Equals`. Each of them also takes an optional second argument, that decides case sensitivity (by-default, they are case sensitive). The string matchers are `StartsWith`, `EndsWith`, `Contains` and `Equals`. Each of them also takes an optional second argument, that decides case sensitivity (by-default, they are case sensitive).
### Vector matchers
The vector matchers are `Contains`, `VectorContains` and `Equals`. `VectorContains` looks for a single element in the matched vector, `Contains` looks for a set (vector) of elements inside the matched vector. The vector matchers are `Contains`, `VectorContains` and `Equals`. `VectorContains` looks for a single element in the matched vector, `Contains` looks for a set (vector) of elements inside the matched vector.
### Floating point matchers
The floating point matchers are `WithinULP` and `WithinAbs`. `WithinAbs` accepts floating point numbers that are within a certain margin of target. `WithinULP` performs an [ULP](https://en.wikipedia.org/wiki/Unit_in_the_last_place)-based comparison of two floating point numbers and accepts them if they are less than certain number of ULPs apart.
Do note that ULP-based checks only make sense when both compared numbers are of the same type and `WithinULP` will use type of its argument as the target type. This means that `WithinULP(1.f, 1)` will expect to compare `float`s, but `WithinULP(1., 1)` will expect to compare `double`s.
## Custom matchers ## Custom matchers
It's easy to provide your own matchers to extend Catch or just to work with your own types. It's easy to provide your own matchers to extend Catch or just to work with your own types.

View File

@ -10,6 +10,7 @@
#include "catch_capture.hpp" #include "catch_capture.hpp"
#include "catch_matchers.h" #include "catch_matchers.h"
#include "catch_matchers_floating.h"
#include "catch_matchers_string.h" #include "catch_matchers_string.h"
#include "catch_matchers_vector.h" #include "catch_matchers_vector.h"

View File

@ -0,0 +1,144 @@
/*
* Created by Martin on 07/11/2017.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "catch_matchers_floating.h"
#include <cmath>
#include <cstdint>
#include <cstring>
#include <stdexcept>
namespace Catch {
namespace Matchers {
namespace Floating {
enum class FloatingPointKind : uint8_t {
Float,
Double
};
}
}
}
namespace {
union Float_t {
Float_t(float num = 0.0f) : f(num) {}
// Portable extraction of components.
bool Negative() const { return i < 0; }
int32_t RawMantissa() const { return i & ((1 << 23) - 1); }
int32_t RawExponent() const { return (i >> 23) & 0xFF; }
int32_t i;
float f;
};
template <typename T>
struct Converter;
template <>
struct Converter<float> {
static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
Converter(float f) {
std::memcpy(&i, &f, sizeof(f));
}
int32_t i;
};
template <>
struct Converter<double> {
static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
Converter(double d) {
std::memcpy(&i, &d, sizeof(d));
}
int64_t i;
};
template <typename T>
auto convert(T t) -> Converter<T> {
return Converter<T>(t);
}
template <typename FP>
bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
// Comparison with NaN should always be false.
// This way we can rule it out before getting into the ugly details
if (std::isnan(lhs) || std::isnan(rhs)) {
return false;
}
auto lc = convert(lhs);
auto rc = convert(rhs);
if ((lc.i < 0) != (rc.i < 0)) {
// Potentially we can have +0 and -0
return lhs == rhs;
}
auto ulpDiff = abs(lc.i - rc.i);
return ulpDiff <= maxUlpDiff;
}
}
namespace Catch {
namespace Matchers {
namespace Floating {
WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
:m_target{ target }, m_margin{ margin } {}
// Performs equivalent check of std::fabs(lhs - rhs) <= margin
// But without the subtraction to allow for INFINITY in comparison
bool WithinAbsMatcher::match(double const& matchee) const {
return (matchee + m_margin >= m_target) && (m_target + m_margin >= m_margin);
}
std::string WithinAbsMatcher::describe() const {
return "is within " + std::to_string(m_margin) + " of " + std::to_string(m_target);
}
WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType)
:m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
if (m_ulps < 0) {
throw std::domain_error("Expected ulp difference has to be >0");
}
}
bool WithinUlpsMatcher::match(double const& matchee) const {
switch (m_type) {
case FloatingPointKind::Float:
return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps);
case FloatingPointKind::Double:
return almostEqualUlps<double>(matchee, m_target, m_ulps);
default:
throw std::domain_error("Unknown FloatingPointKind value");
}
}
std::string WithinUlpsMatcher::describe() const {
return "is within " + std::to_string(m_ulps) + " ULPs of " + std::to_string(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
}
}// namespace Floating
Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) {
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
}
Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) {
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
}
Floating::WithinAbsMatcher WithinAbs(double target, double margin) {
return Floating::WithinAbsMatcher(target, margin);
}
} // namespace Matchers
} // namespace Catch

View File

@ -0,0 +1,59 @@
/*
* Created by Martin on 07/11/2017.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef TWOBLUECUBES_CATCH_MATCHERS_FLOATING_H_INCLUDED
#define TWOBLUECUBES_CATCH_MATCHERS_FLOATING_H_INCLUDED
#include "catch_matchers.h"
#include <type_traits>
#include <cmath>
namespace Catch {
namespace Matchers {
namespace Floating {
enum class FloatingPointKind : uint8_t;
struct WithinAbsMatcher : MatcherBase<double> {
WithinAbsMatcher(double target, double margin);
bool match(double const& matchee) const override;
std::string describe() const override;
private:
double m_target;
double m_margin;
};
struct WithinUlpsMatcher : MatcherBase<double> {
WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType);
bool match(double const& matchee) const override;
std::string describe() const override;
private:
double m_target;
int m_ulps;
FloatingPointKind m_type;
};
} // namespace Floating
Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff);
Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff);
Floating::WithinAbsMatcher WithinAbs(double target, double margin);
// The following functions create the actual matcher objects.
// This allows the types to be inferred
// StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
// StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
// StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
// StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
} // namespace Matchers
} // namespace Catch
#endif // TWOBLUECUBES_CATCH_MATCHERS_FLOATING_H_INCLUDED

View File

@ -1003,6 +1003,6 @@ with expansion:
"{?}" == "1" "{?}" == "1"
=============================================================================== ===============================================================================
test cases: 185 | 134 passed | 47 failed | 4 failed as expected test cases: 187 | 136 passed | 47 failed | 4 failed as expected
assertions: 904 | 787 passed | 96 failed | 21 failed as expected assertions: 935 | 818 passed | 96 failed | 21 failed as expected

View File

@ -1717,6 +1717,234 @@ PASSED:
with expansion: with expansion:
3628800 (0x<hex digits>) == 3628800 (0x<hex digits>) 3628800 (0x<hex digits>) == 3628800 (0x<hex digits>)
-------------------------------------------------------------------------------
Floating point matchers: double
Margin
-------------------------------------------------------------------------------
MatchersTests.cpp:<line number>
...............................................................................
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1., WithinAbs(1., 0) )
with expansion:
1.0 is within 0.000000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 0., WithinAbs(1., 1) )
with expansion:
0.0 is within 1.000000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 0., !WithinAbs(1., 0.99) )
with expansion:
0.0 not is within 0.990000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 0., !WithinAbs(1., 0.99) )
with expansion:
0.0 not is within 0.990000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( NAN, !WithinAbs(NAN, 0) )
with expansion:
nanf not is within 0.000000 of nan
-------------------------------------------------------------------------------
Floating point matchers: double
ULPs
-------------------------------------------------------------------------------
MatchersTests.cpp:<line number>
...............................................................................
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1., WithinULP(1., 0) )
with expansion:
1.0 is within 0 ULPs of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( std::nextafter(1., 2.), WithinULP(1., 1) )
with expansion:
1.0 is within 1 ULPs of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( std::nextafter(1., 0.), WithinULP(1., 1) )
with expansion:
1.0 is within 1 ULPs of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( std::nextafter(1., 2.), !WithinULP(1., 0) )
with expansion:
1.0 not is within 0 ULPs of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1., WithinULP(1., 0) )
with expansion:
1.0 is within 0 ULPs of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( -0., WithinULP(0., 0) )
with expansion:
-0.0 is within 0 ULPs of 0.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( NAN, !WithinULP(NAN, 123) )
with expansion:
nanf not is within 123 ULPs of nanf
-------------------------------------------------------------------------------
Floating point matchers: double
Composed
-------------------------------------------------------------------------------
MatchersTests.cpp:<line number>
...............................................................................
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1., WithinAbs(1., 0.5) || WithinULP(2., 1) )
with expansion:
1.0 ( is within 0.500000 of 1.000000 or is within 1 ULPs of 2.000000 )
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1., WithinAbs(2., 0.5) || WithinULP(1., 0) )
with expansion:
1.0 ( is within 0.500000 of 2.000000 or is within 0 ULPs of 1.000000 )
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)) )
with expansion:
nanf not ( is within 100.000000 of nan or is within 123 ULPs of nanf )
-------------------------------------------------------------------------------
Floating point matchers: float
Margin
-------------------------------------------------------------------------------
MatchersTests.cpp:<line number>
...............................................................................
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1.f, WithinAbs(1.f, 0) )
with expansion:
1.0f is within 0.000000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 0.f, WithinAbs(1.f, 1) )
with expansion:
0.0f is within 1.000000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 0.f, !WithinAbs(1.f, 0.99f) )
with expansion:
0.0f not is within 0.990000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 0.f, !WithinAbs(1.f, 0.99f) )
with expansion:
0.0f not is within 0.990000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 0.f, WithinAbs(-0.f, 0) )
with expansion:
0.0f is within 0.000000 of -0.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( NAN, !WithinAbs(NAN, 0) )
with expansion:
nanf not is within 0.000000 of nan
-------------------------------------------------------------------------------
Floating point matchers: float
ULPs
-------------------------------------------------------------------------------
MatchersTests.cpp:<line number>
...............................................................................
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1.f, WithinULP(1.f, 0) )
with expansion:
1.0f is within 0 ULPs of 1.000000f
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( std::nextafter(1.f, 2.f), WithinULP(1.f, 1) )
with expansion:
1.0f is within 1 ULPs of 1.000000f
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( std::nextafter(1.f, 0.f), WithinULP(1.f, 1) )
with expansion:
1.0f is within 1 ULPs of 1.000000f
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( std::nextafter(1.f, 2.f), !WithinULP(1.f, 0) )
with expansion:
1.0f not is within 0 ULPs of 1.000000f
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1.f, WithinULP(1.f, 0) )
with expansion:
1.0f is within 0 ULPs of 1.000000f
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( -0.f, WithinULP(0.f, 0) )
with expansion:
-0.0f is within 0 ULPs of 0.000000f
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( NAN, !WithinULP(NAN, 123) )
with expansion:
nanf not is within 123 ULPs of nanf
-------------------------------------------------------------------------------
Floating point matchers: float
Composed
-------------------------------------------------------------------------------
MatchersTests.cpp:<line number>
...............................................................................
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1) )
with expansion:
1.0f ( is within 0.500000 of 1.000000 or is within 1 ULPs of 1.000000f )
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0) )
with expansion:
1.0f ( is within 0.500000 of 2.000000 or is within 0 ULPs of 1.000000f )
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)) )
with expansion:
nanf not ( is within 100.000000 of nan or is within 123 ULPs of nanf )
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Greater-than inequalities with different epsilons Greater-than inequalities with different epsilons
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -7632,6 +7860,6 @@ MiscTests.cpp:<line number>:
PASSED: PASSED:
=============================================================================== ===============================================================================
test cases: 185 | 132 passed | 49 failed | 4 failed as expected test cases: 187 | 134 passed | 49 failed | 4 failed as expected
assertions: 903 | 783 passed | 99 failed | 21 failed as expected assertions: 934 | 814 passed | 99 failed | 21 failed as expected

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<testsuitesloose text artifact <testsuitesloose text artifact
> >
<testsuite name="<exe-name>" errors="15" failures="85" tests="904" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}"> <testsuite name="<exe-name>" errors="15" failures="85" tests="935" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testcase classname="<exe-name>.global" name="# A test name that starts with a #" time="{duration}"/> <testcase classname="<exe-name>.global" name="# A test name that starts with a #" time="{duration}"/>
<testcase classname="<exe-name>.global" name="#1005: Comparing pointer to int and long (NULL can be either on various systems)" time="{duration}"/> <testcase classname="<exe-name>.global" name="#1005: Comparing pointer to int and long (NULL can be either on various systems)" time="{duration}"/>
<testcase classname="<exe-name>.global" name="#748 - captures with unexpected exceptions/outside assertions" time="{duration}"> <testcase classname="<exe-name>.global" name="#748 - captures with unexpected exceptions/outside assertions" time="{duration}">
@ -263,6 +263,12 @@ MessageTests.cpp:<line number>
</failure> </failure>
</testcase> </testcase>
<testcase classname="<exe-name>.global" name="Factorials are computed" time="{duration}"/> <testcase classname="<exe-name>.global" name="Factorials are computed" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: double/Margin" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: double/ULPs" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: double/Composed" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: float/Margin" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: float/ULPs" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: float/Composed" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Greater-than inequalities with different epsilons" time="{duration}"/> <testcase classname="<exe-name>.global" name="Greater-than inequalities with different epsilons" time="{duration}"/>
<testcase classname="<exe-name>.global" name="INFO and WARN do not abort tests" time="{duration}"/> <testcase classname="<exe-name>.global" name="INFO and WARN do not abort tests" time="{duration}"/>
<testcase classname="<exe-name>.global" name="INFO gets logged on failure" time="{duration}"> <testcase classname="<exe-name>.global" name="INFO gets logged on failure" time="{duration}">

View File

@ -1945,6 +1945,278 @@
</Expression> </Expression>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<TestCase name="Floating point matchers: double" tags="[floating-point][matchers]" filename="projects/<exe-name>/MatchersTests.cpp" >
<Section name="Margin" filename="projects/<exe-name>/MatchersTests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1., WithinAbs(1., 0)
</Original>
<Expanded>
1.0 is within 0.000000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
0., WithinAbs(1., 1)
</Original>
<Expanded>
0.0 is within 1.000000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
0., !WithinAbs(1., 0.99)
</Original>
<Expanded>
0.0 not is within 0.990000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
0., !WithinAbs(1., 0.99)
</Original>
<Expanded>
0.0 not is within 0.990000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
NAN, !WithinAbs(NAN, 0)
</Original>
<Expanded>
nanf not is within 0.000000 of nan
</Expanded>
</Expression>
<OverallResults successes="5" failures="0" expectedFailures="0"/>
</Section>
<Section name="ULPs" filename="projects/<exe-name>/MatchersTests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1., WithinULP(1., 0)
</Original>
<Expanded>
1.0 is within 0 ULPs of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
std::nextafter(1., 2.), WithinULP(1., 1)
</Original>
<Expanded>
1.0 is within 1 ULPs of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
std::nextafter(1., 0.), WithinULP(1., 1)
</Original>
<Expanded>
1.0 is within 1 ULPs of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
std::nextafter(1., 2.), !WithinULP(1., 0)
</Original>
<Expanded>
1.0 not is within 0 ULPs of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1., WithinULP(1., 0)
</Original>
<Expanded>
1.0 is within 0 ULPs of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
-0., WithinULP(0., 0)
</Original>
<Expanded>
-0.0 is within 0 ULPs of 0.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
NAN, !WithinULP(NAN, 123)
</Original>
<Expanded>
nanf not is within 123 ULPs of nanf
</Expanded>
</Expression>
<OverallResults successes="7" failures="0" expectedFailures="0"/>
</Section>
<Section name="Composed" filename="projects/<exe-name>/MatchersTests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1., WithinAbs(1., 0.5) || WithinULP(2., 1)
</Original>
<Expanded>
1.0 ( is within 0.500000 of 1.000000 or is within 1 ULPs of 2.000000 )
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1., WithinAbs(2., 0.5) || WithinULP(1., 0)
</Original>
<Expanded>
1.0 ( is within 0.500000 of 2.000000 or is within 0 ULPs of 1.000000 )
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123))
</Original>
<Expanded>
nanf not ( is within 100.000000 of nan or is within 123 ULPs of nanf )
</Expanded>
</Expression>
<OverallResults successes="3" failures="0" expectedFailures="0"/>
</Section>
<OverallResult success="true"/>
</TestCase>
<TestCase name="Floating point matchers: float" tags="[floating-point][matchers]" filename="projects/<exe-name>/MatchersTests.cpp" >
<Section name="Margin" filename="projects/<exe-name>/MatchersTests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1.f, WithinAbs(1.f, 0)
</Original>
<Expanded>
1.0f is within 0.000000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
0.f, WithinAbs(1.f, 1)
</Original>
<Expanded>
0.0f is within 1.000000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
0.f, !WithinAbs(1.f, 0.99f)
</Original>
<Expanded>
0.0f not is within 0.990000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
0.f, !WithinAbs(1.f, 0.99f)
</Original>
<Expanded>
0.0f not is within 0.990000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
0.f, WithinAbs(-0.f, 0)
</Original>
<Expanded>
0.0f is within 0.000000 of -0.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
NAN, !WithinAbs(NAN, 0)
</Original>
<Expanded>
nanf not is within 0.000000 of nan
</Expanded>
</Expression>
<OverallResults successes="6" failures="0" expectedFailures="0"/>
</Section>
<Section name="ULPs" filename="projects/<exe-name>/MatchersTests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1.f, WithinULP(1.f, 0)
</Original>
<Expanded>
1.0f is within 0 ULPs of 1.000000f
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
std::nextafter(1.f, 2.f), WithinULP(1.f, 1)
</Original>
<Expanded>
1.0f is within 1 ULPs of 1.000000f
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
std::nextafter(1.f, 0.f), WithinULP(1.f, 1)
</Original>
<Expanded>
1.0f is within 1 ULPs of 1.000000f
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
std::nextafter(1.f, 2.f), !WithinULP(1.f, 0)
</Original>
<Expanded>
1.0f not is within 0 ULPs of 1.000000f
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1.f, WithinULP(1.f, 0)
</Original>
<Expanded>
1.0f is within 0 ULPs of 1.000000f
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
-0.f, WithinULP(0.f, 0)
</Original>
<Expanded>
-0.0f is within 0 ULPs of 0.000000f
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
NAN, !WithinULP(NAN, 123)
</Original>
<Expanded>
nanf not is within 123 ULPs of nanf
</Expanded>
</Expression>
<OverallResults successes="7" failures="0" expectedFailures="0"/>
</Section>
<Section name="Composed" filename="projects/<exe-name>/MatchersTests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1)
</Original>
<Expanded>
1.0f ( is within 0.500000 of 1.000000 or is within 1 ULPs of 1.000000f )
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0)
</Original>
<Expanded>
1.0f ( is within 0.500000 of 2.000000 or is within 0 ULPs of 1.000000f )
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123))
</Original>
<Expanded>
nanf not ( is within 100.000000 of nan or is within 123 ULPs of nanf )
</Expanded>
</Expression>
<OverallResults successes="3" failures="0" expectedFailures="0"/>
</Section>
<OverallResult success="true"/>
</TestCase>
<TestCase name="Greater-than inequalities with different epsilons" tags="[Approx]" filename="projects/<exe-name>/ApproxTests.cpp" > <TestCase name="Greater-than inequalities with different epsilons" tags="[Approx]" filename="projects/<exe-name>/ApproxTests.cpp" >
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/ApproxTests.cpp" > <Expression success="true" type="REQUIRE" filename="projects/<exe-name>/ApproxTests.cpp" >
<Original> <Original>
@ -8446,7 +8718,7 @@ loose text artifact
</Section> </Section>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<OverallResults successes="783" failures="100" expectedFailures="21"/> <OverallResults successes="814" failures="100" expectedFailures="21"/>
</Group> </Group>
<OverallResults successes="783" failures="99" expectedFailures="21"/> <OverallResults successes="814" failures="99" expectedFailures="21"/>
</Catch> </Catch>

View File

@ -226,6 +226,67 @@ TEST_CASE("Exception matchers that fail", "[matchers][exceptions][!throws][.fail
} }
} }
TEST_CASE("Floating point matchers: float", "[matchers][floating-point]") {
SECTION("Margin") {
REQUIRE_THAT(1.f, WithinAbs(1.f, 0));
REQUIRE_THAT(0.f, WithinAbs(1.f, 1));
REQUIRE_THAT(0.f, !WithinAbs(1.f, 0.99f));
REQUIRE_THAT(0.f, !WithinAbs(1.f, 0.99f));
REQUIRE_THAT(0.f, WithinAbs(-0.f, 0));
REQUIRE_THAT(NAN, !WithinAbs(NAN, 0));
}
SECTION("ULPs") {
REQUIRE_THAT(1.f, WithinULP(1.f, 0));
REQUIRE_THAT(std::nextafter(1.f, 2.f), WithinULP(1.f, 1));
REQUIRE_THAT(std::nextafter(1.f, 0.f), WithinULP(1.f, 1));
REQUIRE_THAT(std::nextafter(1.f, 2.f), !WithinULP(1.f, 0));
REQUIRE_THAT(1.f, WithinULP(1.f, 0));
REQUIRE_THAT(-0.f, WithinULP(0.f, 0));
REQUIRE_THAT(NAN, !WithinULP(NAN, 123));
}
SECTION("Composed") {
REQUIRE_THAT(1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1));
REQUIRE_THAT(1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0));
REQUIRE_THAT(NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)));
}
}
TEST_CASE("Floating point matchers: double", "[matchers][floating-point]") {
SECTION("Margin") {
REQUIRE_THAT(1., WithinAbs(1., 0));
REQUIRE_THAT(0., WithinAbs(1., 1));
REQUIRE_THAT(0., !WithinAbs(1., 0.99));
REQUIRE_THAT(0., !WithinAbs(1., 0.99));
REQUIRE_THAT(NAN, !WithinAbs(NAN, 0));
}
SECTION("ULPs") {
REQUIRE_THAT(1., WithinULP(1., 0));
REQUIRE_THAT(std::nextafter(1., 2.), WithinULP(1., 1));
REQUIRE_THAT(std::nextafter(1., 0.), WithinULP(1., 1));
REQUIRE_THAT(std::nextafter(1., 2.), !WithinULP(1., 0));
REQUIRE_THAT(1., WithinULP(1., 0));
REQUIRE_THAT(-0., WithinULP(0., 0));
REQUIRE_THAT(NAN, !WithinULP(NAN, 123));
}
SECTION("Composed") {
REQUIRE_THAT(1., WithinAbs(1., 0.5) || WithinULP(2., 1));
REQUIRE_THAT(1., WithinAbs(2., 0.5) || WithinULP(1., 0));
REQUIRE_THAT(NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)));
}
}
#endif // CATCH_CONFIG_DISABLE_MATCHERS #endif // CATCH_CONFIG_DISABLE_MATCHERS
#ifdef __clang__ #ifdef __clang__

View File

@ -52,6 +52,12 @@ infParser = re.compile(r'''
| |
__builtin_huge_valf\(\) # OSX macro __builtin_huge_valf\(\) # OSX macro
''', re.VERBOSE) ''', re.VERBOSE)
nanParser = re.compile(r'''
\(\(float\)\(\(\(float\)\(1e\+300\ \*\ 1e\+300\)\)\ \*\ 0\.0F\)\) # MSVC NAN macro
|
\(__builtin_nanf\ \(""\)\) # Linux (ubuntu) NAN macro
''', re.VERBOSE)
if len(sys.argv) == 2: if len(sys.argv) == 2:
cmdPath = sys.argv[1] cmdPath = sys.argv[1]
@ -110,6 +116,7 @@ def filterLine(line):
line = errnoParser.sub('errno', line) line = errnoParser.sub('errno', line)
line = sinceEpochParser.sub('{since-epoch-report}', line) line = sinceEpochParser.sub('{since-epoch-report}', line)
line = infParser.sub('INFINITY', line) line = infParser.sub('INFINITY', line)
line = nanParser.sub('NAN', line)
return line return line