Macro googletest::matchers::matches_pattern

source ·
macro_rules! matches_pattern {
    ($($t:tt)*) => { ... };
}
Expand description

Matches a value according to a pattern of matchers.

This takes as an argument a specification similar to a struct or enum initialiser, where each value is a Matcher which is applied to the corresponding field.

This can be used to match arbitrary combinations of fields on structures using arbitrary matchers:

#[derive(Debug)]
struct MyStruct {
    a_field: String,
    another_field: String,
}

let my_struct = MyStruct {
    a_field: "Something to believe in".into(),
    another_field: "Something else".into()
};
verify_that!(my_struct, matches_pattern!(MyStruct {
    a_field: starts_with("Something"),
    another_field: ends_with("else"),
}))

It is not required to include all named fields in the specification. Omitted fields have no effect on the output of the matcher.

verify_that!(my_struct, matches_pattern!(MyStruct {
    a_field: starts_with("Something"),
    // another_field is missing, so it may be anything.
}))

One can use it recursively to match nested structures:

#[derive(Debug)]
struct MyStruct {
    a_nested_struct: MyInnerStruct,
}

#[derive(Debug)]
struct MyInnerStruct {
    a_field: String,
}

let my_struct = MyStruct {
    a_nested_struct: MyInnerStruct { a_field: "Something to believe in".into() },
};
verify_that!(my_struct, matches_pattern!(MyStruct {
     a_nested_struct: matches_pattern!(MyInnerStruct {
        a_field: starts_with("Something"),
    }),
}))

One can use the alias pat to make this less verbose:

verify_that!(my_struct, matches_pattern!(MyStruct {
    a_nested_struct: pat!(MyInnerStruct {
        a_field: starts_with("Something"),
    }),
}))

In addition to fields, one can match on the outputs of methods (“properties”):

#[derive(Debug)]
struct MyStruct {
    a_field: String,
}

impl MyStruct {
    fn get_a_field(&self) -> String { self.a_field.clone() }
}

let my_struct = MyStruct { a_field: "Something to believe in".into() };
verify_that!(my_struct, matches_pattern!(MyStruct {
    get_a_field(): starts_with("Something"),
}))

If an inner matcher is eq(...), it can be omitted:

#[derive(Debug)]
struct MyStruct {
    a_field: String,
    another_field: String,
}

let my_struct = MyStruct {
    a_field: "this".into(),
    another_field: "that".into()
};
verify_that!(my_struct, matches_pattern!(MyStruct {
    a_field: "this",
    another_field: "that",
}))

Important: The method should be pure function with a deterministic output and no side effects. In particular, in the event of an assertion failure, it will be invoked a second time, with the assertion failure output reflecting the second invocation.

These may also include extra litteral parameters you pass in:

impl MyStruct {
    fn append_to_a_field(&self, suffix: &str) -> String { self.a_field.clone() + suffix }
}

verify_that!(my_struct, matches_pattern!(&MyStruct {
    append_to_a_field("a suffix"): ref ends_with("a suffix"),
}))

You can precede both field and property matchers with a ref to match the result by reference:

impl MyStruct {
    fn get_a_field_ref(&self) -> String { self.a_field.clone() }
}

verify_that!(my_struct, matches_pattern!(&MyStruct {
    get_a_field_ref(): ref starts_with("Something"),
}))

Note that if the actual is of type &ActualT and the pattern type is ActualT, this is automatically performed. This behavior is similar to the reference binding mode in pattern matching.

impl MyStruct {
    fn get_a_field_ref(&self) -> String { self.a_field.clone() }
}

verify_that!(my_struct, matches_pattern!(MyStruct {
    get_a_field_ref(): starts_with("Something"),
}))

One can also match tuple structs with up to 10 fields. In this case, all fields must have matchers:

#[derive(Debug)]
struct MyTupleStruct(String, String);

let my_struct = MyTupleStruct("Something".into(), "Some other thing".into());
verify_that!(
    my_struct,
    matches_pattern!(&MyTupleStruct(ref eq("Something"), ref eq("Some other thing")))
)

One can also match enum values:

#[derive(Debug)]
enum MyEnum {
    A(u32),
    B,
}

verify_that!(MyEnum::A(123), matches_pattern!(&MyEnum::A(eq(123))))?; // Passes
verify_that!(MyEnum::B, matches_pattern!(&MyEnum::A(eq(123))))?; // Fails - wrong enum variant

This macro does not support plain (non-struct) tuples. But it should not be necessary as tuple of matchers are matchers of tuple. In other words, if MatcherU: Matcher<U> and MatcherT: Matcher<T>, then (MatcherU, MatcherT): Matcher<(U, T)>.

Trailing commas are allowed (but not required) in both ordinary and tuple structs.

Note that the default format (rustfmt) can format macros if the macro argument is parseable Rust code. This is mostly true for this macro with two exceptions:

  • property matching
  • ref keyword with named fields

An option for formatting large is to avoid these exceptions (by removing the parenthesis of properties and the ref keywords), run rustfmt and add them back.