Skip to content

Commit 733063b

Browse files
hovinenbcopybara-github
authored andcommitted
Allow the macro unordered_elements_are! to work with (dereferenced) slices.
This requires eliminating the dependency on HasSize, which does not work with slices, and replacing it with more direct determination of the number of elements in the actual value. This is done by using the size hint when available and counting through the iterator otherwise. PiperOrigin-RevId: 515588261
1 parent b785e6d commit 733063b

File tree

1 file changed

+40
-29
lines changed

1 file changed

+40
-29
lines changed

googletest/src/matchers/unordered_elements_are_matcher.rs

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,8 @@ macro_rules! contains_each {
142142
/// verify_that!(vec![3, 1], is_contained_in![ge(3), ge(3), ge(3)])?; // Fails: no matching
143143
/// ```
144144
///
145-
/// The actual value must be a container implementing [`IntoIterator`] and
146-
/// [`HasSize`][crate::matchers::has_size::HasSize]. This includes all common
147-
/// containers in the Rust standard library.
145+
/// The actual value must be a container implementing [`IntoIterator`]. This
146+
/// includes standard containers, slices (when dereferenced) and arrays.
148147
///
149148
/// This matcher does not support matching directly against an [`Iterator`]. To
150149
/// match against an iterator, use [`Iterator::collect`] to build a [`Vec`].
@@ -191,10 +190,6 @@ pub mod internal {
191190
use googletest::matcher::{MatchExplanation, Matcher, MatcherResult};
192191
#[cfg(not(google3))]
193192
use googletest::matchers::description::Description;
194-
#[cfg(not(google3))]
195-
use googletest::matchers::has_size::HasSize;
196-
#[cfg(google3)]
197-
use has_size::HasSize;
198193
use std::collections::HashSet;
199194
use std::fmt::{Debug, Display};
200195

@@ -259,17 +254,14 @@ pub mod internal {
259254
// one expected element and vice versa.
260255
// 3. `UnorderedElementsAre` verifies that a perfect matching exists using
261256
// Ford-Fulkerson.
262-
impl<'a, T: Debug, ContainerT: Debug + HasSize, const N: usize> Matcher<ContainerT>
257+
impl<'a, T: Debug, ContainerT: Debug + ?Sized, const N: usize> Matcher<ContainerT>
263258
for UnorderedElementsAre<'a, T, N>
264259
where
265260
for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
266261
{
267262
fn matches(&self, actual: &ContainerT) -> MatcherResult {
268263
match self.requirements {
269264
Requirements::PerfectMatch => {
270-
if actual.size() != N {
271-
return MatcherResult::DoesNotMatch;
272-
}
273265
let match_matrix = MatchMatrix::generate(actual, &self.elements);
274266
if !match_matrix.find_unmatchable_elements().has_unmatchable_elements()
275267
&& match_matrix.find_best_match().is_full_match()
@@ -280,9 +272,6 @@ pub mod internal {
280272
}
281273
}
282274
Requirements::Superset => {
283-
if actual.size() < N {
284-
return MatcherResult::DoesNotMatch;
285-
}
286275
let match_matrix = MatchMatrix::generate(actual, &self.elements);
287276
if !match_matrix.find_unmatched_expected().has_unmatchable_elements()
288277
&& match_matrix.find_best_match().is_superset_match()
@@ -293,9 +282,6 @@ pub mod internal {
293282
}
294283
}
295284
Requirements::Subset => {
296-
if actual.size() > N {
297-
return MatcherResult::DoesNotMatch;
298-
}
299285
let match_matrix = MatchMatrix::generate(actual, &self.elements);
300286
if !match_matrix.find_unmatched_actual().has_unmatchable_elements()
301287
&& match_matrix.find_best_match().is_subset_match()
@@ -309,33 +295,31 @@ pub mod internal {
309295
}
310296

311297
fn explain_match(&self, actual: &ContainerT) -> MatchExplanation {
298+
let actual_size = count_elements(actual);
312299
match self.requirements {
313300
Requirements::PerfectMatch => {
314-
if actual.size() != N {
301+
if actual_size != N {
315302
return MatchExplanation::create(format!(
316303
"which has size {} (expected {})",
317-
actual.size(),
318-
N
304+
actual_size, N
319305
));
320306
}
321307
}
322308

323309
Requirements::Superset => {
324-
if actual.size() < N {
310+
if actual_size < N {
325311
return MatchExplanation::create(format!(
326312
"which has size {} (expected at least {})",
327-
actual.size(),
328-
N
313+
actual_size, N
329314
));
330315
}
331316
}
332317

333318
Requirements::Subset => {
334-
if actual.size() > N {
319+
if actual_size > N {
335320
return MatchExplanation::create(format!(
336321
"which has size {} (expected at most {})",
337-
actual.size(),
338-
N
322+
actual_size, N
339323
));
340324
}
341325
}
@@ -379,14 +363,15 @@ pub mod internal {
379363
struct MatchMatrix<const N: usize>(Vec<[MatcherResult; N]>);
380364

381365
impl<const N: usize> MatchMatrix<N> {
382-
fn generate<'a, T: Debug, ContainerT: Debug + HasSize>(
366+
fn generate<'a, T: Debug, ContainerT: Debug + ?Sized>(
383367
actual: &ContainerT,
384368
expected: &[&'a dyn Matcher<T>; N],
385369
) -> Self
386370
where
387371
for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
388372
{
389-
let mut matrix = MatchMatrix(vec![[MatcherResult::DoesNotMatch; N]; actual.size()]);
373+
let mut matrix =
374+
MatchMatrix(vec![[MatcherResult::DoesNotMatch; N]; count_elements(actual)]);
390375
for (actual_idx, actual) in actual.into_iter().enumerate() {
391376
for (expected_idx, expected) in expected.iter().enumerate() {
392377
matrix.0[actual_idx][expected_idx] = expected.matches(actual);
@@ -591,6 +576,25 @@ pub mod internal {
591576
}
592577
}
593578

579+
/// Counts the number of elements in `value`.
580+
///
581+
/// This uses [`Iterator::size_hint`] when that function returns an
582+
/// unambiguous answer, i.e., the upper bound exists and the lower and upper
583+
/// bounds agree. Otherwise it iterates through `value` and counts the
584+
/// elements.
585+
fn count_elements<T, ContainerT: ?Sized>(value: &ContainerT) -> usize
586+
where
587+
for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
588+
{
589+
let iterator = value.into_iter();
590+
if let (lower, Some(higher)) = iterator.size_hint() {
591+
if lower == higher {
592+
return lower;
593+
}
594+
}
595+
iterator.count()
596+
}
597+
594598
/// The list of elements that do not match any element in the corresponding
595599
/// set.
596600
/// These lists are represented as fixed sized bit set to avoid
@@ -707,7 +711,7 @@ pub mod internal {
707711
(0..N).filter(|expected_idx| !matched_expected.contains(expected_idx)).collect()
708712
}
709713

710-
fn get_explanation<'a, T: Debug, ContainerT: Debug>(
714+
fn get_explanation<'a, T: Debug, ContainerT: Debug + ?Sized>(
711715
&self,
712716
actual: &ContainerT,
713717
expected: &[&'a dyn Matcher<T>; N],
@@ -773,6 +777,13 @@ mod tests {
773777
verify_that!(value, unordered_elements_are![eq(1), eq(2), eq(3)])
774778
}
775779

780+
#[google_test]
781+
fn unordered_elements_are_matches_slice() -> Result<()> {
782+
let value = vec![1, 2, 3];
783+
let slice = value.as_slice();
784+
verify_that!(*slice, unordered_elements_are![eq(1), eq(2), eq(3)])
785+
}
786+
776787
#[google_test]
777788
fn unordered_elements_are_matches_vector_with_trailing_comma() -> Result<()> {
778789
let value = vec![1, 2, 3];

0 commit comments

Comments
 (0)