Rewrite documentation for floating point matchers

This commit is contained in:
Martin Hořeňovský 2019-10-13 21:31:48 +02:00
parent e4d61e4cd8
commit a92a7d0229
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A
1 changed files with 42 additions and 12 deletions

View File

@ -12,11 +12,11 @@ The first argument is the thing (object or value) under test. The second part is
which consists of either a single matcher or one or more matchers combined using `&&`, `||` or `!` operators. which consists of either a single matcher or one or more matchers combined using `&&`, `||` or `!` operators.
For example, to assert that a string ends with a certain substring: For example, to assert that a string ends with a certain substring:
```c++ ```c++
using Catch::Matchers::EndsWith; // or Catch::EndsWith using Catch::Matchers::EndsWith; // or Catch::EndsWith
std::string str = getStringFromSomewhere(); std::string str = getStringFromSomewhere();
REQUIRE_THAT( str, EndsWith( "as a service" ) ); REQUIRE_THAT( str, EndsWith( "as a service" ) );
``` ```
The matcher objects can take multiple arguments, allowing more fine tuning. The matcher objects can take multiple arguments, allowing more fine tuning.
@ -24,19 +24,29 @@ The built-in string matchers, for example, take a second argument specifying whe
case sensitive or not: case sensitive or not:
```c++ ```c++
REQUIRE_THAT( str, EndsWith( "as a service", Catch::CaseSensitive::No ) ); REQUIRE_THAT( str, EndsWith( "as a service", Catch::CaseSensitive::No ) );
``` ```
And matchers can be combined: And matchers can be combined:
```c++ ```c++
REQUIRE_THAT( str, REQUIRE_THAT( str,
EndsWith( "as a service" ) || EndsWith( "as a service" ) ||
(StartsWith( "Big data" ) && !Contains( "web scale" ) ) ); (StartsWith( "Big data" ) && !Contains( "web scale" ) ) );
``` ```
## Built in matchers ## Built in matchers
Catch currently provides some matchers, they are in the `Catch::Matchers` and `Catch` namespaces. Catch2 provides some matchers by default. They can be found in the
`Catch::Matchers::foo` namespace and are imported into the `Catch`
namespace as well.
There are two parts to each of the built-in matchers, the matcher
type itself and a helper function that provides template argument
deduction when creating templated matchers. As an example, the matcher
for checking that two instances of `std::vector` are identical is
`EqualsMatcher<T>`, but the user is expected to use the `Equals`
helper function instead.
### String matchers ### String matchers
The string matchers are `StartsWith`, `EndsWith`, `Contains`, `Equals` and `Matches`. The first four match a literal (sub)string against a result, while `Matches` takes and matches an ECMAScript regex. Do note that `Matches` matches the string as a whole, meaning that "abc" will not match against "abcd", but "abc.*" will. The string matchers are `StartsWith`, `EndsWith`, `Contains`, `Equals` and `Matches`. The first four match a literal (sub)string against a result, while `Matches` takes and matches an ECMAScript regex. Do note that `Matches` matches the string as a whole, meaning that "abc" will not match against "abcd", but "abc.*" will.
@ -57,10 +67,30 @@ These are
### Floating point matchers ### 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. Catch2 provides 3 matchers for working with floating point numbers. These
are `WithinAbsMatcher`, `WithinUlpsMatcher` and `WithinRelMatcher`.
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. The `WithinAbsMatcher` matcher accepts floating point numbers that are
within a certain distance of target. It should be constructed with the
`WithinAbs(double target, double margin)` helper.
The `WithinUlpsMatcher` matcher accepts floating point numbers that are
within a certain number of [ULPs](https://en.wikipedia.org/wiki/Unit_in_the_last_place)
of the target. Because ULP comparisons need to be done differently for
`float`s and for `double`s, there are two overloads of the helpers for
this matcher, `WithinULP(float target, int64_t ULPs)`, and
`WithinULP(double target, int64_t ULPs)`.
The `WithinRelMatcher` matcher accepts floating point numbers that are
_approximately equal_ with the target number with some specific tolerance.
In other words, it checks that `|lhs - rhs| <= epsilon * max(|lhs|, |rhs|)`,
with special casing for `INFINITY` and `NaN`. There are _4_ overloads of
the helpers for this matcher, `WithinRel(double target, double margin)`,
`WithinRel(float target, float margin)`, `WithinRel(double target)`, and
`WithinRel(float target)`. The latter two provide a default epsilon of
machine epsilon * 100.
> `WithinRel` matcher was introduced in Catch X.Y.Z
### Generic matchers ### Generic matchers
Catch also aims to provide a set of generic matchers. Currently this set Catch also aims to provide a set of generic matchers. Currently this set
@ -100,10 +130,10 @@ REQUIRE_THROWS_MATCHES(throwsDerivedException(), DerivedException, Message("De
## 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.
You need to provide two things: You need to provide two things:
1. A matcher class, derived from `Catch::MatcherBase<T>` - where `T` is the type being tested. 1. A matcher class, derived from `Catch::MatcherBase<T>` - where `T` is the type being tested.
The constructor takes and stores any arguments needed (e.g. something to compare against) and you must The constructor takes and stores any arguments needed (e.g. something to compare against) and you must
override two methods: `match()` and `describe()`. override two methods: `match()` and `describe()`.
2. A simple builder function. This is what is actually called from the test code and allows overloading. 2. A simple builder function. This is what is actually called from the test code and allows overloading.
Here's an example for asserting that an integer falls within a given range Here's an example for asserting that an integer falls within a given range
@ -148,7 +178,7 @@ TEST_CASE("Integers are within a range")
``` ```
Running this test gives the following in the console: Running this test gives the following in the console:
``` ```
/**/TestFile.cpp:123: FAILED: /**/TestFile.cpp:123: FAILED:
CHECK_THAT( 100, IsBetween( 1, 10 ) ) CHECK_THAT( 100, IsBetween( 1, 10 ) )