Skip to content

Commit d15f5ad

Browse files
hovinenbcopybara-github
authored andcommitted
Better support the creation of a diff between actual and expected values with the ends_with matcher.
This uses the new `Prefix` mode in `edit_list` to skip the lines preceding the expected suffix. To do this, `StrMatcher` reverses the lines of the actual and expected values before creating the edit list, then restores the correct order from the output. This approach has the side effect that the order in which the actual and expected lines are shown in the diff is the reverse of that used in the `eq` and `starts_with` matcher. It is unlikely that this difference will have any real consequence for the user, so I am leaving it as is. PiperOrigin-RevId: 540859950
1 parent 294678d commit d15f5ad

File tree

2 files changed

+86
-6
lines changed

2 files changed

+86
-6
lines changed

googletest/src/matchers/eq_matcher.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ impl<A: Debug + ?Sized, T: PartialEq<A> + Debug> Matcher for EqMatcher<A, T> {
119119
}
120120
}
121121

122+
/// Returns a string describing how the expected and actual lines differ.
123+
///
124+
/// This is included in a match explanation for [`EqMatcher`] and
125+
/// [`crate::matchers::str_matcher::StrMatcher`].
126+
///
127+
/// If the actual value has at most two lines, or the two differ by more than
128+
/// the maximum edit distance, then this returns the empty string. If the two
129+
/// are equal, it returns a simple statement that they are equal. Otherwise,
130+
/// this constructs a unified diff view of the actual and expected values.
122131
pub(super) fn create_diff(
123132
expected_debug: &str,
124133
actual_debug: &str,
@@ -138,6 +147,37 @@ pub(super) fn create_diff(
138147
}
139148
}
140149

150+
/// Returns a string describing how the expected and actual differ after
151+
/// reversing the lines in each.
152+
///
153+
/// This is similar to [`create_diff`] except that it first reverses the lines
154+
/// in both the expected and actual values, then reverses the constructed edit
155+
/// list. When `diff_mode` is [`edit_distance::Mode::Prefix`], this becomes a
156+
/// diff of the suffix for use by [`ends_with`][crate::matchers::ends_with].
157+
pub(super) fn create_diff_reversed(
158+
expected_debug: &str,
159+
actual_debug: &str,
160+
diff_mode: edit_distance::Mode,
161+
) -> Cow<'static, str> {
162+
if actual_debug.lines().count() < 2 {
163+
// If the actual debug is only one line, then there is no point in doing a
164+
// line-by-line diff.
165+
return "".into();
166+
}
167+
let mut actual_lines_reversed = actual_debug.lines().collect::<Vec<_>>();
168+
let mut expected_lines_reversed = expected_debug.lines().collect::<Vec<_>>();
169+
actual_lines_reversed.reverse();
170+
expected_lines_reversed.reverse();
171+
match edit_distance::edit_list(actual_lines_reversed, expected_lines_reversed, diff_mode) {
172+
edit_distance::Difference::Equal => "No difference found between debug strings.".into(),
173+
edit_distance::Difference::Editable(mut edit_list) => {
174+
edit_list.reverse();
175+
format!("\nDifference:{}", edit_list_summary(&edit_list)).into()
176+
}
177+
edit_distance::Difference::Unrelated => "".into(),
178+
}
179+
}
180+
141181
fn edit_list_summary(edit_list: &[edit_distance::Edit<&str>]) -> String {
142182
let mut summary = String::new();
143183
// Use to collect common line and compress them.

googletest/src/matchers/str_matcher.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::{
1717
matcher_support::edit_distance,
1818
matchers::{
1919
eq_deref_of_matcher::EqDerefOfMatcher,
20-
eq_matcher::{create_diff, EqMatcher},
20+
eq_matcher::{create_diff, create_diff_reversed, EqMatcher},
2121
},
2222
};
2323
use std::borrow::Cow;
@@ -402,7 +402,7 @@ impl MatchMode {
402402
// TODO(b/286515736): Once supported, only MatchMode::Equals should map to
403403
// Mode::Exact.
404404
match self {
405-
MatchMode::StartsWith => edit_distance::Mode::Prefix,
405+
MatchMode::StartsWith | MatchMode::EndsWith => edit_distance::Mode::Prefix,
406406
_ => edit_distance::Mode::Exact,
407407
}
408408
}
@@ -540,10 +540,14 @@ impl Configuration {
540540
return default_explanation;
541541
}
542542

543-
format!(
544-
"{default_explanation}\n{}",
545-
create_diff(expected, actual, self.mode.to_diff_mode())
546-
)
543+
let diff = match self.mode {
544+
MatchMode::Equals | MatchMode::StartsWith | MatchMode::Contains => {
545+
create_diff(expected, actual, self.mode.to_diff_mode())
546+
}
547+
MatchMode::EndsWith => create_diff_reversed(expected, actual, self.mode.to_diff_mode()),
548+
};
549+
550+
format!("{default_explanation}\n{diff}",)
547551
}
548552

549553
fn ignoring_leading_whitespace(self) -> Self {
@@ -1033,6 +1037,7 @@ mod tests {
10331037
result,
10341038
err(displays_as(contains_substring(indoc!(
10351039
"
1040+
Difference:
10361041
First line
10371042
+Second line
10381043
-Second lines
@@ -1042,6 +1047,41 @@ mod tests {
10421047
)
10431048
}
10441049

1050+
#[test]
1051+
fn match_explanation_for_ends_with_ignores_leading_lines_in_actual_string() -> Result<()> {
1052+
let result = verify_that!(
1053+
indoc!(
1054+
"
1055+
First line
1056+
Second line
1057+
Third line
1058+
Fourth line
1059+
"
1060+
),
1061+
ends_with(indoc!(
1062+
"
1063+
Second line
1064+
Third lines
1065+
Fourth line
1066+
"
1067+
))
1068+
);
1069+
1070+
verify_that!(
1071+
result,
1072+
err(displays_as(contains_substring(indoc!(
1073+
"
1074+
Difference:
1075+
<---- remaining lines omitted ---->
1076+
Second line
1077+
-Third lines
1078+
+Third line
1079+
Fourth line
1080+
"
1081+
))))
1082+
)
1083+
}
1084+
10451085
#[test]
10461086
fn match_explanation_for_eq_does_not_ignore_trailing_lines_in_actual_string() -> Result<()> {
10471087
let result = verify_that!(

0 commit comments

Comments
 (0)