Skip to content

Commit 7a16277

Browse files
hovinenbcopybara-github
authored andcommitted
Refactor UnorderedElementsAre to move as much code as possible into other structs.
In order to allow the macro `unordered_elements_are!` to work with `HashMap` and similar classes, it is necessary to support an `IntoIterator` which iterates over a pair of references. The current implementation iterates over single references. It does not appear to be feasible to add an adapter which makes an iterator over a `HashMap` look like something which can be consumed by `UnorderedElementsAre`. The solution is to create a new struct to support `HashMap` and friends. In order to minimise duplication of code, as much code as possible should be moved out of `UnorderedElementsAre` into structs which will be in common with both. Fortunately, the vast majority of the code can be moved this way. PiperOrigin-RevId: 523632451
1 parent 6a2644c commit 7a16277

File tree

1 file changed

+114
-110
lines changed

1 file changed

+114
-110
lines changed

googletest/src/matchers/unordered_elements_are_matcher.rs

Lines changed: 114 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -195,42 +195,6 @@ pub mod internal {
195195
use std::collections::HashSet;
196196
use std::fmt::{Debug, Display};
197197

198-
/// The requirements of the mapping between matchers and actual values by
199-
/// which [`UnorderedElemetnsAre`] is deemed to match its input.
200-
///
201-
/// **For internal use only. API stablility is not guaranteed!**
202-
#[doc(hidden)]
203-
#[derive(Clone, Copy)]
204-
pub enum Requirements {
205-
/// There must be a 1:1 correspondence between the actual values and the
206-
/// matchers.
207-
PerfectMatch,
208-
209-
/// The mapping from matched actual values to their corresponding
210-
/// matchers must be surjective.
211-
Superset,
212-
213-
/// The mapping from matchers to matched actual values must be
214-
/// surjective.
215-
Subset,
216-
}
217-
218-
impl Display for Requirements {
219-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220-
match self {
221-
Requirements::PerfectMatch => {
222-
write!(f, "perfect")
223-
}
224-
Requirements::Superset => {
225-
write!(f, "superset")
226-
}
227-
Requirements::Subset => {
228-
write!(f, "subset")
229-
}
230-
}
231-
}
232-
}
233-
234198
/// This struct is meant to be used only through the
235199
/// `unordered_elements_are![...]` macro.
236200
///
@@ -262,89 +226,30 @@ pub mod internal {
262226
for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
263227
{
264228
fn matches(&self, actual: &ContainerT) -> MatcherResult {
265-
match self.requirements {
266-
Requirements::PerfectMatch => {
267-
let match_matrix = MatchMatrix::generate(actual, &self.elements);
268-
if !match_matrix.find_unmatchable_elements().has_unmatchable_elements()
269-
&& match_matrix.find_best_match().is_full_match()
270-
{
271-
MatcherResult::Matches
272-
} else {
273-
MatcherResult::DoesNotMatch
274-
}
275-
}
276-
Requirements::Superset => {
277-
let match_matrix = MatchMatrix::generate(actual, &self.elements);
278-
if !match_matrix.find_unmatched_expected().has_unmatchable_elements()
279-
&& match_matrix.find_best_match().is_superset_match()
280-
{
281-
MatcherResult::Matches
282-
} else {
283-
MatcherResult::DoesNotMatch
284-
}
285-
}
286-
Requirements::Subset => {
287-
let match_matrix = MatchMatrix::generate(actual, &self.elements);
288-
if !match_matrix.find_unmatched_actual().has_unmatchable_elements()
289-
&& match_matrix.find_best_match().is_subset_match()
290-
{
291-
MatcherResult::Matches
292-
} else {
293-
MatcherResult::DoesNotMatch
294-
}
295-
}
296-
}
229+
let match_matrix = MatchMatrix::generate(actual, &self.elements);
230+
match_matrix.is_match_for(self.requirements).into()
297231
}
298232

299233
fn explain_match(&self, actual: &ContainerT) -> MatchExplanation {
300-
let actual_size = count_elements(actual);
301-
match self.requirements {
302-
Requirements::PerfectMatch => {
303-
if actual_size != N {
304-
return MatchExplanation::create(format!(
305-
"which has size {} (expected {})",
306-
actual_size, N
307-
));
308-
}
309-
}
310-
311-
Requirements::Superset => {
312-
if actual_size < N {
313-
return MatchExplanation::create(format!(
314-
"which has size {} (expected at least {})",
315-
actual_size, N
316-
));
317-
}
318-
}
319-
320-
Requirements::Subset => {
321-
if actual_size > N {
322-
return MatchExplanation::create(format!(
323-
"which has size {} (expected at most {})",
324-
actual_size, N
325-
));
326-
}
327-
}
234+
if let Some(size_mismatch_explanation) =
235+
self.requirements.explain_size_mismatch(actual, N)
236+
{
237+
return size_mismatch_explanation;
328238
}
329239

330240
let match_matrix = MatchMatrix::generate(actual, &self.elements);
331-
let unmatchable_elements = match self.requirements {
332-
Requirements::PerfectMatch => match_matrix.find_unmatchable_elements(),
333-
Requirements::Superset => match_matrix.find_unmatched_expected(),
334-
Requirements::Subset => match_matrix.find_unmatched_actual(),
335-
};
336-
if let Some(unmatchable_explanation) = unmatchable_elements.get_explanation() {
337-
return MatchExplanation::create(unmatchable_explanation);
241+
if let Some(unmatchable_explanation) =
242+
match_matrix.explain_unmatchable(self.requirements)
243+
{
244+
return unmatchable_explanation;
338245
}
339246

340247
let best_match = match_matrix.find_best_match();
341-
if let Some(best_match_explanation) =
342-
best_match.get_explanation(actual, &self.elements, self.requirements)
343-
{
344-
MatchExplanation::create(best_match_explanation)
345-
} else {
346-
MatchExplanation::create("whose elements all match".to_string())
347-
}
248+
MatchExplanation::create(
249+
best_match
250+
.get_explanation(actual, &self.elements, self.requirements)
251+
.unwrap_or("whose elements all match".to_string()),
252+
)
348253
}
349254

350255
fn describe(&self, matcher_result: MatcherResult) -> String {
@@ -361,6 +266,79 @@ pub mod internal {
361266
}
362267
}
363268

269+
/// The requirements of the mapping between matchers and actual values by
270+
/// which [`UnorderedElemetnsAre`] is deemed to match its input.
271+
///
272+
/// **For internal use only. API stablility is not guaranteed!**
273+
#[doc(hidden)]
274+
#[derive(Clone, Copy)]
275+
pub enum Requirements {
276+
/// There must be a 1:1 correspondence between the actual values and the
277+
/// matchers.
278+
PerfectMatch,
279+
280+
/// The mapping from matched actual values to their corresponding
281+
/// matchers must be surjective.
282+
Superset,
283+
284+
/// The mapping from matchers to matched actual values must be
285+
/// surjective.
286+
Subset,
287+
}
288+
289+
impl Requirements {
290+
fn explain_size_mismatch<ContainerT: ?Sized>(
291+
&self,
292+
actual: &ContainerT,
293+
expected_size: usize,
294+
) -> Option<MatchExplanation>
295+
where
296+
for<'b> &'b ContainerT: IntoIterator,
297+
{
298+
let actual_size = count_elements(actual);
299+
match self {
300+
Requirements::PerfectMatch if actual_size != expected_size => {
301+
Some(MatchExplanation::create(format!(
302+
"which has size {} (expected {})",
303+
actual_size, expected_size
304+
)))
305+
}
306+
307+
Requirements::Superset if actual_size < expected_size => {
308+
Some(MatchExplanation::create(format!(
309+
"which has size {} (expected at least {})",
310+
actual_size, expected_size
311+
)))
312+
}
313+
314+
Requirements::Subset if actual_size > expected_size => {
315+
Some(MatchExplanation::create(format!(
316+
"which has size {} (expected at most {})",
317+
actual_size, expected_size
318+
)))
319+
}
320+
321+
_ => None,
322+
}
323+
}
324+
}
325+
326+
impl Display for Requirements {
327+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
328+
match self {
329+
Requirements::PerfectMatch => {
330+
write!(f, "perfect")
331+
}
332+
Requirements::Superset => {
333+
write!(f, "superset")
334+
}
335+
Requirements::Subset => {
336+
write!(f, "subset")
337+
}
338+
}
339+
}
340+
}
341+
364342
/// The bipartite matching graph between actual and expected elements.
365343
struct MatchMatrix<const N: usize>(Vec<[MatcherResult; N]>);
366344

@@ -382,6 +360,32 @@ pub mod internal {
382360
matrix
383361
}
384362

363+
fn is_match_for(&self, requirements: Requirements) -> bool {
364+
match requirements {
365+
Requirements::PerfectMatch => {
366+
!self.find_unmatchable_elements().has_unmatchable_elements()
367+
&& self.find_best_match().is_full_match()
368+
}
369+
Requirements::Superset => {
370+
!self.find_unmatched_expected().has_unmatchable_elements()
371+
&& self.find_best_match().is_superset_match()
372+
}
373+
Requirements::Subset => {
374+
!self.find_unmatched_actual().has_unmatchable_elements()
375+
&& self.find_best_match().is_subset_match()
376+
}
377+
}
378+
}
379+
380+
fn explain_unmatchable(&self, requirements: Requirements) -> Option<MatchExplanation> {
381+
let unmatchable_elements = match requirements {
382+
Requirements::PerfectMatch => self.find_unmatchable_elements(),
383+
Requirements::Superset => self.find_unmatched_expected(),
384+
Requirements::Subset => self.find_unmatched_actual(),
385+
};
386+
unmatchable_elements.get_explanation().map(MatchExplanation::create)
387+
}
388+
385389
// Verifies that each actual matches at least one expected and that
386390
// each expected matches at least one actual.
387391
// This is a necessary condition but not sufficient. But it is faster

0 commit comments

Comments
 (0)