mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 16:49:18 -05:00
chore(tests): ability to pattern match unordered lines (#20488)
This adds the ability to pattern match unordered lines. For example, the downloading messages may appear in any order ``` [UNORDERED_START] Download https://localhost:4546/a.ts Download https://localhost:4546/b.ts [UNORDERED_END] Hello! ``` Additionally, I've made the pattern matching slightly more strict and the output better.
This commit is contained in:
parent
851f795001
commit
54890ee98b
43 changed files with 405 additions and 108 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -5455,6 +5455,7 @@ dependencies = [
|
|||
"lazy-regex",
|
||||
"libc",
|
||||
"lsp-types",
|
||||
"monch",
|
||||
"nix 0.26.2",
|
||||
"once_cell",
|
||||
"os_pipe",
|
||||
|
@ -5471,6 +5472,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"tar",
|
||||
"tempfile",
|
||||
"termcolor",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"url",
|
||||
|
|
|
@ -105,6 +105,7 @@ libc = "0.2.126"
|
|||
log = "=0.4.20"
|
||||
lsp-types = "=0.93.2" # used by tower-lsp and "proposed" feature is unstable in patch releases
|
||||
memmem = "0.1.1"
|
||||
monch = "=0.4.3"
|
||||
notify = "=5.0.0"
|
||||
num-bigint = { version = "0.4", features = ["rand"] }
|
||||
once_cell = "1.17.1"
|
||||
|
|
|
@ -93,7 +93,7 @@ lazy-regex.workspace = true
|
|||
libc.workspace = true
|
||||
log = { workspace = true, features = ["serde"] }
|
||||
lsp-types.workspace = true
|
||||
monch = "=0.4.3"
|
||||
monch.workspace = true
|
||||
notify.workspace = true
|
||||
once_cell.workspace = true
|
||||
os_pipe.workspace = true
|
||||
|
|
|
@ -870,7 +870,7 @@ itest!(lock_write_fetch {
|
|||
|
||||
itest!(lock_check_ok {
|
||||
args:
|
||||
"run --lock=run/lock_check_ok.json http://127.0.0.1:4545/run/003_relative_import.ts",
|
||||
"run --quiet --lock=run/lock_check_ok.json http://127.0.0.1:4545/run/003_relative_import.ts",
|
||||
output: "run/003_relative_import.ts.out",
|
||||
http_server: true,
|
||||
});
|
||||
|
@ -917,7 +917,7 @@ itest!(lock_flag_overrides_config_file_lock_path {
|
|||
|
||||
itest!(lock_v2_check_ok {
|
||||
args:
|
||||
"run --lock=run/lock_v2_check_ok.json http://127.0.0.1:4545/run/003_relative_import.ts",
|
||||
"run --quiet --lock=run/lock_v2_check_ok.json http://127.0.0.1:4545/run/003_relative_import.ts",
|
||||
output: "run/003_relative_import.ts.out",
|
||||
http_server: true,
|
||||
});
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[WILDCARD]
|
||||
error: Uncaught AggregateError
|
||||
Error: bar <ref *1>
|
||||
at file:///[WILDCARD]/error_cause_recursive_aggregate.ts:2:13
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[WILDCARD]
|
||||
error: Uncaught Error: baz
|
||||
const baz = new Error("baz", { cause: bar });
|
||||
^
|
||||
|
@ -8,4 +7,3 @@ Caused by: Error: bar <ref *1>
|
|||
Caused by: Error: foo
|
||||
at file:///[WILDCARD]/error_cause_recursive_tail.ts:1:13
|
||||
Caused by: [Circular *1]
|
||||
[WILDCARD]
|
||||
|
|
1
cli/tests/testdata/eval/dyn_import_eval.out
vendored
1
cli/tests/testdata/eval/dyn_import_eval.out
vendored
|
@ -1,2 +1 @@
|
|||
[WILDCARD]
|
||||
[Module: null prototype] { isMod4: true }
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[WILDCARD]
|
||||
error: Uncaught (in promise) TypeError: Expected a "JavaScriptOrWasm" module but loaded a "JSON" module.
|
||||
const data = await import("./data.json");
|
||||
^
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[WILDCARD]
|
||||
error: Expected a JavaScript or TypeScript module, but identified a Json module. Consider importing Json modules with an import attribute with the type of "json".
|
||||
Specifier: [WILDCARD]/data.json
|
||||
at [WILDCARD]static_error.ts:1:18
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
[WILDCARD]
|
||||
{ a: "b", c: { d: 10 } }
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
[WILDCARD]
|
||||
{ a: "b", c: { d: 10 } }
|
||||
{ a: "b", c: { d: 10 } }
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[WILDCARD]
|
||||
local: [WILDCARD]031_info_ts_error.ts
|
||||
type: TypeScript
|
||||
dependencies: 0 unique
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[WILDCARD]
|
||||
local: [WILDCARD]test.ts
|
||||
type: TypeScript
|
||||
dependencies: 7 unique
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
[WILDCARD]
|
||||
Hello
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[WILDCARD]
|
||||
error: Expected a JavaScript or TypeScript module, but identified a Json module. Consider importing Json modules with an import attribute with the type of "json".
|
||||
Specifier: [WILDCARD]/subdir/config.json
|
||||
[WILDCARD]
|
3
cli/tests/testdata/run/070_location.ts.out
vendored
3
cli/tests/testdata/run/070_location.ts.out
vendored
|
@ -1,4 +1,4 @@
|
|||
[WILDCARD][class Location]
|
||||
[class Location]
|
||||
Object [Location] {}
|
||||
Location {
|
||||
hash: "#bat",
|
||||
|
@ -13,4 +13,3 @@ Location {
|
|||
}
|
||||
NotSupportedError: Cannot set "location".
|
||||
NotSupportedError: Cannot set "location.hostname".
|
||||
[WILDCARD]
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
[WILDCARD][class Location]
|
||||
[class Location]
|
||||
Object [Location] {}
|
||||
undefined
|
||||
/bar
|
||||
[WILDCARD]
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
[WILDCARD]
|
||||
https://baz/qux
|
||||
[WILDCARD]
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[WILDCARD]
|
||||
error: Uncaught (in promise) TypeError: Relative import path "unmapped" not prefixed with / or ./ or ../ and not in import map from "file://[WILDCARD]/092_import_map_unmapped_bare_specifier.ts"
|
||||
at file://[WILDCARD]/092_import_map_unmapped_bare_specifier.ts:1:14
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[WILDCARD]error: Uncaught Error: bad
|
||||
[WILDCARD]
|
||||
error: Uncaught Error: bad
|
||||
throw Error("bad");
|
||||
^
|
||||
at foo (http://localhost:4545/run/error_001.ts:2:9)
|
||||
at bar (http://localhost:4545/run/error_001.ts:6:3)
|
||||
at http://localhost:4545/run/error_001.ts:9:1
|
||||
[WILDCARD]
|
||||
|
|
2
cli/tests/testdata/run/error_cause.ts.out
vendored
2
cli/tests/testdata/run/error_cause.ts.out
vendored
|
@ -1,4 +1,3 @@
|
|||
[WILDCARD]
|
||||
error: Uncaught Error: foo
|
||||
throw new Error("foo", { cause: new Error("bar", { cause: "deno" as any }) });
|
||||
^
|
||||
|
@ -12,4 +11,3 @@ Caused by: Error: bar
|
|||
at c (file:///[WILDCARD]/error_cause.ts:11:3)
|
||||
at file:///[WILDCARD]/error_cause.ts:14:1
|
||||
Caused by: "deno"
|
||||
[WILDCARD]
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[WILDCARD]
|
||||
error: Uncaught Error: bar <ref *1>
|
||||
const y = new Error("bar", { cause: x });
|
||||
^
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
[WILDCARD]
|
||||
error: Module not found "file://[WILDCARD]/does_not_exist.js".
|
||||
at file:///[WILDCARD]/error_missing_module_named_import.ts:[WILDCARD]
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
[WILDCARD]
|
||||
[Function (anonymous)]
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
[WILDCARD]error: Uncaught (in promise) Error: Hello 2
|
||||
error: Uncaught (in promise) Error: Hello 2
|
||||
throw new Error(`Hello ${A.C}`);
|
||||
^
|
||||
at a (blob:null/[WILDCARD]:8:10)
|
||||
at file:///[WILDCARD]/import_blob_url_error_stack.ts:13:1
|
||||
[WILDCARD]
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
[WILDCARD]error: Uncaught Error: Hello 2
|
||||
error: Uncaught Error: Hello 2
|
||||
throw new Error(`Hello ${A.C}`);
|
||||
^
|
||||
at a (data:application/typescript;base64,ZW51bSBBIHsKICBBLAog......JHtBLkN9YCk7CiB9CiA=:8:10)
|
||||
at file:///[WILDCARD]/import_data_url_error_stack.ts:3:1
|
||||
[WILDCARD]
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
[WILDCARD]
|
||||
Hello
|
||||
|
|
1
cli/tests/testdata/run/import_type.ts.out
vendored
1
cli/tests/testdata/run/import_type.ts.out
vendored
|
@ -1,2 +1 @@
|
|||
[WILDCARD]
|
||||
B { a: "a" }
|
||||
|
|
1
cli/tests/testdata/run/issue13562.ts.out
vendored
1
cli/tests/testdata/run/issue13562.ts.out
vendored
|
@ -1,2 +1 @@
|
|||
[WILDCARD]
|
||||
Hello
|
||||
|
|
1
cli/tests/testdata/run/mts_dmts_mjs.out
vendored
1
cli/tests/testdata/run/mts_dmts_mjs.out
vendored
|
@ -1,2 +1 @@
|
|||
[WILDCARD]
|
||||
a
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
[WILDCARD]
|
||||
false
|
||||
true
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
[WILDCARD]
|
||||
x
|
|
@ -1,3 +1,2 @@
|
|||
[WILDCARD]
|
||||
Storage {[WILDCARD]
|
||||
Storage { length: 1, hello: "deno" }
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
[WILDCARD]
|
||||
5
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
[WILDCARD]
|
||||
Uint8Array(1) [ 49 ]
|
||||
Uint8Array(1) [ 50 ]
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[WILDCARD]
|
||||
!
|
||||
.
|
||||
!
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[WILDCARD]
|
||||
,
|
||||
.
|
||||
.
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
[WILDCARD]ReadableStream { locked: false }
|
||||
[WILDCARD]
|
||||
ReadableStream { locked: false }
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
[WILDCARD]
|
||||
error: Uncaught (in worker "") Requires read access to "[WILDCARD]worker_types.ts", run again with the --allow-read flag
|
||||
[WILDCARD]
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
[WILDCARD]
|
||||
error: Uncaught (in worker "") Requires net access to "localhost:4545", run again with the --allow-net flag
|
||||
[WILDCARD]
|
||||
|
|
|
@ -26,6 +26,7 @@ hyper = { workspace = true, features = ["server", "http1", "http2", "runtime"] }
|
|||
lazy-regex.workspace = true
|
||||
libc.workspace = true
|
||||
lsp-types.workspace = true
|
||||
monch.workspace = true
|
||||
nix.workspace = true
|
||||
once_cell.workspace = true
|
||||
os_pipe.workspace = true
|
||||
|
@ -41,6 +42,7 @@ serde.workspace = true
|
|||
serde_json.workspace = true
|
||||
tar.workspace = true
|
||||
tempfile.workspace = true
|
||||
termcolor.workspace = true
|
||||
tokio.workspace = true
|
||||
tokio-rustls.workspace = true
|
||||
url.workspace = true
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::colors;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_starts_with {
|
||||
($string:expr, $($test:expr),+) => {
|
||||
|
@ -52,11 +54,40 @@ macro_rules! assert_not_contains {
|
|||
|
||||
#[track_caller]
|
||||
pub fn assert_wildcard_match(actual: &str, expected: &str) {
|
||||
if !expected.contains("[WILDCARD]") {
|
||||
if !expected.contains("[WILDCARD]") && !expected.contains("[IGNORE_START]") {
|
||||
pretty_assertions::assert_eq!(actual, expected);
|
||||
} else if !crate::wildcard_match(expected, actual) {
|
||||
println!("OUTPUT START\n{actual}\nOUTPUT END");
|
||||
println!("EXPECTED START\n{expected}\nEXPECTED END");
|
||||
panic!("pattern match failed");
|
||||
} else {
|
||||
match crate::wildcard_match_detailed(expected, actual) {
|
||||
crate::WildcardMatchResult::Success => {
|
||||
// ignore
|
||||
}
|
||||
crate::WildcardMatchResult::Fail(debug_output) => {
|
||||
println!(
|
||||
"{}{}{}",
|
||||
colors::bold("-- "),
|
||||
colors::bold_red("OUTPUT"),
|
||||
colors::bold(" START --"),
|
||||
);
|
||||
println!("{}", actual);
|
||||
println!("{}", colors::bold("-- OUTPUT END --"));
|
||||
println!(
|
||||
"{}{}{}",
|
||||
colors::bold("-- "),
|
||||
colors::bold_green("EXPECTED"),
|
||||
colors::bold(" START --"),
|
||||
);
|
||||
println!("{}", expected);
|
||||
println!("{}", colors::bold("-- EXPECTED END --"));
|
||||
println!(
|
||||
"{}{}{}",
|
||||
colors::bold("-- "),
|
||||
colors::bold_blue("DEBUG"),
|
||||
colors::bold(" START --"),
|
||||
);
|
||||
println!("{debug_output}");
|
||||
println!("{}", colors::bold("-- DEBUG END --"));
|
||||
panic!("pattern match failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2410,57 +2410,266 @@ impl<'a> CheckOutputIntegrationTest<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn wildcard_match(pattern: &str, s: &str) -> bool {
|
||||
pattern_match(pattern, s, "[WILDCARD]")
|
||||
pub fn wildcard_match(pattern: &str, text: &str) -> bool {
|
||||
match wildcard_match_detailed(pattern, text) {
|
||||
WildcardMatchResult::Success => true,
|
||||
WildcardMatchResult::Fail(debug_output) => {
|
||||
eprintln!("{}", debug_output);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pattern_match(pattern: &str, s: &str, wildcard: &str) -> bool {
|
||||
pub enum WildcardMatchResult {
|
||||
Success,
|
||||
Fail(String),
|
||||
}
|
||||
|
||||
pub fn wildcard_match_detailed(
|
||||
pattern: &str,
|
||||
text: &str,
|
||||
) -> WildcardMatchResult {
|
||||
fn annotate_whitespace(text: &str) -> String {
|
||||
text.replace('\t', "\u{2192}").replace(' ', "\u{00B7}")
|
||||
}
|
||||
|
||||
// Normalize line endings
|
||||
let mut s = s.replace("\r\n", "\n");
|
||||
let original_text = text.replace("\r\n", "\n");
|
||||
let mut current_text = original_text.as_str();
|
||||
let pattern = pattern.replace("\r\n", "\n");
|
||||
let mut output_lines = Vec::new();
|
||||
|
||||
if pattern == wildcard {
|
||||
return true;
|
||||
}
|
||||
|
||||
let parts = pattern.split(wildcard).collect::<Vec<&str>>();
|
||||
if parts.len() == 1 {
|
||||
return pattern == s;
|
||||
}
|
||||
|
||||
if !s.starts_with(parts[0]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the first line of the pattern is just a wildcard the newline character
|
||||
// needs to be pre-pended so it can safely match anything or nothing and
|
||||
// continue matching.
|
||||
if pattern.lines().next() == Some(wildcard) {
|
||||
s.insert(0, '\n');
|
||||
}
|
||||
|
||||
let mut t = s.split_at(parts[0].len());
|
||||
let parts = parse_wildcard_pattern_text(&pattern).unwrap();
|
||||
|
||||
let mut was_last_wildcard = false;
|
||||
for (i, part) in parts.iter().enumerate() {
|
||||
if i == 0 {
|
||||
continue;
|
||||
match part {
|
||||
WildcardPatternPart::Wildcard => {
|
||||
output_lines.push("<WILDCARD />".to_string());
|
||||
}
|
||||
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 {
|
||||
current_text.find(search_text)
|
||||
};
|
||||
match search_index {
|
||||
Some(found_index) if was_last_wildcard || found_index == 0 => {
|
||||
output_lines.push(format!(
|
||||
"<FOUND>{}</FOUND>",
|
||||
colors::gray(annotate_whitespace(search_text))
|
||||
));
|
||||
current_text = ¤t_text[found_index + search_text.len()..];
|
||||
}
|
||||
Some(index) => {
|
||||
output_lines.push(
|
||||
"==== FOUND SEARCH TEXT IN WRONG POSITION ====".to_string(),
|
||||
);
|
||||
output_lines.push(colors::gray(annotate_whitespace(search_text)));
|
||||
output_lines
|
||||
.push("==== HAD UNKNOWN PRECEEDING TEXT ====".to_string());
|
||||
output_lines
|
||||
.push(colors::red(annotate_whitespace(¤t_text[..index])));
|
||||
return WildcardMatchResult::Fail(output_lines.join("\n"));
|
||||
}
|
||||
None => {
|
||||
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 {
|
||||
max_found_index = index;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !was_last_wildcard && max_found_index > 0 {
|
||||
output_lines.push(format!(
|
||||
"<FOUND>{}</FOUND>",
|
||||
colors::gray(annotate_whitespace(
|
||||
&search_text[..max_found_index]
|
||||
))
|
||||
));
|
||||
}
|
||||
output_lines
|
||||
.push("==== COULD NOT FIND SEARCH TEXT ====".to_string());
|
||||
output_lines.push(colors::green(annotate_whitespace(
|
||||
if was_last_wildcard {
|
||||
search_text
|
||||
} else {
|
||||
&search_text[max_found_index..]
|
||||
},
|
||||
)));
|
||||
if was_last_wildcard && max_found_index > 0 {
|
||||
output_lines.push(format!(
|
||||
"==== MAX FOUND ====\n{}",
|
||||
colors::red(annotate_whitespace(
|
||||
&search_text[..max_found_index]
|
||||
))
|
||||
));
|
||||
}
|
||||
return WildcardMatchResult::Fail(output_lines.join("\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
WildcardPatternPart::UnorderedLines(expected_lines) => {
|
||||
assert!(!was_last_wildcard, "unsupported");
|
||||
let mut actual_lines = Vec::with_capacity(expected_lines.len());
|
||||
for _ in 0..expected_lines.len() {
|
||||
match current_text.find('\n') {
|
||||
Some(end_line_index) => {
|
||||
actual_lines.push(¤t_text[..end_line_index]);
|
||||
current_text = ¤t_text[end_line_index + 1..];
|
||||
}
|
||||
None => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
actual_lines.sort_unstable();
|
||||
let mut expected_lines = expected_lines.clone();
|
||||
expected_lines.sort_unstable();
|
||||
|
||||
if actual_lines.len() != expected_lines.len() {
|
||||
output_lines
|
||||
.push("==== HAD WRONG NUMBER OF UNORDERED LINES ====".to_string());
|
||||
output_lines.push("# ACTUAL".to_string());
|
||||
output_lines.extend(
|
||||
actual_lines
|
||||
.iter()
|
||||
.map(|l| colors::green(annotate_whitespace(l))),
|
||||
);
|
||||
output_lines.push("# EXPECTED".to_string());
|
||||
output_lines.extend(
|
||||
expected_lines
|
||||
.iter()
|
||||
.map(|l| colors::green(annotate_whitespace(l))),
|
||||
);
|
||||
return WildcardMatchResult::Fail(output_lines.join("\n"));
|
||||
}
|
||||
for (actual, expected) in actual_lines.iter().zip(expected_lines.iter())
|
||||
{
|
||||
if actual != expected {
|
||||
output_lines
|
||||
.push("==== UNORDERED LINE DID NOT MATCH ====".to_string());
|
||||
output_lines.push(format!(
|
||||
" ACTUAL: {}",
|
||||
colors::red(annotate_whitespace(actual))
|
||||
));
|
||||
output_lines.push(format!(
|
||||
"EXPECTED: {}",
|
||||
colors::green(annotate_whitespace(expected))
|
||||
));
|
||||
return WildcardMatchResult::Fail(output_lines.join("\n"));
|
||||
}
|
||||
}
|
||||
output_lines.push("# Found matching unordered lines".to_string());
|
||||
}
|
||||
}
|
||||
dbg!(part, i);
|
||||
if i == parts.len() - 1 && (part.is_empty() || *part == "\n") {
|
||||
dbg!("exit 1 true", i);
|
||||
return true;
|
||||
}
|
||||
if let Some(found) = t.1.find(*part) {
|
||||
dbg!("found ", found);
|
||||
t = t.1.split_at(found + part.len());
|
||||
} else {
|
||||
dbg!("exit false ", i);
|
||||
return false;
|
||||
was_last_wildcard = matches!(part, WildcardPatternPart::Wildcard);
|
||||
}
|
||||
|
||||
if was_last_wildcard || current_text.is_empty() {
|
||||
WildcardMatchResult::Success
|
||||
} else {
|
||||
output_lines.push("==== HAD TEXT AT END OF FILE ====".to_string());
|
||||
output_lines.push(colors::red(annotate_whitespace(current_text)));
|
||||
WildcardMatchResult::Fail(output_lines.join("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum WildcardPatternPart<'a> {
|
||||
Wildcard,
|
||||
Text(&'a str),
|
||||
UnorderedLines(Vec<&'a str>),
|
||||
}
|
||||
|
||||
fn parse_wildcard_pattern_text(
|
||||
text: &str,
|
||||
) -> Result<Vec<WildcardPatternPart>, monch::ParseErrorFailureError> {
|
||||
use monch::*;
|
||||
|
||||
fn parse_unordered_lines(input: &str) -> ParseResult<Vec<&str>> {
|
||||
const END_TEXT: &str = "\n[UNORDERED_END]\n";
|
||||
let (input, _) = tag("[UNORDERED_START]\n")(input)?;
|
||||
match input.find(END_TEXT) {
|
||||
Some(end_index) => ParseResult::Ok((
|
||||
&input[end_index + END_TEXT.len()..],
|
||||
input[..end_index].lines().collect::<Vec<_>>(),
|
||||
)),
|
||||
None => ParseError::fail(input, "Could not find [UNORDERED_END]"),
|
||||
}
|
||||
}
|
||||
|
||||
dbg!("end ", t.1.len());
|
||||
t.1.is_empty()
|
||||
enum InnerPart<'a> {
|
||||
Wildcard,
|
||||
UnorderedLines(Vec<&'a str>),
|
||||
Char,
|
||||
}
|
||||
|
||||
struct Parser<'a> {
|
||||
current_input: &'a str,
|
||||
last_text_input: &'a str,
|
||||
parts: Vec<WildcardPatternPart<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
fn parse(mut self) -> ParseResult<'a, Vec<WildcardPatternPart<'a>>> {
|
||||
while !self.current_input.is_empty() {
|
||||
let (next_input, inner_part) = or3(
|
||||
map(tag("[WILDCARD]"), |_| InnerPart::Wildcard),
|
||||
map(parse_unordered_lines, |lines| {
|
||||
InnerPart::UnorderedLines(lines)
|
||||
}),
|
||||
map(next_char, |_| InnerPart::Char),
|
||||
)(self.current_input)?;
|
||||
match inner_part {
|
||||
InnerPart::Wildcard => {
|
||||
self.queue_previous_text(next_input);
|
||||
self.parts.push(WildcardPatternPart::Wildcard);
|
||||
}
|
||||
InnerPart::UnorderedLines(expected_lines) => {
|
||||
self.queue_previous_text(next_input);
|
||||
self
|
||||
.parts
|
||||
.push(WildcardPatternPart::UnorderedLines(expected_lines));
|
||||
}
|
||||
InnerPart::Char => {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
self.current_input = next_input;
|
||||
}
|
||||
|
||||
self.queue_previous_text("");
|
||||
|
||||
ParseResult::Ok(("", self.parts))
|
||||
}
|
||||
|
||||
fn queue_previous_text(&mut self, next_input: &'a str) {
|
||||
let previous_text = &self.last_text_input
|
||||
[..self.last_text_input.len() - self.current_input.len()];
|
||||
if !previous_text.is_empty() {
|
||||
self.parts.push(WildcardPatternPart::Text(previous_text));
|
||||
}
|
||||
self.last_text_input = next_input;
|
||||
}
|
||||
}
|
||||
|
||||
with_failure_handling(|input| {
|
||||
Parser {
|
||||
current_input: input,
|
||||
last_text_input: input,
|
||||
parts: Vec::new(),
|
||||
}
|
||||
.parse()
|
||||
})(text)
|
||||
}
|
||||
|
||||
pub fn with_pty(deno_args: &[&str], action: impl FnMut(Pty)) {
|
||||
|
@ -2604,6 +2813,67 @@ pub fn parse_max_mem(output: &str) -> Option<u64> {
|
|||
None
|
||||
}
|
||||
|
||||
pub(crate) mod colors {
|
||||
use std::io::Write;
|
||||
|
||||
use termcolor::Ansi;
|
||||
use termcolor::Color;
|
||||
use termcolor::ColorSpec;
|
||||
use termcolor::WriteColor;
|
||||
|
||||
pub fn bold<S: AsRef<str>>(s: S) -> String {
|
||||
let mut style_spec = ColorSpec::new();
|
||||
style_spec.set_bold(true);
|
||||
style(s, style_spec)
|
||||
}
|
||||
|
||||
pub fn red<S: AsRef<str>>(s: S) -> String {
|
||||
fg_color(s, Color::Red)
|
||||
}
|
||||
|
||||
pub fn bold_red<S: AsRef<str>>(s: S) -> String {
|
||||
bold_fg_color(s, Color::Red)
|
||||
}
|
||||
|
||||
pub fn green<S: AsRef<str>>(s: S) -> String {
|
||||
fg_color(s, Color::Green)
|
||||
}
|
||||
|
||||
pub fn bold_green<S: AsRef<str>>(s: S) -> String {
|
||||
bold_fg_color(s, Color::Green)
|
||||
}
|
||||
|
||||
pub fn bold_blue<S: AsRef<str>>(s: S) -> String {
|
||||
bold_fg_color(s, Color::Blue)
|
||||
}
|
||||
|
||||
pub fn gray<S: AsRef<str>>(s: S) -> String {
|
||||
fg_color(s, Color::Ansi256(245))
|
||||
}
|
||||
|
||||
fn bold_fg_color<S: AsRef<str>>(s: S, color: Color) -> String {
|
||||
let mut style_spec = ColorSpec::new();
|
||||
style_spec.set_bold(true);
|
||||
style_spec.set_fg(Some(color));
|
||||
style(s, style_spec)
|
||||
}
|
||||
|
||||
fn fg_color<S: AsRef<str>>(s: S, color: Color) -> String {
|
||||
let mut style_spec = ColorSpec::new();
|
||||
style_spec.set_fg(Some(color));
|
||||
style(s, style_spec)
|
||||
}
|
||||
|
||||
fn style<S: AsRef<str>>(s: S, colorspec: ColorSpec) -> String {
|
||||
let mut v = Vec::new();
|
||||
let mut ansi_writer = Ansi::new(&mut v);
|
||||
ansi_writer.set_color(&colorspec).unwrap();
|
||||
ansi_writer.write_all(s.as_ref().as_bytes()).unwrap();
|
||||
ansi_writer.reset().unwrap();
|
||||
String::from_utf8_lossy(&v).into_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -2689,6 +2959,15 @@ mod tests {
|
|||
assert_eq!(strace.get("total").unwrap().usecs_per_call, Some(6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_parse_wildcard_match_text() {
|
||||
let result =
|
||||
parse_wildcard_pattern_text("[UNORDERED_START]\ntesting\ntesting")
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_contains!(result.to_string(), "Could not find [UNORDERED_END]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wildcard_match() {
|
||||
let fixtures = vec![
|
||||
|
@ -2733,16 +3012,15 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_pattern_match() {
|
||||
fn test_wildcard_match2() {
|
||||
// foo, bar, baz, qux, quux, quuz, corge, grault, garply, waldo, fred, plugh, xyzzy
|
||||
|
||||
let wildcard = "[BAR]";
|
||||
assert!(pattern_match("foo[BAR]baz", "foobarbaz", wildcard));
|
||||
assert!(!pattern_match("foo[BAR]baz", "foobazbar", wildcard));
|
||||
assert!(wildcard_match("foo[WILDCARD]baz", "foobarbaz"));
|
||||
assert!(!wildcard_match("foo[WILDCARD]baz", "foobazbar"));
|
||||
|
||||
let multiline_pattern = "[BAR]
|
||||
let multiline_pattern = "[WILDCARD]
|
||||
foo:
|
||||
[BAR]baz[BAR]";
|
||||
[WILDCARD]baz[WILDCARD]";
|
||||
|
||||
fn multi_line_builder(input: &str, leading_text: Option<&str>) -> String {
|
||||
// If there is leading text add a newline so it's on it's own line
|
||||
|
@ -2767,31 +3045,52 @@ grault",
|
|||
);
|
||||
|
||||
// Correct input & leading line
|
||||
assert!(pattern_match(
|
||||
assert!(wildcard_match(
|
||||
multiline_pattern,
|
||||
&multi_line_builder("baz", Some("QUX=quux")),
|
||||
wildcard
|
||||
));
|
||||
|
||||
// Correct input & no leading line
|
||||
assert!(pattern_match(
|
||||
// Should fail when leading line
|
||||
assert!(!wildcard_match(
|
||||
multiline_pattern,
|
||||
&multi_line_builder("baz", None),
|
||||
wildcard
|
||||
));
|
||||
|
||||
// Incorrect input & leading line
|
||||
assert!(!pattern_match(
|
||||
assert!(!wildcard_match(
|
||||
multiline_pattern,
|
||||
&multi_line_builder("garply", Some("QUX=quux")),
|
||||
wildcard
|
||||
));
|
||||
|
||||
// Incorrect input & no leading line
|
||||
assert!(!pattern_match(
|
||||
assert!(!wildcard_match(
|
||||
multiline_pattern,
|
||||
&multi_line_builder("garply", None),
|
||||
wildcard
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wildcard_match_unordered_lines() {
|
||||
// matching
|
||||
assert!(wildcard_match(
|
||||
concat!("[UNORDERED_START]\n", "B\n", "A\n", "[UNORDERED_END]\n"),
|
||||
concat!("A\n", "B\n",)
|
||||
));
|
||||
// different line
|
||||
assert!(!wildcard_match(
|
||||
concat!("[UNORDERED_START]\n", "Ba\n", "A\n", "[UNORDERED_END]\n"),
|
||||
concat!("A\n", "B\n",)
|
||||
));
|
||||
// different number of lines
|
||||
assert!(!wildcard_match(
|
||||
concat!(
|
||||
"[UNORDERED_START]\n",
|
||||
"B\n",
|
||||
"A\n",
|
||||
"C\n",
|
||||
"[UNORDERED_END]\n"
|
||||
),
|
||||
concat!("A\n", "B\n",)
|
||||
));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue