diff options
author | Daniel Mueller <deso@posteo.net> | 2020-04-05 10:47:40 -0700 |
---|---|---|
committer | Daniel Mueller <deso@posteo.net> | 2020-04-05 10:47:40 -0700 |
commit | cac16996ffc4ef15e3eaf2943a7ac42c921f55ac (patch) | |
tree | 1296e71963489db8fb4ff55c4d4d43843c43ebb0 /var | |
parent | b2368d3647a469f1e923f19325d5a87daed4436e (diff) | |
download | nitrocli-cac16996ffc4ef15e3eaf2943a7ac42c921f55ac.tar.gz nitrocli-cac16996ffc4ef15e3eaf2943a7ac42c921f55ac.tar.bz2 |
Add test for bash completion functionality
This change adds a test for the previously introduced bash completion
functionality. To test the generated completion script, we spin up a
bash instance, source the script, and then perform a completion as the
shell would do it. It seems impossible to convince compgen to do the
heavy lifting for us and so we invoke the completion function with the
expected environment variables present.
Diffstat (limited to 'var')
-rw-r--r-- | var/shell-complete.rs | 109 |
1 files changed, 107 insertions, 2 deletions
diff --git a/var/shell-complete.rs b/var/shell-complete.rs index 0a525d8..c69a426 100644 --- a/var/shell-complete.rs +++ b/var/shell-complete.rs @@ -52,8 +52,113 @@ pub struct Args { pub command: String, } +fn generate_bash<W>(command: &str, output: &mut W) +where + W: io::Write, +{ + let mut app = nitrocli::Args::clap(); + app.gen_completions_to(command, clap::Shell::Bash, output); +} + fn main() { let args = Args::from_args(); - let mut app = nitrocli::Args::clap(); - app.gen_completions_to(&args.command, clap::Shell::Bash, &mut io::stdout()); + generate_bash(&args.command, &mut io::stdout()) +} + +#[cfg(test)] +mod tests { + use super::*; + + use std::io; + use std::ops::Add as _; + use std::process; + + /// Separate the given words by newlines. + fn lines<'w, W>(mut words: W) -> String + where + W: Iterator<Item = &'w str>, + { + let first = words.next().unwrap_or(""); + words + .fold(first.to_string(), |words, word| { + format!("{}\n{}", words, word) + }) + .add("\n") + } + + /// Check if `bash` is present on the system. + fn has_bash() -> bool { + match process::Command::new("bash").arg("-c").arg("exit").spawn() { + // We deliberately only indicate that bash does not exist if we + // get a file-not-found error. We don't expect any other error but + // should there be one things will blow up later. + Err(ref err) if err.kind() == io::ErrorKind::NotFound => false, + _ => true, + } + } + + /// Perform a bash completion of the given arguments to nitrocli. + fn complete_bash<'w, W>(words: W) -> Vec<u8> + where + W: ExactSizeIterator<Item = &'w str>, + { + let mut buffer = Vec::new(); + generate_bash("nitrocli", &mut buffer); + + let script = String::from_utf8(buffer).unwrap(); + let command = format!( + " +set -e; +eval '{script}'; +export COMP_WORDS=({words}); +export COMP_CWORD={index}; +_nitrocli; +echo -n ${{COMPREPLY}} + ", + index = words.len(), + words = lines(Some("nitrocli").into_iter().chain(words)), + script = script + ); + + let output = process::Command::new("bash") + .arg("-c") + .arg(command) + .output() + .unwrap(); + + output.stdout + } + + #[test] + fn array_lines() { + assert_eq!(&lines(vec![].into_iter()), "\n"); + assert_eq!(&lines(vec!["first"].into_iter()), "first\n"); + assert_eq!( + &lines(vec!["first", "second"].into_iter()), + "first\nsecond\n" + ); + assert_eq!( + &lines(vec!["first", "second", "third"].into_iter()), + "first\nsecond\nthird\n" + ); + } + + #[test] + fn complete_all_the_things() { + if !has_bash() { + return; + } + + assert_eq!(complete_bash(vec!["stat"].into_iter()), b"status"); + assert_eq!( + complete_bash(vec!["status", "--ver"].into_iter()), + b"--version" + ); + assert_eq!(complete_bash(vec!["--version"].into_iter()), b"--version"); + assert_eq!(complete_bash(vec!["--model", "s"].into_iter()), b"storage"); + assert_eq!( + complete_bash(vec!["otp", "get", "--model", "p"].into_iter()), + b"pro" + ); + } } |