test_that/matchers/
near_matcher.rs1use crate::{
17 description::Description,
18 matcher::{Describable, Matcher, MatcherResult},
19};
20use core::fmt::Debug;
21use num_traits::{Float, FloatConst};
22
23pub 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
141pub struct NearMatcher<T: Debug> {
144 expected: T,
145 max_abs_error: T,
146 nans_are_equal: bool,
147}
148
149impl<T: Debug> NearMatcher<T> {
150 pub fn nans_are_equal(mut self) -> Self {
155 self.nans_are_equal = true;
156 self
157 }
158
159 pub fn nans_are_not_equal(mut self) -> Self {
165 self.nans_are_equal = false;
166 self
167 }
168}
169
170impl<T: Debug + Float> Matcher<T> for NearMatcher<T> {
171 fn matches(&self, actual: &T) -> MatcherResult {
172 if self.nans_are_equal && self.expected.is_nan() && actual.is_nan() {
173 return MatcherResult::Match;
174 }
175
176 let delta = *actual - self.expected;
177 if delta >= -self.max_abs_error && delta <= self.max_abs_error {
178 MatcherResult::Match
179 } else {
180 MatcherResult::NoMatch
181 }
182 }
183}
184
185impl<T: Debug> Describable for NearMatcher<T> {
186 fn describe(&self, matcher_result: MatcherResult) -> Description {
187 match matcher_result {
188 MatcherResult::Match => {
189 format!("is within {:?} of {:?}", self.max_abs_error, self.expected).into()
190 }
191 MatcherResult::NoMatch => {
192 format!("isn't within {:?} of {:?}", self.max_abs_error, self.expected).into()
193 }
194 }
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::{approx_eq, near};
201 use crate::matcher::{Matcher, MatcherResult};
202 use crate::prelude::*;
203
204 #[test]
205 fn matches_value_inside_range() -> TestResult<()> {
206 let matcher = near(1.0f64, 0.1f64);
207
208 let result = matcher.matches(&1.0f64);
209
210 verify_that!(result, eq(MatcherResult::Match))
211 }
212
213 #[test]
214 fn matches_value_at_low_end_of_range() -> TestResult<()> {
215 let matcher = near(1.0f64, 0.1f64);
216
217 let result = matcher.matches(&0.9f64);
218
219 verify_that!(result, eq(MatcherResult::Match))
220 }
221
222 #[test]
223 fn matches_value_at_high_end_of_range() -> TestResult<()> {
224 let matcher = near(1.0f64, 0.25f64);
225
226 let result = matcher.matches(&1.25f64);
227
228 verify_that!(result, eq(MatcherResult::Match))
229 }
230
231 #[test]
232 fn does_not_match_value_below_low_end_of_range() -> TestResult<()> {
233 let matcher = near(1.0f64, 0.1f64);
234
235 let result = matcher.matches(&0.899999f64);
236
237 verify_that!(result, eq(MatcherResult::NoMatch))
238 }
239
240 #[test]
241 fn does_not_match_value_above_high_end_of_range() -> TestResult<()> {
242 let matcher = near(1.0f64, 0.1f64);
243
244 let result = matcher.matches(&1.100001f64);
245
246 verify_that!(result, eq(MatcherResult::NoMatch))
247 }
248
249 #[test]
250 fn nan_is_not_near_a_number() -> TestResult<()> {
251 let matcher = near(0.0f64, f64::MAX);
252
253 let result = matcher.matches(&f64::NAN);
254
255 verify_that!(result, eq(MatcherResult::NoMatch))
256 }
257
258 #[test]
259 fn nan_is_not_near_nan_by_default() -> TestResult<()> {
260 verify_that!(f64::NAN, not(near(f64::NAN, f64::MAX)))
261 }
262
263 #[test]
264 fn nan_is_not_near_nan_when_explicitly_configured() -> TestResult<()> {
265 verify_that!(f64::NAN, not(near(f64::NAN, f64::MAX).nans_are_not_equal()))
266 }
267
268 #[test]
269 fn nan_is_near_nan_if_nans_are_equal() -> TestResult<()> {
270 verify_that!(f64::NAN, near(f64::NAN, f64::MAX).nans_are_equal())
271 }
272
273 #[test]
274 fn nan_is_not_near_number_when_nans_are_equal() -> TestResult<()> {
275 verify_that!(f64::NAN, not(near(0.0, f64::MAX).nans_are_equal()))
276 }
277
278 #[test]
279 fn number_is_not_near_nan_when_nans_are_equal() -> TestResult<()> {
280 verify_that!(0.0, not(near(f64::NAN, f64::MAX).nans_are_equal()))
281 }
282
283 #[test]
284 fn inf_is_not_near_inf() -> TestResult<()> {
285 let matcher = near(f64::INFINITY, f64::MAX);
286
287 let result = matcher.matches(&f64::INFINITY);
288
289 verify_that!(result, eq(MatcherResult::NoMatch))
290 }
291
292 #[test]
293 fn inf_is_not_near_a_number() -> TestResult<()> {
294 let matcher = near(f64::INFINITY, f64::MAX);
295
296 let result = matcher.matches(&f64::MIN);
297
298 verify_that!(result, eq(MatcherResult::NoMatch))
299 }
300
301 #[test]
302 fn any_two_numbers_are_within_inf_of_each_other() -> TestResult<()> {
303 let matcher = near(f64::MIN, f64::INFINITY);
304
305 let result = matcher.matches(&f64::MAX);
306
307 verify_that!(result, eq(MatcherResult::Match))
308 }
309
310 #[::core::prelude::v1::test]
311 #[should_panic]
312 fn panics_if_max_abs_error_is_nan() {
313 near(0.0, f64::NAN);
314 }
315
316 #[::core::prelude::v1::test]
317 #[should_panic]
318 fn panics_if_tolerance_is_negative() {
319 near(0.0, -1.0);
320 }
321
322 #[test]
323 fn approx_eq_matches_equal_number() -> TestResult<()> {
324 verify_that!(1.0f64, approx_eq(1.0f64))
325 }
326
327 #[test]
328 fn approx_eq_matches_really_close_f64_number() -> TestResult<()> {
329 verify_that!(1.0f64, approx_eq(1.0 + 16.0 * f64::EPSILON))
330 }
331
332 #[test]
333 fn approx_eq_matches_really_close_f64_number_to_large_number() -> TestResult<()> {
334 verify_that!(1000f64, approx_eq(1000.0 + 16000.0 * f64::EPSILON))
335 }
336
337 #[test]
338 fn approx_eq_matches_really_close_f64_number_to_zero() -> TestResult<()> {
339 verify_that!(16.0 * f64::EPSILON, approx_eq(0.0))
340 }
341
342 #[test]
343 fn approx_eq_matches_really_close_f32_number() -> TestResult<()> {
344 verify_that!(1.0f32, approx_eq(1.0 + 16.0 * f32::EPSILON))
345 }
346
347 #[test]
348 fn approx_eq_does_not_match_distant_number() -> TestResult<()> {
349 verify_that!(0.0f64, not(approx_eq(1.0f64)))
350 }
351}