Skip to main content

googletest/matchers/
near_matcher.rs

1// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::{
16    description::Description,
17    matcher::{Matcher, MatcherBase, MatcherResult},
18};
19use num_traits::{Float, FloatConst};
20use std::{borrow::Borrow, fmt::Debug};
21
22/// Matches a value equal within `max_abs_error` of `expected`.
23///
24/// The type `T` of the actual, `expected`, and `max_abs_error` values must
25/// implement [`Float`].
26///
27/// The values `expected` and `max_abs_error` may not be NaN. The value
28/// `max_abs_error` must be non-negative. The matcher panics on construction
29/// otherwise.
30///
31/// ```
32/// # use googletest::prelude::*;
33/// # fn should_pass_1() -> Result<()> {
34/// verify_that!(1.0, near(1.0, 0.1))?; // Passes
35/// verify_that!(1.01, near(1.0, 0.1))?; // Passes
36/// verify_that!(1.25, near(1.0, 0.25))?; // Passes
37/// verify_that!(0.75, near(1.0, 0.25))?; // Passes
38/// #     Ok(())
39/// # }
40/// # fn should_fail_1() -> Result<()> {
41/// verify_that!(1.101, near(1.0, 0.1))?; // Fails
42/// #     Ok(())
43/// # }
44/// # fn should_fail_2() -> Result<()> {
45/// verify_that!(0.899, near(1.0, 0.1))?; // Fails
46/// #     Ok(())
47/// # }
48/// # fn should_pass_2() -> Result<()> {
49/// verify_that!(100.25, near(100.0, 0.25))?; // Passes
50/// #     Ok(())
51/// # }
52/// # should_pass_1().unwrap();
53/// # should_fail_1().unwrap_err();
54/// # should_fail_2().unwrap_err();
55/// # should_pass_2().unwrap();
56/// ```
57///
58/// The default behaviour for special values is consistent with the IEEE
59/// floating point standard. Thus infinity is infinitely far away from any
60/// floating point value:
61///
62/// ```
63/// # use googletest::prelude::*;
64/// # fn should_fail_1() -> Result<()> {
65/// verify_that!(f64::INFINITY, near(0.0, f64::MAX))?; // Fails
66/// #     Ok(())
67/// # }
68/// # fn should_fail_2() -> Result<()> {
69/// verify_that!(0.0, near(f64::INFINITY, f64::MAX))?; // Fails
70/// #     Ok(())
71/// # }
72/// # fn should_fail_3() -> Result<()> {
73/// verify_that!(f64::INFINITY, near(f64::INFINITY, f64::MAX))?; // Fails
74/// #     Ok(())
75/// # }
76/// # should_fail_1().unwrap_err();
77/// # should_fail_2().unwrap_err();
78/// # should_fail_3().unwrap_err();
79/// ```
80///
81/// Similarly, by default, `NaN` is infinitely far away from any value:
82///
83/// ```
84/// # use googletest::prelude::*;
85/// # fn should_fail_1() -> Result<()> {
86/// verify_that!(f64::NAN, near(0.0, f64::MAX))?; // Fails
87/// #     Ok(())
88/// # }
89/// # fn should_fail_2() -> Result<()> {
90/// verify_that!(0.0, near(f64::NAN, f64::MAX))?; // Fails
91/// #     Ok(())
92/// # }
93/// # fn should_fail_3() -> Result<()> {
94/// verify_that!(f64::NAN, near(f64::NAN, f64::MAX))?; // Fails
95/// #     Ok(())
96/// # }
97/// # should_fail_1().unwrap_err();
98/// # should_fail_2().unwrap_err();
99/// # should_fail_3().unwrap_err();
100/// ```
101///
102/// To treat two `NaN` values as equal, use the method
103/// [`NearMatcher::nans_are_equal`].
104///
105/// ```
106/// # use googletest::prelude::*;
107/// # fn should_pass() -> Result<()> {
108/// verify_that!(f64::NAN, near(f64::NAN, f64::MAX).nans_are_equal())?; // Passes
109/// #     Ok(())
110/// # }
111/// # should_pass().unwrap();
112/// ```
113#[track_caller]
114pub fn near<T: Debug + Float + Copy>(expected: T, max_abs_error: T) -> NearMatcher<T> {
115    if max_abs_error.is_nan() {
116        panic!("max_abs_error must not be NaN");
117    }
118    if max_abs_error < T::zero() {
119        panic!("max_abs_error must be non-negative");
120    }
121    NearMatcher { expected, max_abs_error, nans_are_equal: false }
122}
123
124/// Matches a value approximately equal to `expected`.
125///
126/// This automatically computes a tolerance from the magnitude of `expected` and
127/// matches any actual value within this tolerance of the expected value. The
128/// tolerance is chosen to account for the inaccuracies in most ordinary
129/// floating point calculations.
130///
131/// Otherwise this works analogously to [`near`]; see its documentation for
132/// further notes.
133pub fn approx_eq<T: Debug + Float + FloatConst + Copy>(expected: T) -> NearMatcher<T> {
134    // The FloatConst trait doesn't offer 2 as a constant but does offer 1.
135    let five_bits_of_mantissa = (T::one() + T::one()).powi(5);
136    let abs_tolerance = five_bits_of_mantissa * T::epsilon();
137    let max_abs_error = T::max(expected.abs() * abs_tolerance, abs_tolerance);
138    NearMatcher { expected, max_abs_error, nans_are_equal: false }
139}
140
141/// A matcher which matches floating-point numbers approximately equal to its
142/// expected value.
143#[derive(MatcherBase)]
144pub struct NearMatcher<T: Debug> {
145    expected: T,
146    max_abs_error: T,
147    nans_are_equal: bool,
148}
149
150impl<T: Debug> NearMatcher<T> {
151    /// Configures this instance to treat two NaNs as equal.
152    ///
153    /// This behaviour differs from the IEEE standad for floating point which
154    /// treats two NaNs as infinitely far apart.
155    pub fn nans_are_equal(mut self) -> Self {
156        self.nans_are_equal = true;
157        self
158    }
159
160    /// Configures this instance to treat two NaNs as not equal.
161    ///
162    /// This behaviour complies with the IEEE standad for floating point. It is
163    /// the default behaviour for this matcher, so invoking this method is
164    /// usually redunant.
165    pub fn nans_are_not_equal(mut self) -> Self {
166        self.nans_are_equal = false;
167        self
168    }
169}
170
171impl<T: Borrow<F> + Debug + Copy, F: Debug + Float> Matcher<T> for NearMatcher<F> {
172    fn matches(&self, actual: T) -> MatcherResult {
173        if self.nans_are_equal && self.expected.is_nan() && actual.borrow().is_nan() {
174            return MatcherResult::Match;
175        }
176
177        let delta = *actual.borrow() - self.expected;
178        if delta >= -self.max_abs_error && delta <= self.max_abs_error {
179            MatcherResult::Match
180        } else {
181            MatcherResult::NoMatch
182        }
183    }
184
185    fn describe(&self, matcher_result: MatcherResult) -> Description {
186        match matcher_result {
187            MatcherResult::Match => {
188                format!("is within {:?} of {:?}", self.max_abs_error, self.expected).into()
189            }
190            MatcherResult::NoMatch => {
191                format!("isn't within {:?} of {:?}", self.max_abs_error, self.expected).into()
192            }
193        }
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use crate::matcher::MatcherResult;
200    use crate::prelude::*;
201    use crate::Result;
202
203    #[test]
204    fn matches_value_inside_range() -> Result<()> {
205        let matcher = near(1.0f64, 0.1f64);
206
207        let result = matcher.matches(1.0f64);
208
209        verify_that!(result, eq(MatcherResult::Match))
210    }
211
212    #[test]
213    fn matches_value_at_low_end_of_range() -> Result<()> {
214        let matcher = near(1.0f64, 0.1f64);
215
216        let result = matcher.matches(0.9f64);
217
218        verify_that!(result, eq(MatcherResult::Match))
219    }
220
221    #[test]
222    fn matches_value_at_high_end_of_range() -> Result<()> {
223        let matcher = near(1.0f64, 0.25f64);
224
225        let result = matcher.matches(1.25f64);
226
227        verify_that!(result, eq(MatcherResult::Match))
228    }
229
230    #[test]
231    fn does_not_match_value_below_low_end_of_range() -> Result<()> {
232        let matcher = near(1.0f64, 0.1f64);
233
234        let result = matcher.matches(0.899999f64);
235
236        verify_that!(result, eq(MatcherResult::NoMatch))
237    }
238
239    #[test]
240    fn does_not_match_value_above_high_end_of_range() -> Result<()> {
241        let matcher = near(1.0f64, 0.1f64);
242
243        let result = matcher.matches(1.100001f64);
244
245        verify_that!(result, eq(MatcherResult::NoMatch))
246    }
247
248    #[test]
249    fn nan_is_not_near_a_number() -> Result<()> {
250        let matcher = near(0.0f64, f64::MAX);
251
252        let result = matcher.matches(f64::NAN);
253
254        verify_that!(result, eq(MatcherResult::NoMatch))
255    }
256
257    #[test]
258    fn nan_is_not_near_nan_by_default() -> Result<()> {
259        verify_that!(f64::NAN, not(near(f64::NAN, f64::MAX)))
260    }
261
262    #[test]
263    fn nan_is_not_near_nan_when_explicitly_configured() -> Result<()> {
264        verify_that!(f64::NAN, not(near(f64::NAN, f64::MAX).nans_are_not_equal()))
265    }
266
267    #[test]
268    fn nan_is_near_nan_if_nans_are_equal() -> Result<()> {
269        verify_that!(f64::NAN, near(f64::NAN, f64::MAX).nans_are_equal())
270    }
271
272    #[test]
273    fn nan_is_not_near_number_when_nans_are_equal() -> Result<()> {
274        verify_that!(f64::NAN, not(near(0.0, f64::MAX).nans_are_equal()))
275    }
276
277    #[test]
278    fn number_is_not_near_nan_when_nans_are_equal() -> Result<()> {
279        verify_that!(0.0, not(near(f64::NAN, f64::MAX).nans_are_equal()))
280    }
281
282    #[test]
283    fn inf_is_not_near_inf() -> Result<()> {
284        let matcher = near(f64::INFINITY, f64::MAX);
285
286        let result = matcher.matches(f64::INFINITY);
287
288        verify_that!(result, eq(MatcherResult::NoMatch))
289    }
290
291    #[test]
292    fn inf_is_not_near_a_number() -> Result<()> {
293        let matcher = near(f64::INFINITY, f64::MAX);
294
295        let result = matcher.matches(f64::MIN);
296
297        verify_that!(result, eq(MatcherResult::NoMatch))
298    }
299
300    #[test]
301    fn any_two_numbers_are_within_inf_of_each_other() -> Result<()> {
302        let matcher = near(f64::MIN, f64::INFINITY);
303
304        let result = matcher.matches(f64::MAX);
305
306        verify_that!(result, eq(MatcherResult::Match))
307    }
308
309    #[::core::prelude::v1::test]
310    #[should_panic]
311    fn panics_if_max_abs_error_is_nan() {
312        near(0.0, f64::NAN);
313    }
314
315    #[::core::prelude::v1::test]
316    #[should_panic]
317    fn panics_if_tolerance_is_negative() {
318        near(0.0, -1.0);
319    }
320
321    #[test]
322    fn approx_eq_matches_equal_number() -> Result<()> {
323        verify_that!(1.0f64, approx_eq(1.0f64))
324    }
325
326    #[test]
327    fn approx_eq_matches_really_close_f64_number() -> Result<()> {
328        verify_that!(1.0f64, approx_eq(1.0 + 16.0 * f64::EPSILON))
329    }
330
331    #[test]
332    fn approx_eq_matches_really_close_f64_number_to_large_number() -> Result<()> {
333        verify_that!(1000f64, approx_eq(1000.0 + 16000.0 * f64::EPSILON))
334    }
335
336    #[test]
337    fn approx_eq_matches_really_close_f64_number_to_zero() -> Result<()> {
338        verify_that!(16.0 * f64::EPSILON, approx_eq(0.0))
339    }
340
341    #[test]
342    fn approx_eq_matches_really_close_f32_number() -> Result<()> {
343        verify_that!(1.0f32, approx_eq(1.0 + 16.0 * f32::EPSILON))
344    }
345
346    #[test]
347    fn approx_eq_does_not_match_distant_number() -> Result<()> {
348        verify_that!(0.0f64, not(approx_eq(1.0f64)))
349    }
350
351    #[test]
352    fn approx_eq_supports_ref() -> Result<()> {
353        verify_that!(&0.0f64, approx_eq(0.0f64))
354    }
355
356    #[test]
357    fn approx_eq_supports_container_matchers() -> Result<()> {
358        verify_that!(
359            vec![1., 2., 3.],
360            unordered_elements_are![approx_eq(2.), approx_eq(3.), approx_eq(1.)]
361        )
362    }
363}