Skip to content

Commit 5ff7aa5

Browse files
committed
Parameterize Matcher::matches and Matcher::explain_match.
This allows the use of smart pointers for the actual value when invoking `Matcher::matches` and `Matcher::explain_match`. The Protobuf implementation namely uses a [kind of smart pointer](https://github.com/protocolbuffers/protobuf/blob/62d5d9bf67d27d3b0084dabcf67bff2f8238162b/rust/proxied.rs#L113). The lifetime on that smart pointer has no a priori relation to that of the parameter `actual`, causing the borrow checker to complain whenever invoking an inner matcher on the output of `as_view`. This commit addresses the problem by allowing the `ViewProxy` -- rather than a reference to it -- to be passed directly to `Matcher::matches` and `Matcher::explain_match`. It does this by making those two methods generic with respect to the input type. They must only implement `Deref<Target = Self::ActualT>`. The new test `googletest/tests/arena_test.rs` illustrates this with a simplified example. Introducing these generics unfortunately makes `Matcher` no longer object-safe. Since several matchers construct trait objts on `Matcher`, this also introduces an internal-only wrapper trait `ObjectSafeMatcher` which every `Matcher` auto-implements. While this makes some changes to how `Matcher` is defined and implemented, it should have no effect on how it is _used_, other than to fix the aforementioned problem. So it should not affect compatibility for most users of the crate. Fixes #323.
1 parent 4a1540b commit 5ff7aa5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+694
-226
lines changed

googletest/crate_docs.md

+15-6
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ a struct holding the matcher's data and have it implement the trait
214214

215215
```no_run
216216
use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
217-
use std::fmt::Debug;
217+
use std::{fmt::Debug, ops::Deref};
218218
219219
struct MyEqMatcher<T> {
220220
expected: T,
@@ -223,7 +223,10 @@ struct MyEqMatcher<T> {
223223
impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
224224
type ActualT = T;
225225
226-
fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
226+
fn matches<ActualRefT: Deref<Target = Self::ActualT>>(
227+
&self,
228+
actual: ActualRefT,
229+
) -> MatcherResult {
227230
if self.expected == *actual {
228231
MatcherResult::Match
229232
} else {
@@ -248,7 +251,7 @@ impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
248251

249252
```no_run
250253
# use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
251-
# use std::fmt::Debug;
254+
# use std::{fmt::Debug, ops::Deref};
252255
#
253256
# struct MyEqMatcher<T> {
254257
# expected: T,
@@ -257,7 +260,10 @@ impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
257260
# impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
258261
# type ActualT = T;
259262
#
260-
# fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
263+
# fn matches<ActualRefT: Deref<Target = Self::ActualT>>(
264+
# &self,
265+
# actual: ActualRefT,
266+
# ) -> MatcherResult {
261267
# if self.expected == *actual {
262268
# MatcherResult::Match
263269
# } else {
@@ -287,7 +293,7 @@ impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
287293
```
288294
# use googletest::prelude::*;
289295
# use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
290-
# use std::fmt::Debug;
296+
# use std::{fmt::Debug, ops::Deref};
291297
#
292298
# struct MyEqMatcher<T> {
293299
# expected: T,
@@ -296,7 +302,10 @@ impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
296302
# impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
297303
# type ActualT = T;
298304
#
299-
# fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
305+
# fn matches<ActualRefT: Deref<Target = Self::ActualT>>(
306+
# &self,
307+
# actual: ActualRefT,
308+
# ) -> MatcherResult {
300309
# if self.expected == *actual {
301310
# MatcherResult::Match
302311
# } else {

googletest/src/matcher.rs

+64-7
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::internal::test_outcome::TestAssertionFailure;
2020
use crate::matchers::__internal_unstable_do_not_depend_on_these::ConjunctionMatcher;
2121
use crate::matchers::__internal_unstable_do_not_depend_on_these::DisjunctionMatcher;
2222
use std::fmt::Debug;
23+
use std::ops::Deref;
2324

2425
/// An interface for checking an arbitrary condition on a datum.
2526
///
@@ -36,7 +37,10 @@ pub trait Matcher {
3637
/// matching condition is based on data stored in the matcher. For example,
3738
/// `eq` matches when its stored expected value is equal (in the sense of
3839
/// the `==` operator) to the value `actual`.
39-
fn matches(&self, actual: &Self::ActualT) -> MatcherResult;
40+
fn matches<ActualRefT: Deref<Target = Self::ActualT> + Clone>(
41+
&self,
42+
actual: ActualRefT,
43+
) -> MatcherResult;
4044

4145
/// Returns a description of `self` or a negative description if
4246
/// `matcher_result` is `DoesNotMatch`.
@@ -137,7 +141,10 @@ pub trait Matcher {
137141
/// .nested(self.expected.explain_match(actual.deref()))
138142
/// }
139143
/// ```
140-
fn explain_match(&self, actual: &Self::ActualT) -> Description {
144+
fn explain_match<ActualRefT: Deref<Target = Self::ActualT> + Clone>(
145+
&self,
146+
actual: ActualRefT,
147+
) -> Description {
141148
format!("which {}", self.describe(self.matches(actual))).into()
142149
}
143150

@@ -205,6 +212,46 @@ pub trait Matcher {
205212
}
206213
}
207214

215+
/// Functionality used by macros within this crate.
216+
///
217+
/// For internal use only. API stablility is not guaranteed!
218+
#[doc(hidden)]
219+
pub mod __internal_unstable_do_not_depend_on_these {
220+
use super::{Matcher, MatcherResult};
221+
use crate::description::Description;
222+
use std::fmt::Debug;
223+
224+
/// A variant of [`Matcher`] which is object-safe.
225+
///
226+
/// This is used in contexts where a `dyn Matcher` is required. It supplies all methods of
227+
/// [`Matcher`] but without any generics on the methods.
228+
pub trait ObjectSafeMatcher {
229+
type ActualT: Debug + ?Sized;
230+
231+
fn obj_matches(&self, actual: &Self::ActualT) -> MatcherResult;
232+
233+
fn obj_describe(&self, matcher_result: MatcherResult) -> Description;
234+
235+
fn obj_explain_match(&self, actual: &Self::ActualT) -> Description;
236+
}
237+
238+
impl<MatcherT: Matcher> ObjectSafeMatcher for MatcherT {
239+
type ActualT = <Self as Matcher>::ActualT;
240+
241+
fn obj_matches(&self, actual: &Self::ActualT) -> MatcherResult {
242+
Matcher::matches(self, actual)
243+
}
244+
245+
fn obj_describe(&self, matcher_result: MatcherResult) -> Description {
246+
Matcher::describe(self, matcher_result)
247+
}
248+
249+
fn obj_explain_match(&self, actual: &Self::ActualT) -> Description {
250+
Matcher::explain_match(self, actual)
251+
}
252+
}
253+
}
254+
208255
/// Any actual value whose debug length is greater than this value will be
209256
/// pretty-printed. Otherwise, it will have normal debug output formatting.
210257
const PRETTY_PRINT_LENGTH_THRESHOLD: usize = 60;
@@ -249,7 +296,11 @@ pub enum MatcherResult {
249296

250297
impl From<bool> for MatcherResult {
251298
fn from(b: bool) -> Self {
252-
if b { MatcherResult::Match } else { MatcherResult::NoMatch }
299+
if b {
300+
MatcherResult::Match
301+
} else {
302+
MatcherResult::NoMatch
303+
}
253304
}
254305
}
255306

@@ -276,16 +327,22 @@ impl MatcherResult {
276327
impl<M: Matcher> Matcher for &M {
277328
type ActualT = M::ActualT;
278329

279-
fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
280-
(*self).matches(actual)
330+
fn matches<ActualRefT: Deref<Target = Self::ActualT>>(
331+
&self,
332+
actual: ActualRefT,
333+
) -> MatcherResult {
334+
(*self).matches(actual.deref())
281335
}
282336

283337
fn describe(&self, matcher_result: MatcherResult) -> Description {
284338
(*self).describe(matcher_result)
285339
}
286340

287-
fn explain_match(&self, actual: &Self::ActualT) -> Description {
288-
(*self).explain_match(actual)
341+
fn explain_match<ActualRefT: Deref<Target = Self::ActualT>>(
342+
&self,
343+
actual: ActualRefT,
344+
) -> Description {
345+
(*self).explain_match(actual.deref())
289346
}
290347
}
291348

googletest/src/matchers/anything_matcher.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::{
1616
description::Description,
1717
matcher::{Matcher, MatcherResult},
1818
};
19-
use std::{fmt::Debug, marker::PhantomData};
19+
use std::{fmt::Debug, marker::PhantomData, ops::Deref};
2020

2121
/// Matches anything. This matcher always succeeds.
2222
///
@@ -41,7 +41,7 @@ struct Anything<T: ?Sized>(PhantomData<T>);
4141
impl<T: Debug + ?Sized> Matcher for Anything<T> {
4242
type ActualT = T;
4343

44-
fn matches(&self, _: &T) -> MatcherResult {
44+
fn matches<ActualRefT: Deref<Target = Self::ActualT>>(&self, _: ActualRefT) -> MatcherResult {
4545
MatcherResult::Match
4646
}
4747

googletest/src/matchers/char_count_matcher.rs

+18-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::{
1616
description::Description,
1717
matcher::{Matcher, MatcherResult},
1818
};
19-
use std::{fmt::Debug, marker::PhantomData};
19+
use std::{fmt::Debug, marker::PhantomData, ops::Deref};
2020

2121
/// Matches a string whose number of Unicode scalars matches `expected`.
2222
///
@@ -70,7 +70,10 @@ struct CharLenMatcher<T: ?Sized, E> {
7070
impl<T: Debug + ?Sized + AsRef<str>, E: Matcher<ActualT = usize>> Matcher for CharLenMatcher<T, E> {
7171
type ActualT = T;
7272

73-
fn matches(&self, actual: &T) -> MatcherResult {
73+
fn matches<ActualRefT: Deref<Target = Self::ActualT>>(
74+
&self,
75+
actual: ActualRefT,
76+
) -> MatcherResult {
7477
self.expected.matches(&actual.as_ref().chars().count())
7578
}
7679

@@ -89,7 +92,10 @@ impl<T: Debug + ?Sized + AsRef<str>, E: Matcher<ActualT = usize>> Matcher for Ch
8992
}
9093
}
9194

92-
fn explain_match(&self, actual: &T) -> Description {
95+
fn explain_match<ActualRefT: Deref<Target = Self::ActualT>>(
96+
&self,
97+
actual: ActualRefT,
98+
) -> Description {
9399
let actual_size = actual.as_ref().chars().count();
94100
format!(
95101
"which has character count {}, {}",
@@ -109,6 +115,7 @@ mod tests {
109115
use indoc::indoc;
110116
use std::fmt::Debug;
111117
use std::marker::PhantomData;
118+
use std::ops::Deref;
112119

113120
#[test]
114121
fn char_count_matches_string_slice() -> Result<()> {
@@ -134,15 +141,21 @@ mod tests {
134141
impl<T: Debug> Matcher for TestMatcher<T> {
135142
type ActualT = T;
136143

137-
fn matches(&self, _: &T) -> MatcherResult {
144+
fn matches<ActualRefT: Deref<Target = Self::ActualT>>(
145+
&self,
146+
_: ActualRefT,
147+
) -> MatcherResult {
138148
false.into()
139149
}
140150

141151
fn describe(&self, _: MatcherResult) -> Description {
142152
"called described".into()
143153
}
144154

145-
fn explain_match(&self, _: &T) -> Description {
155+
fn explain_match<ActualRefT: Deref<Target = Self::ActualT>>(
156+
&self,
157+
_: ActualRefT,
158+
) -> Description {
146159
"called explain_match".into()
147160
}
148161
}

googletest/src/matchers/conjunction_matcher.rs

+16-10
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::{
1919
description::Description,
2020
matcher::{Matcher, MatcherResult},
2121
};
22-
use std::fmt::Debug;
22+
use std::{fmt::Debug, ops::Deref};
2323

2424
/// Matcher created by [`Matcher::and`] and [`all!`].
2525
///
@@ -58,25 +58,31 @@ where
5858
{
5959
type ActualT = M1::ActualT;
6060

61-
fn matches(&self, actual: &M1::ActualT) -> MatcherResult {
62-
match (self.m1.matches(actual), self.m2.matches(actual)) {
61+
fn matches<ActualRefT: Deref<Target = Self::ActualT> + Clone>(
62+
&self,
63+
actual: ActualRefT,
64+
) -> MatcherResult {
65+
match (self.m1.matches(actual.clone()), self.m2.matches(actual.clone())) {
6366
(MatcherResult::Match, MatcherResult::Match) => MatcherResult::Match,
6467
_ => MatcherResult::NoMatch,
6568
}
6669
}
6770

68-
fn explain_match(&self, actual: &M1::ActualT) -> Description {
69-
match (self.m1.matches(actual), self.m2.matches(actual)) {
70-
(MatcherResult::NoMatch, MatcherResult::Match) => self.m1.explain_match(actual),
71-
(MatcherResult::Match, MatcherResult::NoMatch) => self.m2.explain_match(actual),
71+
fn explain_match<ActualRefT: Deref<Target = Self::ActualT> + Clone>(
72+
&self,
73+
actual: ActualRefT,
74+
) -> Description {
75+
match (self.m1.matches(actual.clone()), self.m2.matches(actual.clone())) {
76+
(MatcherResult::NoMatch, MatcherResult::Match) => self.m1.explain_match(actual.clone()),
77+
(MatcherResult::Match, MatcherResult::NoMatch) => self.m2.explain_match(actual.clone()),
7278
(_, _) => {
73-
let m1_description = self.m1.explain_match(actual);
79+
let m1_description = self.m1.explain_match(actual.clone());
7480
if m1_description.is_conjunction_description() {
75-
m1_description.nested(self.m2.explain_match(actual))
81+
m1_description.nested(self.m2.explain_match(actual.clone()))
7682
} else {
7783
Description::new()
7884
.bullet_list()
79-
.collect([m1_description, self.m2.explain_match(actual)])
85+
.collect([m1_description, self.m2.explain_match(actual.clone())])
8086
.conjunction_description()
8187
}
8288
}

googletest/src/matchers/container_eq_matcher.rs

+18-7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::description::Description;
1616
use crate::matcher::{Matcher, MatcherResult};
1717
use std::fmt::Debug;
1818
use std::marker::PhantomData;
19+
use std::ops::Deref;
1920

2021
/// Matches a container equal (in the sense of `==`) to `expected`.
2122
///
@@ -116,12 +117,22 @@ where
116117
{
117118
type ActualT = ActualContainerT;
118119

119-
fn matches(&self, actual: &ActualContainerT) -> MatcherResult {
120+
fn matches<ActualRefT: Deref<Target = Self::ActualT>>(
121+
&self,
122+
actual: ActualRefT,
123+
) -> MatcherResult {
120124
(*actual == self.expected).into()
121125
}
122126

123-
fn explain_match(&self, actual: &ActualContainerT) -> Description {
124-
build_explanation(self.get_missing_items(actual), self.get_unexpected_items(actual)).into()
127+
fn explain_match<ActualRefT: Deref<Target = Self::ActualT>>(
128+
&self,
129+
actual: ActualRefT,
130+
) -> Description {
131+
build_explanation(
132+
self.get_missing_items(actual.deref()),
133+
self.get_unexpected_items(actual.deref()),
134+
)
135+
.into()
125136
}
126137

127138
fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -271,15 +282,15 @@ mod tests {
271282
}
272283

273284
#[test]
274-
fn container_eq_matches_owned_vec_of_owned_strings_with_slice_of_string_references()
275-
-> Result<()> {
285+
fn container_eq_matches_owned_vec_of_owned_strings_with_slice_of_string_references(
286+
) -> Result<()> {
276287
let vector = vec!["A string".to_string(), "Another string".to_string()];
277288
verify_that!(vector, container_eq(["A string", "Another string"]))
278289
}
279290

280291
#[test]
281-
fn container_eq_matches_owned_vec_of_owned_strings_with_shorter_slice_of_string_references()
282-
-> Result<()> {
292+
fn container_eq_matches_owned_vec_of_owned_strings_with_shorter_slice_of_string_references(
293+
) -> Result<()> {
283294
let actual = vec!["A string".to_string(), "Another string".to_string()];
284295
let matcher = container_eq(["A string"]);
285296

0 commit comments

Comments
 (0)