From 65b9c2b37d72ef4fee2010ed04cc880811fd6fe2 Mon Sep 17 00:00:00 2001 From: Roland Fredenhagen Date: Fri, 4 Aug 2023 01:13:49 +0700 Subject: [PATCH] test(complete): Helper for asserting dynamic completions --- clap_complete/tests/testsuite/dynamic.rs | 150 ++++++++++++----------- 1 file changed, 81 insertions(+), 69 deletions(-) diff --git a/clap_complete/tests/testsuite/dynamic.rs b/clap_complete/tests/testsuite/dynamic.rs index 8a2d9e466b4..452a638a447 100644 --- a/clap_complete/tests/testsuite/dynamic.rs +++ b/clap_complete/tests/testsuite/dynamic.rs @@ -1,34 +1,38 @@ #![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] fn suggest_subcommand_subset() { - let name = "exhaustive"; - let mut cmd = clap::Command::new(name) - .subcommand(clap::Command::new("hello-world")) - .subcommand(clap::Command::new("hello-moon")) - .subcommand(clap::Command::new("goodbye-world")); - - let args = [name, "he"]; - let arg_index = 1; - let args = IntoIterator::into_iter(args) - .map(std::ffi::OsString::from) - .collect::>(); - 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::>(); - - assert_eq!(completions, ["hello-moon", "hello-world", "help"]); + let mut cmd = Command::new("exhaustive") + .subcommand(Command::new("hello-world")) + .subcommand(Command::new("hello-moon")) + .subcommand(Command::new("goodbye-world")); + + snapbox::assert_eq( + "hello-moon +hello-world +help\tPrint this message or the help of the given subcommand(s)", + complete!(cmd, "he"), + ); } #[test] fn suggest_long_flag_subset() { - let name = "exhaustive"; - let mut cmd = clap::Command::new(name) + let mut cmd = Command::new("exhaustive") .arg( clap::Arg::new("hello-world") .long("hello-world") @@ -45,53 +49,33 @@ fn suggest_long_flag_subset() { .action(clap::ArgAction::Count), ); - let args = [name, "--he"]; - let arg_index = 1; - let args = IntoIterator::into_iter(args) - .map(std::ffi::OsString::from) - .collect::>(); - 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::>(); - - assert_eq!(completions, ["--hello-world", "--hello-moon", "--help"]); + snapbox::assert_eq( + "--hello-world +--hello-moon +--help\tPrint help", + complete!(cmd, "--he"), + ); } #[test] fn suggest_possible_value_subset() { 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-moon", "goodbye-world", ])); - let args = [name, "hello"]; - let arg_index = 1; - let args = IntoIterator::into_iter(args) - .map(std::ffi::OsString::from) - .collect::>(); - 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::>(); - - assert_eq!(completions, ["hello-world", "hello-moon"]); + snapbox::assert_eq( + "hello-world +hello-moon", + complete!(cmd, "hello"), + ); } #[test] fn suggest_additional_short_flags() { - let name = "exhaustive"; - let mut cmd = clap::Command::new(name) + let mut cmd = Command::new("exhaustive") .arg( clap::Arg::new("a") .short('a') @@ -108,19 +92,47 @@ fn suggest_additional_short_flags() { .action(clap::ArgAction::Count), ); - let args = [name, "-a"]; - let arg_index = 1; - let args = IntoIterator::into_iter(args) - .map(std::ffi::OsString::from) - .collect::>(); - let current_dir = None; + snapbox::assert_eq( + "-aa +-ab +-ac +-ah\tPrint help", + 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, current_dir: Option<&Path>) -> String { + let input = args.as_ref(); + let mut args = vec![std::ffi::OsString::from(cmd.get_name())]; + let arg_index; + + 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(|s| s.0.to_string_lossy().into_owned()) - .collect::>(); - - assert_eq!(completions, ["-aa", "-ab", "-ac", "-ah"]); + .map(|(compl, help)| { + let compl = compl.to_str().unwrap(); + if let Some(help) = help { + format!("{compl}\t{help}") + } else { + compl.to_owned() + } + }) + .collect::>() + .join("\n") }