galvanic_assert/matchers/
core.rs

1/* Copyright 2017 Christopher Bacher
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
16//! The core module contains the basic matchers needed for writing assertions.
17//!
18//! The matchers in this module all operate on single values.
19
20use std::fmt::Debug;
21use super::super::*;
22
23macro_rules! matchresult_from_comparison {
24    (  $actual: ident $comparison: tt $expected: ident, $name: expr ) => {{
25        let builder = MatchResultBuilder::for_($name);
26        if $actual $comparison &$expected {
27            builder.matched()
28        } else {
29            builder.failed_comparison($actual, &$expected)
30        }
31    }}
32}
33
34/// A matcher which always matches.
35///
36/// #Examples
37/// ```rust
38/// # #[macro_use] extern crate galvanic_assert;
39/// use galvanic_assert::matchers::*;
40/// # fn main() {
41/// assert_that!(&(1+1), assertion_always_succeeds());
42/// # }
43pub fn assertion_always_succeeds<'a,T:'a>() -> Box<Matcher<'a,T> + 'a> {
44    Box::new(|_s: &T| MatchResultBuilder::for_("succeeds_always").matched())
45}
46/// A matcher which always matches.
47///
48/// #Examples
49/// ```rust
50/// # #[macro_use] extern crate galvanic_assert;
51/// use galvanic_assert::matchers::*;
52/// # fn main() {
53/// assert_that!(&(1+1), any_value());
54/// # }
55pub fn any_value<'a,T:'a>() -> Box<Matcher<'a,T> + 'a> {
56    Box::new(|_s: &T| MatchResultBuilder::for_("any_value").matched())
57}
58
59/// A matcher which never matches.
60///
61/// #Examples
62/// ```rust
63/// # #[macro_use] extern crate galvanic_assert;
64/// use galvanic_assert::matchers::*;
65/// # fn main() {
66/// assert_that!(
67///     assert_that!(&(1+1), assertion_always_fails()),
68///     panics
69/// );
70/// # }
71pub fn assertion_always_fails<'a,T:'a>() -> Box<Matcher<'a,T> + 'a> {
72    Box::new(|_s: &T| {
73        MatchResultBuilder::for_("fails_always").failed_because("This matcher fails always")
74    })
75}
76/// A matcher which never matches.
77///
78/// #Examples
79/// ```rust
80/// # #[macro_use] extern crate galvanic_assert;
81/// use galvanic_assert::matchers::*;
82/// # fn main() {
83/// assert_that!(
84///     assert_that!(&(1+1), no_value()),
85///     panics
86/// );
87/// # }
88pub fn no_value<'a,T:'a>() -> Box<Matcher<'a,T> + 'a> { assertion_always_fails() }
89
90/// Accepts a matcher and returns it unmodified.
91///
92/// This is just syntactic sugar.
93///
94/// #Examples
95/// ```rust
96/// # #[macro_use] extern crate galvanic_assert;
97/// use galvanic_assert::matchers::*;
98/// # fn main() {
99/// assert_that!(&(1+1), is(eq(2)));
100/// # }
101pub fn is<'a, T:'a>(matcher: Box<Matcher<'a,T> + 'a>) -> Box<Matcher<'a,T> + 'a> {
102    matcher
103}
104
105/// Accepts a matcher and returns it unmodified.
106///
107/// This is just syntactic sugar.
108///
109/// #Examples
110/// ```rust
111/// # #[macro_use] extern crate galvanic_assert;
112/// use galvanic_assert::matchers::*;
113/// # fn main() {
114/// let vs = vec![1, 2];
115/// assert_that!(&vs.len(), has(lt(3)));
116/// # }
117pub fn has<'a, T:'a>(matcher: Box<Matcher<'a,T> + 'a>) -> Box<Matcher<'a,T> + 'a> {
118    matcher
119}
120
121/// A matcher negating the result of the passed matcher.
122///
123/// #Examples
124/// ```rust
125/// # #[macro_use] extern crate galvanic_assert;
126/// use galvanic_assert::matchers::*;
127/// # fn main() {
128/// assert_that!(&(1+1), not(eq(3)));
129/// # }
130pub fn not<'a, T: 'a>(matcher: Box<Matcher<'a,T> + 'a>) -> Box<Matcher<'a,T> + 'a> {
131    Box::new(move |actual: &'a T| {
132        match matcher.check(actual) {
133            MatchResult::Matched { name } =>
134                MatchResultBuilder::for_(&format!("not({})", name))
135                                   .failed_because(&format!("{} is satisfied", name)),
136            MatchResult::Failed { name, .. } =>
137                MatchResultBuilder::for_(&format!("not({})", name)).matched()
138        }
139    })
140}
141
142/// Matches if the asserted value is equal to the expected value.
143///
144/// This matcher should not be used when asserting floating point values.
145/// Use [close_to] instead. This is the same as [eq].
146///
147/// #Examples
148/// ```rust
149/// # #[macro_use] extern crate galvanic_assert;
150/// use galvanic_assert::matchers::*;
151/// # fn main() {
152/// assert_that!(&(1+1), equal_to(2));
153/// # }
154pub fn equal_to<'a, T>(expected: T) -> Box<Matcher<'a,T> + 'a>
155where T: PartialEq + Debug + 'a {
156    Box::new(move |actual: &T| matchresult_from_comparison!(actual == expected, "equal"))
157}
158/// Matches if the asserted value is equal to the expected value.
159///
160/// This matcher should not be used when asserting floating point values.
161/// Use [close_to] instead. This is the same as [equal_to].
162///
163/// #Examples
164/// ```rust
165/// # #[macro_use] extern crate galvanic_assert;
166/// use galvanic_assert::matchers::*;
167/// # fn main() {
168/// assert_that!(&(1+1), eq(2));
169/// # }
170pub fn eq<'a, T: PartialEq + Debug + 'a>(expected: T) -> Box<Matcher<'a,T> + 'a> { equal_to(expected) }
171
172/// Matches if the asserted value is less than the expected value.
173///
174/// This is the same as [lt].
175///
176/// #Examples
177/// ```rust
178/// # #[macro_use] extern crate galvanic_assert;
179/// use galvanic_assert::matchers::*;
180/// # fn main() {
181/// assert_that!(&(1+1), less_than(3));
182/// # }
183pub fn less_than<'a, T>(expected: T) -> Box<Matcher<'a,T> + 'a>
184where T: PartialOrd + Debug + 'a {
185    Box::new(move |actual: &T| matchresult_from_comparison!(actual < expected, "less_than"))
186}
187/// Matches if the asserted value is less than the expected value.
188///
189/// This is the same as [less_than].
190///
191/// #Examples
192/// ```rust
193/// # #[macro_use] extern crate galvanic_assert;
194/// use galvanic_assert::matchers::*;
195/// # fn main() {
196/// assert_that!(&(1+1), less_than(3));
197/// # }
198pub fn lt<'a, T: PartialOrd + Debug + 'a>(expected: T) -> Box<Matcher<'a,T> + 'a> { less_than(expected) }
199
200/// Matches if the asserted value is greater than the expected value.
201///
202/// This is the same as [gt].
203///
204/// #Examples
205/// ```rust
206/// # #[macro_use] extern crate galvanic_assert;
207/// use galvanic_assert::matchers::*;
208/// # fn main() {
209/// assert_that!(&(1+1), greater_than(1));
210/// # }
211pub fn greater_than<'a, T>(expected: T) -> Box<Matcher<'a,T> + 'a>
212where T: PartialOrd + Debug + 'a {
213    Box::new(move |actual: &T| matchresult_from_comparison!(actual > expected, "greater_than"))
214}
215/// Matches if the asserted value is greater than the expected value.
216///
217/// This is the same as [greater_than].
218///
219/// #Examples
220/// ```rust
221/// # #[macro_use] extern crate galvanic_assert;
222/// use galvanic_assert::matchers::*;
223/// # fn main() {
224/// assert_that!(&(1+1), gt(1));
225/// # }
226pub fn gt<'a, T: PartialOrd + Debug + 'a>(expected: T) -> Box<Matcher<'a,T> + 'a> { greater_than(expected) }
227
228/// Matches if the asserted value is less than or equal to the expected value.
229///
230/// This is the same as [leq].
231///
232/// #Examples
233/// ```rust
234/// # #[macro_use] extern crate galvanic_assert;
235/// use galvanic_assert::matchers::*;
236/// # fn main() {
237/// assert_that!(&(1+1), less_than_or_equal(3));
238/// assert_that!(&(1+1), less_than_or_equal(2));
239/// # }
240pub fn less_than_or_equal<'a, T>(expected: T) -> Box<Matcher<'a,T> + 'a>
241where T: PartialOrd + Debug + 'a {
242    Box::new(move |actual: &T| matchresult_from_comparison!(actual <= expected, "less_than_or_equal"))
243}
244/// Matches if the asserted value is less than or equal to the expected value.
245///
246/// This is the same as [less_than_or_equal].
247///
248/// #Examples
249/// ```rust
250/// # #[macro_use] extern crate galvanic_assert;
251/// use galvanic_assert::matchers::*;
252/// # fn main() {
253/// assert_that!(&(1+1), leq(3));
254/// assert_that!(&(1+1), leq(2));
255/// # }
256pub fn leq<'a, T: PartialOrd + Debug + 'a>(expected: T) -> Box<Matcher<'a,T> + 'a> { less_than_or_equal(expected) }
257
258/// Matches if the asserted value is greater than or equal to the expected value.
259///
260/// This is the same as [geq].
261///
262/// #Examples
263/// ```rust
264/// # #[macro_use] extern crate galvanic_assert;
265/// use galvanic_assert::matchers::*;
266/// # fn main() {
267/// assert_that!(&(1+1), greater_than_or_equal(1));
268/// assert_that!(&(1+1), greater_than_or_equal(2));
269/// # }
270pub fn greater_than_or_equal<'a, T>(expected: T) -> Box<Matcher<'a,T> + 'a>
271where T: PartialOrd + Debug + 'a {
272    Box::new(move |actual: &T| matchresult_from_comparison!(actual >= expected, "greater_than_or_equal"))
273}
274/// Matches if the asserted value is greater than or equal to the expected value.
275///
276/// This is the same as [greater_than_or_equal].
277///
278/// #Examples
279/// ```rust
280/// # #[macro_use] extern crate galvanic_assert;
281/// use galvanic_assert::matchers::*;
282/// # fn main() {
283/// assert_that!(&(1+1), geq(1));
284/// assert_that!(&(1+1), geq(2));
285/// # }
286pub fn geq<'a, T: PartialOrd + Debug + 'a>(expected: T) -> Box<Matcher<'a,T> + 'a> { greater_than_or_equal(expected) }
287
288/// Matches if the asserted value is in an epsilon range around the expected value.
289///
290/// If floating point values are compared for equality this matcher should be used instead of [equal_to]
291///
292/// #Examples
293/// ```rust
294/// # #[macro_use] extern crate galvanic_assert;
295/// use galvanic_assert::matchers::*;
296/// # fn main() {
297/// assert_that!(&(1.2 + 3.14), close_to(4.34, 0.00001));
298/// # }
299pub fn close_to<'a, T>(expected: T, eps: T) -> Box<Matcher<'a,T> + 'a>
300where T: Copy + PartialOrd + std::ops::Add<Output=T> + std::ops::Sub<Output=T> + Debug + 'a {
301    Box::new(move |actual: &T| {
302        let builder = MatchResultBuilder::for_("close_to");
303        if &(expected - eps) <= actual && actual <= &(expected + eps) {
304            builder.matched()
305        } else {
306            builder.failed_because(&format!("{:?} should be between {:?} and {:?}",
307                                            actual, expected - eps, expected + eps)
308            )
309        }
310    })
311}
312
313/// Matches if asserted value and the expected value are truely the same object.
314///
315/// The two values are the same if the reside at the same memory address.
316///
317/// #Examples
318/// ```rust
319/// # #[macro_use] extern crate galvanic_assert;
320/// use galvanic_assert::matchers::*;
321/// # fn main() {
322/// #[derive(Debug)]
323/// struct Foo;
324/// let foo1 = Foo;
325/// let foo2 = Foo;
326///
327/// assert_that!(&foo1, same_object(&foo1));
328///
329/// assert_that!(
330///     assert_that!(&foo1, same_object(&foo2)),
331///     panics
332/// );
333/// # }
334pub fn same_object<'a, T>(expected: &'a T) -> Box<Matcher<'a,T> + 'a>
335where T: Debug + 'a {
336    Box::new(move |actual: &T| {
337        let builder = MatchResultBuilder::for_("same_object");
338        if (actual as *const _) == (expected as *const _) {
339            builder.matched()
340        } else {
341            builder.failed_comparison(&actual, &expected)
342        }
343    })
344}
345
346/// Write patterns of structs/enums which use `Matcher`s instead of field values.
347///
348/// When providing matchers for multiple fields, not that *all* matchers will be evaluated.
349/// Even if one of them returns `MatchResult::Failed`.
350///
351/// For struct-like structs/enum-variants not all fields need to be listed in the pattern.
352/// Unwanted fields can safely be ommitted. For tuple-like structs/enum-variants all fields
353/// need to be listed in the correct order. Although you can use `any_value()`
354/// to effectively ignore the field.
355///
356/// Note that the correct brace/bracket style for tuple-like structs/enums is `Variant[any_value(), any_value()]`
357/// not `Variant(any_value(), any_value())`. This discrepancy is due to macro parsing reasons.
358///
359/// #Examples
360/// ```rust
361/// // Matching a struct-like ...
362/// # #[macro_use] extern crate galvanic_assert;
363/// use galvanic_assert::matchers::*;
364/// # fn main() {
365/// struct Foo { x: i32, y: f64 }
366/// let foo = Foo { x: 12, y: 23.4 };
367/// assert_that!(&foo, has_structure!(Foo {
368///     x: eq(12),
369///     y: lt(25.0)
370/// }));
371/// assert_that!(&foo, has_structure!(Foo {
372///     y: lt(25.0) // not all fields need to be given for struct-likes
373/// }));
374///
375/// // Matching a tuple-like ...
376/// struct Bar(i32, f64);
377/// let bar = Bar(12, 23.4);
378/// assert_that!(&bar, has_structure!(Bar[ eq(12), lt(25.0) ]));
379///
380/// // Matching enum variants ...
381/// enum Baz {
382///     Var1 { x: i32, y: f64 },
383///     Var2(i32, f64)
384/// }
385/// let var1 = Baz::Var1 { x: 12, y: 23.4 };
386/// assert_that!(&var1, has_structure!(Baz::Var1 {
387///     x: eq(12),
388///     y: lt(25.0)
389/// }));
390///
391/// let var2 = Baz::Var2(12, 23.4);
392/// assert_that!(&var2, has_structure!(Baz::Var2 [eq(12), lt(25.0)] ));
393/// # }
394#[macro_export]
395macro_rules! has_structure {
396    ( $variant:path { $( $field:ident : $matcher:expr ),* $(,)* } ) => { structure!($variant { $($field : $matcher),* }) };
397    ( $variant:path [ $( $matchers:expr ),* ] ) => { structure!($variant [ $($matchers),* ]) }
398}
399/// Shorter name for `has_structure!`.
400#[macro_export]
401macro_rules! structure {
402    ( $variant:path { $( $field:ident : $matcher:expr ),* $(,)* } ) => {
403        Box::new(|actual: &_| {
404            use galvanic_assert::{MatchResultBuilder, MatchResult};
405            let builder = MatchResultBuilder::for_("has_structure");
406            #[allow(unreachable_patterns)]
407            match actual {
408                &$variant { $( ref $field, )* ..} => {
409                    let mut failed_msgs = Vec::new();
410                    $(
411                        if let MatchResult::Failed{ name, reason } = $matcher.check($field) {
412                            failed_msgs.push(
413                                format!("Matcher '{}' for field '{}' at {}:{} failed:\n\t{}",
414                                        name, stringify!($field), file!().to_string(), line!(), reason)
415                            );
416                        }
417                    )*
418                    if failed_msgs.is_empty() { builder.matched() }
419                    else { builder.failed_because(&failed_msgs.join("\n")) }
420                },
421                _ => builder.failed_because(
422                        &format!("passed variant does not match '{}'", stringify!($variant))
423                )
424            }
425        })
426    };
427
428    (@expand ( $variant:path ; $field:ident ; $m:expr ; $($wildcard:tt),* ) -> ($($body:tt)*) ) => {
429        structure!(@generate ($field ; $($body)* ($m ; &$variant($($wildcard,)* ref $field))) )
430    };
431    (@expand ( $variant:path ; $field:ident ; $m:expr , $($matchers:expr),* ; $($wildcard:tt),* ) -> ($($body:tt)*) ) => {
432        structure!(@expand ( $variant ; $field ; $($matchers),* ; $($wildcard,)* _ ) -> ($($body)* ($m ; &$variant($($wildcard,)* ref $field, ..)),) )
433    };
434    (@generate ($field:ident ; $(($matcher:expr ; $pattern:pat)),*) ) => {
435        Box::new(|actual: &_| {
436            use galvanic_assert::{MatchResultBuilder, MatchResult};
437            let builder = MatchResultBuilder::for_("has_structure");
438
439            let mut failed_msgs = Vec::new();
440            $(
441                #[allow(unreachable_patterns)]
442                match actual {
443                    $pattern => if let MatchResult::Failed{ name, reason } = $matcher.check($field) {
444                        failed_msgs.push(
445                            format!("Matcher '{}' for field '{}' at {}:{} failed:\n\t{}",
446                                    name, stringify!($field), file!().to_string(), line!(), reason)
447                        );
448                    },
449                    _ => return builder.failed_because(
450                            &format!("passed variant does not match '{}'", stringify!($variant))
451                    )
452                }
453            )*
454
455            if failed_msgs.is_empty() { builder.matched() }
456            else { builder.failed_because(&failed_msgs.join("\n")) }
457        })
458    };
459    ( $variant:path [ $( $matchers:expr ),* ] ) => {
460        structure![ @expand ( $variant ; x ; $($matchers),* ; ) -> () ]
461    };
462}