aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Mueller <deso@posteo.net>2020-04-05 10:47:40 -0700
committerDaniel Mueller <deso@posteo.net>2020-04-05 10:47:40 -0700
commitcac16996ffc4ef15e3eaf2943a7ac42c921f55ac (patch)
tree1296e71963489db8fb4ff55c4d4d43843c43ebb0
parentb2368d3647a469f1e923f19325d5a87daed4436e (diff)
downloadnitrocli-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.
-rw-r--r--var/shell-complete.rs109
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"
+ );
+ }
}