test(complete): Helper for asserting dynamic completions

This commit is contained in:
Roland Fredenhagen 2023-08-04 01:13:49 +07:00
parent 5b10a9d83c
commit 65b9c2b37d
No known key found for this signature in database
GPG key ID: 094AF99241035EB6

View file

@ -1,34 +1,38 @@
#![cfg(feature = "unstable-dynamic")] #![cfg(feature = "unstable-dynamic")]
use std::path::Path;
use clap::Command;
macro_rules! complete {
($cmd:expr, $input:expr$(, current_dir = $current_dir:expr)? $(,)?) => {
{
#[allow(unused)]
let current_dir = None;
$(let current_dir = $current_dir;)?
complete(&mut $cmd, $input, current_dir)
}
}
}
#[test] #[test]
fn suggest_subcommand_subset() { fn suggest_subcommand_subset() {
let name = "exhaustive"; let mut cmd = Command::new("exhaustive")
let mut cmd = clap::Command::new(name) .subcommand(Command::new("hello-world"))
.subcommand(clap::Command::new("hello-world")) .subcommand(Command::new("hello-moon"))
.subcommand(clap::Command::new("hello-moon")) .subcommand(Command::new("goodbye-world"));
.subcommand(clap::Command::new("goodbye-world"));
let args = [name, "he"]; snapbox::assert_eq(
let arg_index = 1; "hello-moon
let args = IntoIterator::into_iter(args) hello-world
.map(std::ffi::OsString::from) help\tPrint this message or the help of the given subcommand(s)",
.collect::<Vec<_>>(); complete!(cmd, "he"),
let current_dir = None; );
let completions =
clap_complete::dynamic::complete(&mut cmd, args, arg_index, current_dir).unwrap();
let completions = completions
.into_iter()
.map(|s| s.0.to_string_lossy().into_owned())
.collect::<Vec<_>>();
assert_eq!(completions, ["hello-moon", "hello-world", "help"]);
} }
#[test] #[test]
fn suggest_long_flag_subset() { fn suggest_long_flag_subset() {
let name = "exhaustive"; let mut cmd = Command::new("exhaustive")
let mut cmd = clap::Command::new(name)
.arg( .arg(
clap::Arg::new("hello-world") clap::Arg::new("hello-world")
.long("hello-world") .long("hello-world")
@ -45,53 +49,33 @@ fn suggest_long_flag_subset() {
.action(clap::ArgAction::Count), .action(clap::ArgAction::Count),
); );
let args = [name, "--he"]; snapbox::assert_eq(
let arg_index = 1; "--hello-world
let args = IntoIterator::into_iter(args) --hello-moon
.map(std::ffi::OsString::from) --help\tPrint help",
.collect::<Vec<_>>(); complete!(cmd, "--he"),
let current_dir = None; );
let completions =
clap_complete::dynamic::complete(&mut cmd, args, arg_index, current_dir).unwrap();
let completions = completions
.into_iter()
.map(|s| s.0.to_string_lossy().into_owned())
.collect::<Vec<_>>();
assert_eq!(completions, ["--hello-world", "--hello-moon", "--help"]);
} }
#[test] #[test]
fn suggest_possible_value_subset() { fn suggest_possible_value_subset() {
let name = "exhaustive"; let name = "exhaustive";
let mut cmd = clap::Command::new(name).arg(clap::Arg::new("hello-world").value_parser([ let mut cmd = Command::new(name).arg(clap::Arg::new("hello-world").value_parser([
"hello-world", "hello-world",
"hello-moon", "hello-moon",
"goodbye-world", "goodbye-world",
])); ]));
let args = [name, "hello"]; snapbox::assert_eq(
let arg_index = 1; "hello-world
let args = IntoIterator::into_iter(args) hello-moon",
.map(std::ffi::OsString::from) complete!(cmd, "hello"),
.collect::<Vec<_>>(); );
let current_dir = None;
let completions =
clap_complete::dynamic::complete(&mut cmd, args, arg_index, current_dir).unwrap();
let completions = completions
.into_iter()
.map(|s| s.0.to_string_lossy().into_owned())
.collect::<Vec<_>>();
assert_eq!(completions, ["hello-world", "hello-moon"]);
} }
#[test] #[test]
fn suggest_additional_short_flags() { fn suggest_additional_short_flags() {
let name = "exhaustive"; let mut cmd = Command::new("exhaustive")
let mut cmd = clap::Command::new(name)
.arg( .arg(
clap::Arg::new("a") clap::Arg::new("a")
.short('a') .short('a')
@ -108,19 +92,47 @@ fn suggest_additional_short_flags() {
.action(clap::ArgAction::Count), .action(clap::ArgAction::Count),
); );
let args = [name, "-a"]; snapbox::assert_eq(
let arg_index = 1; "-aa
let args = IntoIterator::into_iter(args) -ab
.map(std::ffi::OsString::from) -ac
.collect::<Vec<_>>(); -ah\tPrint help",
let current_dir = None; complete!(cmd, "-a"),
);
let completions = }
clap_complete::dynamic::complete(&mut cmd, args, arg_index, current_dir).unwrap();
let completions = completions fn complete(cmd: &mut Command, args: impl AsRef<str>, current_dir: Option<&Path>) -> String {
.into_iter() let input = args.as_ref();
.map(|s| s.0.to_string_lossy().into_owned()) let mut args = vec![std::ffi::OsString::from(cmd.get_name())];
.collect::<Vec<_>>(); let arg_index;
assert_eq!(completions, ["-aa", "-ab", "-ac", "-ah"]); if let Some((prior, after)) = input.split_once("[TAB]") {
args.extend(prior.split_whitespace().map(From::from));
if prior.ends_with(char::is_whitespace) {
args.push(std::ffi::OsString::default())
}
arg_index = args.len() - 1;
// HACK: this cannot handle in-word '[TAB]'
args.extend(after.split_whitespace().map(From::from));
} else {
args.extend(input.split_whitespace().map(From::from));
if input.ends_with(char::is_whitespace) {
args.push(std::ffi::OsString::default())
}
arg_index = args.len() - 1;
}
clap_complete::dynamic::complete(cmd, args, arg_index, current_dir)
.unwrap()
.into_iter()
.map(|(compl, help)| {
let compl = compl.to_str().unwrap();
if let Some(help) = help {
format!("{compl}\t{help}")
} else {
compl.to_owned()
}
})
.collect::<Vec<_>>()
.join("\n")
} }