diff --git a/tests/util/server/src/assertions.rs b/tests/util/server/src/assertions.rs
index b9aba9354a..048115d567 100644
--- a/tests/util/server/src/assertions.rs
+++ b/tests/util/server/src/assertions.rs
@@ -54,8 +54,7 @@ macro_rules! assert_not_contains {
#[track_caller]
pub fn assert_wildcard_match(actual: &str, expected: &str) {
- if !expected.contains("[WILDCARD]") && !expected.contains("[UNORDERED_START]")
- {
+ if !expected.contains("[WILD") && !expected.contains("[UNORDERED_START]") {
pretty_assertions::assert_eq!(actual, expected);
} else {
match crate::wildcard_match_detailed(expected, actual) {
diff --git a/tests/util/server/src/lib.rs b/tests/util/server/src/lib.rs
index debe2b6980..7638c4efeb 100644
--- a/tests/util/server/src/lib.rs
+++ b/tests/util/server/src/lib.rs
@@ -674,21 +674,43 @@ pub fn wildcard_match_detailed(
let parts = parse_wildcard_pattern_text(&pattern).unwrap();
let mut was_last_wildcard = false;
+ let mut was_last_wildline = false;
for (i, part) in parts.iter().enumerate() {
match part {
WildcardPatternPart::Wildcard => {
output_lines.push("".to_string());
}
+ WildcardPatternPart::Wildline => {
+ output_lines.push("".to_string());
+ }
+ WildcardPatternPart::Wildnum(times) => {
+ if current_text.len() < *times {
+ output_lines
+ .push(format!("==== HAD MISSING WILDCHARS({}) ====", times));
+ output_lines.push(colors::red(annotate_whitespace(current_text)));
+ return WildcardMatchResult::Fail(output_lines.join("\n"));
+ }
+ output_lines.push(format!("", times));
+ current_text = ¤t_text[*times..];
+ }
WildcardPatternPart::Text(search_text) => {
let is_last = i + 1 == parts.len();
let search_index = if is_last && was_last_wildcard {
// search from the end of the file
current_text.rfind(search_text)
+ } else if was_last_wildline {
+ if is_last {
+ find_last_text_on_line(search_text, current_text)
+ } else {
+ find_first_text_on_line(search_text, current_text)
+ }
} else {
current_text.find(search_text)
};
match search_index {
- Some(found_index) if was_last_wildcard || found_index == 0 => {
+ Some(found_index)
+ if was_last_wildcard || was_last_wildline || found_index == 0 =>
+ {
output_lines.push(format!(
"{}",
colors::gray(annotate_whitespace(search_text))
@@ -707,11 +729,12 @@ pub fn wildcard_match_detailed(
return WildcardMatchResult::Fail(output_lines.join("\n"));
}
None => {
+ let was_wildcard_or_line = was_last_wildcard || was_last_wildline;
let mut max_found_index = 0;
for (index, _) in search_text.char_indices() {
let sub_string = &search_text[..index];
if let Some(found_index) = current_text.find(sub_string) {
- if was_last_wildcard || found_index == 0 {
+ if was_wildcard_or_line || found_index == 0 {
max_found_index = index;
} else {
break;
@@ -720,7 +743,7 @@ pub fn wildcard_match_detailed(
break;
}
}
- if !was_last_wildcard && max_found_index > 0 {
+ if !was_wildcard_or_line && max_found_index > 0 {
output_lines.push(format!(
"{}",
colors::gray(annotate_whitespace(
@@ -731,13 +754,13 @@ pub fn wildcard_match_detailed(
output_lines
.push("==== COULD NOT FIND SEARCH TEXT ====".to_string());
output_lines.push(colors::green(annotate_whitespace(
- if was_last_wildcard {
+ if was_wildcard_or_line {
search_text
} else {
&search_text[max_found_index..]
},
)));
- if was_last_wildcard && max_found_index > 0 {
+ if was_wildcard_or_line && max_found_index > 0 {
output_lines.push(format!(
"==== MAX FOUND ====\n{}",
colors::red(annotate_whitespace(
@@ -766,6 +789,7 @@ pub fn wildcard_match_detailed(
}
WildcardPatternPart::UnorderedLines(expected_lines) => {
assert!(!was_last_wildcard, "unsupported");
+ assert!(!was_last_wildline, "unsupported");
let mut actual_lines = Vec::with_capacity(expected_lines.len());
for _ in 0..expected_lines.len() {
match current_text.find('\n') {
@@ -823,9 +847,10 @@ pub fn wildcard_match_detailed(
}
}
was_last_wildcard = matches!(part, WildcardPatternPart::Wildcard);
+ was_last_wildline = matches!(part, WildcardPatternPart::Wildline);
}
- if was_last_wildcard || current_text.is_empty() {
+ if was_last_wildcard || was_last_wildline || current_text.is_empty() {
WildcardMatchResult::Success
} else {
output_lines.push("==== HAD TEXT AT END OF FILE ====".to_string());
@@ -837,6 +862,8 @@ pub fn wildcard_match_detailed(
#[derive(Debug)]
enum WildcardPatternPart<'a> {
Wildcard,
+ Wildline,
+ Wildnum(usize),
Text(&'a str),
UnorderedLines(Vec<&'a str>),
}
@@ -860,6 +887,8 @@ fn parse_wildcard_pattern_text(
enum InnerPart<'a> {
Wildcard,
+ Wildline,
+ Wildnum(usize),
UnorderedLines(Vec<&'a str>),
Char,
}
@@ -872,9 +901,29 @@ fn parse_wildcard_pattern_text(
impl<'a> Parser<'a> {
fn parse(mut self) -> ParseResult<'a, Vec>> {
+ fn parse_num(input: &str) -> ParseResult {
+ let num_char_count =
+ input.chars().take_while(|c| c.is_ascii_digit()).count();
+ if num_char_count == 0 {
+ return ParseError::backtrace();
+ }
+ let (char_text, input) = input.split_at(num_char_count);
+ let value = str::parse::(char_text).unwrap();
+ Ok((input, value))
+ }
+
+ fn parse_wild_num(input: &str) -> ParseResult {
+ let (input, _) = tag("[WILDCHARS(")(input)?;
+ let (input, times) = parse_num(input)?;
+ let (input, _) = tag(")]")(input)?;
+ ParseResult::Ok((input, times))
+ }
+
while !self.current_input.is_empty() {
- let (next_input, inner_part) = or3(
+ let (next_input, inner_part) = or5(
map(tag("[WILDCARD]"), |_| InnerPart::Wildcard),
+ map(tag("[WILDLINE]"), |_| InnerPart::Wildline),
+ map(parse_wild_num, InnerPart::Wildnum),
map(parse_unordered_lines, |lines| {
InnerPart::UnorderedLines(lines)
}),
@@ -885,6 +934,14 @@ fn parse_wildcard_pattern_text(
self.queue_previous_text(next_input);
self.parts.push(WildcardPatternPart::Wildcard);
}
+ InnerPart::Wildline => {
+ self.queue_previous_text(next_input);
+ self.parts.push(WildcardPatternPart::Wildline);
+ }
+ InnerPart::Wildnum(times) => {
+ self.queue_previous_text(next_input);
+ self.parts.push(WildcardPatternPart::Wildnum(times));
+ }
InnerPart::UnorderedLines(expected_lines) => {
self.queue_previous_text(next_input);
self
@@ -923,6 +980,38 @@ fn parse_wildcard_pattern_text(
})(text)
}
+fn find_first_text_on_line(
+ search_text: &str,
+ current_text: &str,
+) -> Option {
+ let end_search_pos = current_text.find('\n').unwrap_or(current_text.len());
+ let found_pos = current_text.find(search_text)?;
+ if found_pos <= end_search_pos {
+ Some(found_pos)
+ } else {
+ None
+ }
+}
+
+fn find_last_text_on_line(
+ search_text: &str,
+ current_text: &str,
+) -> Option {
+ let end_search_pos = current_text.find('\n').unwrap_or(current_text.len());
+ let mut best_match = None;
+ let mut search_pos = 0;
+ while let Some(new_pos) = current_text[search_pos..].find(search_text) {
+ search_pos += new_pos;
+ if search_pos <= end_search_pos {
+ best_match = Some(search_pos);
+ } else {
+ break;
+ }
+ search_pos += 1;
+ }
+ best_match
+}
+
pub fn with_pty(deno_args: &[&str], action: impl FnMut(Pty)) {
let context = TestContextBuilder::default().use_temp_cwd().build();
context.new_command().args_vec(deno_args).with_pty(action);
@@ -1318,6 +1407,19 @@ grault",
multiline_pattern,
&multi_line_builder("garply", None),
));
+
+ // wildline
+ assert!(wildcard_match("foo[WILDLINE]baz", "foobarbaz"));
+ assert!(wildcard_match("foo[WILDLINE]bar", "foobarbar"));
+ assert!(!wildcard_match("foo[WILDLINE]baz", "fooba\nrbaz"));
+ assert!(wildcard_match("foo[WILDLINE]", "foobar"));
+
+ // wildnum
+ assert!(wildcard_match("foo[WILDCHARS(3)]baz", "foobarbaz"));
+ assert!(!wildcard_match("foo[WILDCHARS(4)]baz", "foobarbaz"));
+ assert!(!wildcard_match("foo[WILDCHARS(2)]baz", "foobarbaz"));
+ assert!(!wildcard_match("foo[WILDCHARS(1)]baz", "foobarbaz"));
+ assert!(!wildcard_match("foo[WILDCHARS(20)]baz", "foobarbaz"));
}
#[test]
@@ -1352,4 +1454,26 @@ grault",
assert_eq!(size, Some(120380 * 1024));
}
+
+ #[test]
+ fn test_find_first_text_on_line() {
+ let text = "foo\nbar\nbaz";
+ assert_eq!(find_first_text_on_line("foo", text), Some(0));
+ assert_eq!(find_first_text_on_line("oo", text), Some(1));
+ assert_eq!(find_first_text_on_line("o", text), Some(1));
+ assert_eq!(find_first_text_on_line("o\nbar", text), Some(2));
+ assert_eq!(find_first_text_on_line("f", text), Some(0));
+ assert_eq!(find_first_text_on_line("bar", text), None);
+ }
+
+ #[test]
+ fn test_find_last_text_on_line() {
+ let text = "foo\nbar\nbaz";
+ assert_eq!(find_last_text_on_line("foo", text), Some(0));
+ assert_eq!(find_last_text_on_line("oo", text), Some(1));
+ assert_eq!(find_last_text_on_line("o", text), Some(2));
+ assert_eq!(find_last_text_on_line("o\nbar", text), Some(2));
+ assert_eq!(find_last_text_on_line("f", text), Some(0));
+ assert_eq!(find_last_text_on_line("bar", text), None);
+ }
}