use super::classify;
fn paths(v: &[&str]) -> Option<Vec<String>> {
Some(v.iter().map(|s| s.to_string()).collect())
}
#[test]
fn terminal_app_backslash_escaped_specials() {
assert_eq!(
classify(r"/Users/San/abc\(ev50\)_xyz.tif "),
paths(&["/Users/San/abc(ev50)_xyz.tif"])
);
assert_eq!(classify(r"/Lorem\ Ipsum.txt"), paths(&["/Lorem Ipsum.txt"]));
}
#[test]
fn terminal_app_multi_drop_space_joined_escaped() {
assert_eq!(
classify(r"/a/one\ file.txt /b/two.txt "),
paths(&["/a/one file.txt", "/b/two.txt"])
);
}
#[test]
fn iterm2_default_backslash_and_optional_single_quotes() {
assert_eq!(
classify(r"/Users/anon/text\ file\ \(1\).txt"),
paths(&["/Users/anon/text file (1).txt"])
);
assert_eq!(
classify("'/Users/anon/text file (1).txt'"),
paths(&["/Users/anon/text file (1).txt"])
);
assert_eq!(
classify("'/a/one file.txt' '/b/two file.txt'"),
paths(&["/a/one file.txt", "/b/two file.txt"])
);
}
#[test]
fn ghostty_backslash_escaped_and_gtk_newline_joined_multi_drop() {
assert_eq!(
classify(r"/home/u/my\ document.jpg"),
paths(&["/home/u/my document.jpg"])
);
assert_eq!(
classify("/home/u/a\\ b.txt\n/home/u/c.txt\n"),
paths(&["/home/u/a b.txt", "/home/u/c.txt"])
);
}
#[test]
fn wezterm_all_five_quote_dropped_files_modes() {
assert_eq!(
classify(r"/w/hello\ world.txt"),
paths(&["/w/hello world.txt"])
);
assert_eq!(
classify(r#""/w/hello (\$world)""#),
paths(&["/w/hello ($world)"])
);
assert_eq!(
classify(r#""C:\Users\me\hello world.txt""#),
paths(&[r"C:\Users\me\hello world.txt"])
);
assert_eq!(classify("/w/plain.txt"), paths(&["/w/plain.txt"]));
assert_eq!(classify("/w/hello world.txt"), None);
}
#[test]
fn kitty_raw_single_file() {
assert_eq!(classify("/home/u/photo.png"), paths(&["/home/u/photo.png"]));
assert_eq!(classify("/home/u/dir/"), paths(&["/home/u/dir/"]));
}
#[test]
fn windows_terminal_quoted_and_raw_drive_paths() {
assert_eq!(
classify(r"C:\Users\me\report.xlsx"),
paths(&[r"C:\Users\me\report.xlsx"])
);
assert_eq!(
classify(r#""C:\My Project\config.xml""#),
paths(&[r"C:\My Project\config.xml"])
);
assert_eq!(classify("C:/Users/me/x.txt"), paths(&["C:/Users/me/x.txt"]));
assert_eq!(
classify(r"\\server\share\file.txt"),
paths(&[r"\\server\share\file.txt"])
);
assert_eq!(
classify(r#""\\server\share\my file.txt""#),
paths(&[r"\\server\share\my file.txt"])
);
assert_eq!(
classify("'/mnt/c/Users/me/a b.txt'"),
paths(&["/mnt/c/Users/me/a b.txt"])
);
}
#[test]
fn windows_terminal_wsl_embedded_quote_bug_refuses() {
assert_eq!(classify("'/mnt/d/John's Archive'"), None);
}
#[test]
fn gnome_terminal_g_shell_quote_spellings() {
assert_eq!(
classify("'/home/u/my file.txt' '/home/u/two.txt'"),
paths(&["/home/u/my file.txt", "/home/u/two.txt"])
);
assert_eq!(
classify(r"'/home/u/can'\''t.txt'"),
paths(&["/home/u/can't.txt"])
);
}
#[test]
fn file_urls_decode_with_percent_and_host_handling() {
assert_eq!(
classify("file:///home/u/a%20b.txt"),
paths(&["/home/u/a b.txt"])
);
assert_eq!(
classify("file://localhost/Users/me/x.txt"),
paths(&["/Users/me/x.txt"])
);
assert_eq!(classify("FILE://LOCALHOST/tmp/x"), paths(&["/tmp/x"]));
assert_eq!(
classify("file:///C:/Users/me/x.txt"),
paths(&["C:/Users/me/x.txt"])
);
assert_eq!(
classify("file:///a/one.txt\r\nfile:///b/two%20x.txt\r\n"),
paths(&["/a/one.txt", "/b/two x.txt"])
);
assert_eq!(
classify("file:///tmp/caf%C3%A9.txt"),
paths(&["/tmp/café.txt"])
);
}
#[test]
fn home_relative_returned_as_is() {
assert_eq!(
classify("~/Documents/notes.md"),
paths(&["~/Documents/notes.md"])
);
assert_eq!(classify(r"~/My\ Docs/a.txt"), paths(&["~/My Docs/a.txt"]));
}
#[test]
fn mixed_spellings_in_one_drop() {
assert_eq!(
classify(r"/a/one.txt '/b/two 2.txt' file:///c/three.txt ~/four.txt"),
paths(&["/a/one.txt", "/b/two 2.txt", "/c/three.txt", "~/four.txt"])
);
}
#[test]
fn trailing_newline_variants_accept() {
assert_eq!(classify("/tmp/x\n"), paths(&["/tmp/x"]));
assert_eq!(classify("/tmp/x\r\n"), paths(&["/tmp/x"]));
assert_eq!(classify("/tmp/x \n\n"), paths(&["/tmp/x"]));
}
#[test]
fn root_and_deep_paths_accept() {
assert_eq!(classify("/"), paths(&["/"]));
let deep = format!("/{}", "d/".repeat(100));
assert_eq!(classify(&deep), paths(&[deep.as_str()]));
}
#[test]
fn compiler_diagnostic_single_token_classifies_by_design() {
assert_eq!(classify("/src/main.rs:12:5"), paths(&["/src/main.rs:12:5"]));
}
#[test]
fn prose_and_commands_refuse() {
assert_eq!(classify("hello world"), None);
assert_eq!(classify("see /usr/bin for details"), None);
assert_eq!(classify("ls /a /b"), None);
assert_eq!(classify("rm -rf /tmp/x"), None);
assert_eq!(classify("/usr and more prose"), None);
assert_eq!(classify("--path=/x /y"), None);
}
#[test]
fn code_snippets_refuse() {
assert_eq!(classify("let x = 1;\nlet y = 2;"), None);
assert_eq!(classify("/a/one.txt\n\n/b/two.txt"), None);
assert_eq!(classify("/src/main.rs:12:5: error: expected `;`"), None);
}
#[test]
fn non_file_urls_refuse() {
assert_eq!(classify("http://example.com/a.txt"), None);
assert_eq!(classify("https://example.com/"), None);
assert_eq!(classify("ssh://host/path"), None);
assert_eq!(classify("file://nas.local/share/x.txt"), None);
assert_eq!(classify("file://"), None);
assert_eq!(classify("file://localhost"), None);
}
#[test]
fn relative_and_bare_words_refuse() {
assert_eq!(classify("foo.txt"), None);
assert_eq!(classify("./relative/path"), None);
assert_eq!(classify("../up/one"), None);
assert_eq!(classify("src/lib.rs"), None);
assert_eq!(classify("~"), None);
assert_eq!(classify("~/"), None);
assert_eq!(classify("~bob/x"), None);
}
#[test]
fn malformed_quoting_and_escapes_refuse() {
assert_eq!(classify("'/a/unterminated"), None);
assert_eq!(classify("\"/a/unterminated"), None);
assert_eq!(classify("/a/dangling\\"), None);
assert_eq!(classify("/a/John's.txt"), None);
assert_eq!(classify("'' /a/x"), None);
}
#[test]
fn control_characters_refuse() {
assert_eq!(classify("/a/x\t/b/y"), None);
assert_eq!(classify("/a/x\u{7}"), None);
assert_eq!(classify("file:///a/x%0Ay"), None);
assert_eq!(classify("file:///a/x%2"), None);
assert_eq!(classify("file:///a/x%zz"), None);
assert_eq!(classify("file:///a/x%FF%FE"), None);
}
#[test]
fn size_and_count_guards_refuse() {
let huge = format!("/a/{}", "x".repeat(70 * 1024));
assert_eq!(classify(&huge), None);
let long_tok = format!("/a/{}", "x".repeat(5000));
assert_eq!(classify(&long_tok), None);
let many = (0..600)
.map(|i| format!("/f/{i}"))
.collect::<Vec<_>>()
.join(" ");
assert_eq!(classify(&many), None);
assert_eq!(classify(""), None);
assert_eq!(classify(" \n "), None);
}
#[test]
fn windows_edge_shapes() {
assert_eq!(classify("C:"), None);
assert_eq!(classify("note: /a/x"), None);
assert_eq!(classify("C://x"), None);
assert_eq!(classify(r#""C:\a b"tail"#), None);
assert_eq!(
classify(r"\\\\srv\\share\\x.txt"),
paths(&[r"\\srv\share\x.txt"])
);
}
#[test]
fn multiline_requires_every_line_to_classify() {
assert_eq!(classify("/a/one.txt\nand some prose"), None);
assert_eq!(
classify("/a/one.txt /b/two.txt\nfile:///c/three.txt"),
paths(&["/a/one.txt", "/b/two.txt", "/c/three.txt"])
);
}