googletest/matchers/
near_matcher.rs1use crate::{
16 description::Description,
17 matcher::{Matcher, MatcherBase, MatcherResult},
18};
19use num_traits::{Float, FloatConst};
20use std::{borrow::Borrow, fmt::Debug};
21
22#[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
124pub fn approx_eq<T: Debug + Float + FloatConst + Copy>(expected: T) -> NearMatcher<T> {
134 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#[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 pub fn nans_are_equal(mut self) -> Self {
156 self.nans_are_equal = true;
157 self
158 }
159
160 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}