|
12 | 12 | // See the License for the specific language governing permissions and
|
13 | 13 | // limitations under the License.
|
14 | 14 |
|
15 |
| -use crate::matcher::{Matcher, MatcherResult}; |
16 |
| -use crate::matchers::{eq_deref_of_matcher::EqDerefOfMatcher, eq_matcher::EqMatcher}; |
| 15 | +use crate::{ |
| 16 | + matcher::{Matcher, MatcherResult}, |
| 17 | + matcher_support::edit_distance, |
| 18 | + matchers::{ |
| 19 | + eq_deref_of_matcher::EqDerefOfMatcher, |
| 20 | + eq_matcher::{create_diff, EqMatcher}, |
| 21 | + }, |
| 22 | +}; |
17 | 23 | use std::borrow::Cow;
|
18 | 24 | use std::fmt::Debug;
|
19 | 25 | use std::marker::PhantomData;
|
@@ -300,6 +306,10 @@ where
|
300 | 306 | fn describe(&self, matcher_result: MatcherResult) -> String {
|
301 | 307 | self.configuration.describe(matcher_result, self.expected.deref())
|
302 | 308 | }
|
| 309 | + |
| 310 | + fn explain_match(&self, actual: &ActualT) -> String { |
| 311 | + self.configuration.explain_match(self.expected.deref(), actual.as_ref()) |
| 312 | + } |
303 | 313 | }
|
304 | 314 |
|
305 | 315 | impl<ActualT: ?Sized, ExpectedT, MatcherT: Into<StrMatcher<ActualT, ExpectedT>>>
|
@@ -485,6 +495,55 @@ impl Configuration {
|
485 | 495 | format!("{match_mode_description} {expected:?}{extra}")
|
486 | 496 | }
|
487 | 497 |
|
| 498 | + fn explain_match(&self, expected: &str, actual: &str) -> String { |
| 499 | + let default_explanation = format!( |
| 500 | + "which {}", |
| 501 | + self.describe(self.do_strings_match(expected, actual).into(), expected) |
| 502 | + ); |
| 503 | + if !expected.contains('\n') || !actual.contains('\n') { |
| 504 | + return default_explanation; |
| 505 | + } |
| 506 | + |
| 507 | + if self.ignore_leading_whitespace { |
| 508 | + // TODO - b/283448414 : Support StrMatcher with ignore_leading_whitespace. |
| 509 | + return default_explanation; |
| 510 | + } |
| 511 | + |
| 512 | + if self.ignore_trailing_whitespace { |
| 513 | + // TODO - b/283448414 : Support StrMatcher with ignore_trailing_whitespace. |
| 514 | + return default_explanation; |
| 515 | + } |
| 516 | + |
| 517 | + if self.times.is_some() { |
| 518 | + // TODO - b/283448414 : Support StrMatcher with times. |
| 519 | + return default_explanation; |
| 520 | + } |
| 521 | + if matches!(self.case_policy, CasePolicy::IgnoreAscii) { |
| 522 | + // TODO - b/283448414 : Support StrMatcher with ignore ascii case policy. |
| 523 | + return default_explanation; |
| 524 | + } |
| 525 | + if self.do_strings_match(expected, actual) { |
| 526 | + // TODO - b/283448414 : Consider supporting debug difference if the |
| 527 | + // strings match. This can be useful when a small contains is found |
| 528 | + // in a long string. |
| 529 | + return default_explanation; |
| 530 | + } |
| 531 | + |
| 532 | + format!( |
| 533 | + "{default_explanation}\n{}", |
| 534 | + create_diff(expected, actual, self.get_edit_distance_mode()) |
| 535 | + ) |
| 536 | + } |
| 537 | + |
| 538 | + fn get_edit_distance_mode(&self) -> edit_distance::Mode { |
| 539 | + match self.mode { |
| 540 | + MatchMode::Equals => edit_distance::Mode::FullMatch, |
| 541 | + MatchMode::Contains => edit_distance::Mode::Contains, |
| 542 | + MatchMode::StartsWith => edit_distance::Mode::StartsWith, |
| 543 | + MatchMode::EndsWith => edit_distance::Mode::EndsWith, |
| 544 | + } |
| 545 | + } |
| 546 | + |
488 | 547 | fn ignoring_leading_whitespace(self) -> Self {
|
489 | 548 | Self { ignore_leading_whitespace: true, ..self }
|
490 | 549 | }
|
@@ -523,6 +582,7 @@ mod tests {
|
523 | 582 | use super::{contains_substring, ends_with, starts_with, StrMatcher, StrMatcherConfigurator};
|
524 | 583 | use crate::matcher::{Matcher, MatcherResult};
|
525 | 584 | use crate::prelude::*;
|
| 585 | + use indoc::indoc; |
526 | 586 |
|
527 | 587 | #[test]
|
528 | 588 | fn matches_string_reference_with_equal_string_reference() -> Result<()> {
|
@@ -881,4 +941,67 @@ mod tests {
|
881 | 941 | eq("does not end with \"A string\"")
|
882 | 942 | )
|
883 | 943 | }
|
| 944 | + |
| 945 | + #[test] |
| 946 | + fn match_explanation_contains_diff_of_strings_if_more_than_one_line() -> Result<()> { |
| 947 | + let result = verify_that!( |
| 948 | + indoc!( |
| 949 | + " |
| 950 | + First line |
| 951 | + Second line |
| 952 | + Third line |
| 953 | + " |
| 954 | + ), |
| 955 | + starts_with(indoc!( |
| 956 | + " |
| 957 | + First line |
| 958 | + Second lines |
| 959 | + Third line |
| 960 | + " |
| 961 | + )) |
| 962 | + ); |
| 963 | + |
| 964 | + verify_that!( |
| 965 | + result, |
| 966 | + err(displays_as(contains_substring(indoc!( |
| 967 | + " |
| 968 | + First line |
| 969 | + +Second line |
| 970 | + -Second lines |
| 971 | + Third line |
| 972 | + " |
| 973 | + )))) |
| 974 | + ) |
| 975 | + } |
| 976 | + |
| 977 | + #[test] |
| 978 | + fn match_explanation_does_not_show_diff_if_actual_value_is_single_line() -> Result<()> { |
| 979 | + let result = verify_that!( |
| 980 | + "First line", |
| 981 | + starts_with(indoc!( |
| 982 | + " |
| 983 | + Second line |
| 984 | + Third line |
| 985 | + " |
| 986 | + )) |
| 987 | + ); |
| 988 | + |
| 989 | + verify_that!(result, err(displays_as(not(contains_substring("Difference:"))))) |
| 990 | + } |
| 991 | + |
| 992 | + #[test] |
| 993 | + fn match_explanation_does_not_show_diff_if_expected_value_is_single_line() -> Result<()> { |
| 994 | + let result = verify_that!( |
| 995 | + indoc!( |
| 996 | + " |
| 997 | + First line |
| 998 | + Second line |
| 999 | + Third line |
| 1000 | + " |
| 1001 | + ), |
| 1002 | + starts_with("Second line") |
| 1003 | + ); |
| 1004 | + |
| 1005 | + verify_that!(result, err(displays_as(not(contains_substring("Difference:"))))) |
| 1006 | + } |
884 | 1007 | }
|
0 commit comments