diff --git a/Cargo.toml b/Cargo.toml index d338463..fae4be6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,4 @@ exclude = [".github/", "bors.toml", "rustfmt.toml"] [dependencies] once_cell = "1" dissimilar = "1" +regex = "1.5.4" diff --git a/src/lib.rs b/src/lib.rs index dd877be..5175a9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -145,6 +145,7 @@ use std::{ }; use once_cell::sync::Lazy; +use regex::Regex; const HELP: &str = " You can update all `expect![[]]` tests by running: @@ -274,13 +275,30 @@ impl Expect { line_start += line.len(); } let (literal_start, line_indent) = target_line.unwrap(); - let literal_length = - file[literal_start..].find("]]").expect("Couldn't find matching `]]` for `expect![[`."); - let literal_range = literal_start..literal_start + literal_length; + + let literal_end = locate_end(&file[literal_start..]) + .expect("Couldn't find matching `]]` for `expect![[`."); + + let literal_range = literal_start..literal_start + literal_end; Location { line_indent, literal_range } } } +/// Returns the index of the end of the literal passed to `expect` in the input string. +fn locate_end(expect_invocation_to_eof: &str) -> Option { + static TRAILER: Lazy = Lazy::new(|| Regex::new(r##""#*(\s*]])"##).unwrap()); + + // First, check for `expect![[]]` (no literal). + let mut chars = expect_invocation_to_eof.chars().skip_while(|c| c.is_whitespace()); + if (']', ']') == (chars.next()?, chars.next()?) { + return Some(0); + } + + let trailer = TRAILER.captures(expect_invocation_to_eof)?; + let literal_end = trailer.get(0).unwrap().end() - trailer[1].len(); + Some(literal_end) +} + impl ExpectFile { /// Checks if file contents is equal to `actual`. pub fn assert_eq(&self, actual: &str) { @@ -661,4 +679,28 @@ line1 "#]], ); } + + #[test] + fn test_locate() { + macro_rules! check_locate { + ($( [[$s:literal]] ),* $(,)?) => {$({ + let lit = stringify!($s); + let with_trailer = format!("{} \t]]\n", lit); + assert_eq!(locate_end(&with_trailer), Some(lit.len())); + })*}; + } + + // Check that we handle string literals containing "]]" correctly. + check_locate!( + [[r#"{ arr: [[1, 2], [3, 4]], other: "foo" } "#]], + + // We don't actually parse string literals, so these cases fail. + // [["]]"]], + // [["\"]]"]], + // [[r#""]]"#]], + ); + + // Check `expect![[ ]]` as well. + assert_eq!(locate_end(" ]]"), Some(0)); + } }