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
15 use crate::{
16 description::Description,
17 matcher::{Matcher, MatcherResult},
18 };
19 use num_traits::{Float, FloatConst};
20 use std::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 /// ```
near<T: Debug + Float + Copy>(expected: T, max_abs_error: T) -> NearMatcher<T>113 pub fn near<T: Debug + Float + Copy>(expected: T, max_abs_error: T) -> NearMatcher<T> {
114 if max_abs_error.is_nan() {
115 panic!("max_abs_error must not be NaN");
116 }
117 if max_abs_error < T::zero() {
118 panic!("max_abs_error must be non-negative");
119 }
120 NearMatcher { expected, max_abs_error, nans_are_equal: false }
121 }
122
123 /// Matches a value approximately equal to `expected`.
124 ///
125 /// This automatically computes a tolerance from the magnitude of `expected` and
126 /// matches any actual value within this tolerance of the expected value. The
127 /// tolerance is chosen to account for the inaccuracies in most ordinary
128 /// floating point calculations.
129 ///
130 /// Otherwise this works analogously to [`near`]; see its documentation for
131 /// further notes.
approx_eq<T: Debug + Float + FloatConst + Copy>(expected: T) -> NearMatcher<T>132 pub fn approx_eq<T: Debug + Float + FloatConst + Copy>(expected: T) -> NearMatcher<T> {
133 // The FloatConst trait doesn't offer 2 as a constant but does offer 1.
134 let five_bits_of_mantissa = (T::one() + T::one()).powi(5);
135 let abs_tolerance = five_bits_of_mantissa * T::epsilon();
136 let max_abs_error = T::max(expected.abs() * abs_tolerance, abs_tolerance);
137 NearMatcher { expected, max_abs_error, nans_are_equal: false }
138 }
139
140 /// A matcher which matches floating-point numbers approximately equal to its
141 /// expected value.
142 pub struct NearMatcher<T: Debug> {
143 expected: T,
144 max_abs_error: T,
145 nans_are_equal: bool,
146 }
147
148 impl<T: Debug> NearMatcher<T> {
149 /// Configures this instance to treat two NaNs as equal.
150 ///
151 /// This behaviour differs from the IEEE standad for floating point which
152 /// treats two NaNs as infinitely far apart.
nans_are_equal(mut self) -> Self153 pub fn nans_are_equal(mut self) -> Self {
154 self.nans_are_equal = true;
155 self
156 }
157
158 /// Configures this instance to treat two NaNs as not equal.
159 ///
160 /// This behaviour complies with the IEEE standad for floating point. It is
161 /// the default behaviour for this matcher, so invoking this method is
162 /// usually redunant.
nans_are_not_equal(mut self) -> Self163 pub fn nans_are_not_equal(mut self) -> Self {
164 self.nans_are_equal = false;
165 self
166 }
167 }
168
169 impl<T: Debug + Float> Matcher for NearMatcher<T> {
170 type ActualT = T;
171
matches(&self, actual: &T) -> MatcherResult172 fn matches(&self, actual: &T) -> MatcherResult {
173 if self.nans_are_equal && self.expected.is_nan() && actual.is_nan() {
174 return MatcherResult::Match;
175 }
176
177 let delta = *actual - 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
describe(&self, matcher_result: MatcherResult) -> Description185 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)]
198 mod tests {
199 use super::{approx_eq, near};
200 use crate::matcher::{Matcher, MatcherResult};
201 use crate::prelude::*;
202
203 #[test]
matches_value_inside_range() -> Result<()>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]
matches_value_at_low_end_of_range() -> Result<()>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]
matches_value_at_high_end_of_range() -> Result<()>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]
does_not_match_value_below_low_end_of_range() -> Result<()>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]
does_not_match_value_above_high_end_of_range() -> Result<()>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]
nan_is_not_near_a_number() -> Result<()>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]
nan_is_not_near_nan_by_default() -> Result<()>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]
nan_is_not_near_nan_when_explicitly_configured() -> Result<()>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]
nan_is_near_nan_if_nans_are_equal() -> Result<()>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]
nan_is_not_near_number_when_nans_are_equal() -> Result<()>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]
number_is_not_near_nan_when_nans_are_equal() -> Result<()>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]
inf_is_not_near_inf() -> Result<()>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]
inf_is_not_near_a_number() -> Result<()>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]
any_two_numbers_are_within_inf_of_each_other() -> Result<()>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]
panics_if_max_abs_error_is_nan()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]
panics_if_tolerance_is_negative()317 fn panics_if_tolerance_is_negative() {
318 near(0.0, -1.0);
319 }
320
321 #[test]
approx_eq_matches_equal_number() -> Result<()>322 fn approx_eq_matches_equal_number() -> Result<()> {
323 verify_that!(1.0f64, approx_eq(1.0f64))
324 }
325
326 #[test]
approx_eq_matches_really_close_f64_number() -> Result<()>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]
approx_eq_matches_really_close_f64_number_to_large_number() -> Result<()>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]
approx_eq_matches_really_close_f64_number_to_zero() -> Result<()>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]
approx_eq_matches_really_close_f32_number() -> Result<()>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]
approx_eq_does_not_match_distant_number() -> Result<()>347 fn approx_eq_does_not_match_distant_number() -> Result<()> {
348 verify_that!(0.0f64, not(approx_eq(1.0f64)))
349 }
350 }
351