Add tests for completions (#204)

This commit is contained in:
Ajeet D'Souza 2021-05-07 13:04:44 +05:30 committed by GitHub
parent efe11ec924
commit d33bfd111f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 119 additions and 42 deletions

View File

@ -20,7 +20,5 @@ jobs:
- uses: cachix/install-nix-action@v12
if: ${{ matrix.os != 'windows-latest' }}
with:
nix_path: nixpkgs=channel:nixos-stable
- run: mkdir -p /tmp/home && make test
env:
HOME: /tmp/home
nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/20.09.tar.gz
- run: make test

View File

@ -11,7 +11,7 @@ endif
.PHONY: build clean install test uninstall
build:
cargo build --release $(ci_color_always)
cargo build $(ci_color_always)
clean:
cargo clean $(ci_color_always)
@ -22,16 +22,16 @@ install:
ifeq ($(NIX), true)
test:
nix-shell --pure --run 'cargo fmt -- --check --files-with-diff $(ci_color_always)'
nix-shell --pure --run 'cargo check --all-features --release $(ci_color_always)'
nix-shell --pure --run 'cargo clippy --all-features --release $(ci_color_always) -- --deny warnings --deny clippy::all'
nix-shell --pure --run 'cargo test --all-features --no-fail-fast --release $(ci_color_always)'
nix-shell --pure --run 'cargo check --all-features $(ci_color_always)'
nix-shell --pure --run 'cargo clippy --all-features $(ci_color_always) -- --deny warnings --deny clippy::all'
nix-shell --pure --run 'cargo test --all-features --no-fail-fast $(ci_color_always)'
nix-shell --pure --run 'cargo audit --deny warnings $(ci_color_always) --ignore=RUSTSEC-2020-0095'
else
test:
cargo fmt -- --check --files-with-diff $(ci_color_always)
cargo check --all-features --release $(ci_color_always)
cargo clippy --all-features --release $(ci_color_always) -- --deny warnings --deny clippy::all
cargo test --no-fail-fast --release $(ci_color_always)
cargo check --all-features $(ci_color_always)
cargo clippy --all-features $(ci_color_always) -- --deny warnings --deny clippy::all
cargo test --no-fail-fast $(ci_color_always)
cargo audit --deny warnings $(ci_color_always) --ignore=RUSTSEC-2020-0095
endif

View File

@ -19,9 +19,8 @@ fn crate_version() -> String {
}
fn generate_completions() {
mod app {
include!("src/app.rs");
}
#[path = "src/app/_app.rs"]
mod app;
use app::App;
use clap::IntoApp;

View File

@ -38,6 +38,6 @@ impl Run for Init {
InitShell::Zsh => shell::Zsh(opts).render(),
}
.context("could not render template")?;
writeln!(io::stdout(), "{}", source).wrap_write("stdout")
writeln!(io::stdout(), "{}", source).pipe_exit("stdout")
}
}

View File

@ -1,10 +1,11 @@
mod _app;
mod add;
mod import;
mod init;
mod query;
mod remove;
use crate::app::App;
pub use crate::app::_app::*;
use anyhow::Result;

View File

@ -27,7 +27,7 @@ impl Run for Query {
if self.interactive {
let mut fzf = Fzf::new(false)?;
for dir in matches {
writeln!(fzf.stdin(), "{}", dir.display_score(now)).wrap_write("fzf")?;
writeln!(fzf.stdin(), "{}", dir.display_score(now)).pipe_exit("fzf")?;
}
let selection = fzf.wait_select()?;
@ -53,9 +53,9 @@ impl Run for Query {
} else {
writeln!(handle, "{}", dir.display())
}
.wrap_write("stdout")?;
.pipe_exit("stdout")?;
}
handle.flush().wrap_write("stdout")?;
handle.flush().pipe_exit("stdout")?;
} else {
let dir = matches.next().context("no match found")?;
if self.score {
@ -63,7 +63,7 @@ impl Run for Query {
} else {
writeln!(io::stdout(), "{}", dir.display())
}
.wrap_write("stdout")?;
.pipe_exit("stdout")?;
}
Ok(())

View File

@ -25,7 +25,7 @@ impl Run for Remove {
let mut fzf = Fzf::new(true)?;
for dir in db.iter_matches(&query, now, resolve_symlinks) {
writeln!(fzf.stdin(), "{}", dir.display_score(now)).wrap_write("fzf")?;
writeln!(fzf.stdin(), "{}", dir.display_score(now)).pipe_exit("fzf")?;
}
selection = fzf.wait_select()?;

View File

@ -16,11 +16,11 @@ impl Display for SilentExit {
}
pub trait WriteErrorHandler {
fn wrap_write(self, device: &str) -> Result<()>;
fn pipe_exit(self, device: &str) -> Result<()>;
}
impl WriteErrorHandler for io::Result<()> {
fn wrap_write(self, device: &str) -> Result<()> {
fn pipe_exit(self, device: &str) -> Result<()> {
match self {
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => bail!(SilentExit { code: 0 }),
result => result.with_context(|| format!("could not write to {}", device)),

View File

@ -3,6 +3,7 @@ use crate::error::SilentExit;
use anyhow::{bail, Context, Result};
use std::io;
use std::process::{Child, ChildStdin, Command, Stdio};
pub struct Fzf {
@ -23,9 +24,15 @@ impl Fzf {
command.env("FZF_DEFAULT_OPTS", fzf_opts);
}
Ok(Fzf {
child: command.spawn().context("could not launch fzf")?,
})
let child = match command.spawn() {
Ok(child) => child,
Err(e) if e.kind() == io::ErrorKind::NotFound => {
bail!("could not find fzf, is it installed?")
}
Err(e) => Err(e).context("could not launch fzf")?,
};
Ok(Fzf { child })
}
pub fn stdin(&mut self) -> &mut ChildStdin {

View File

@ -1,5 +1,4 @@
mod app;
mod cmd;
mod config;
mod db;
mod error;
@ -7,8 +6,7 @@ mod fzf;
mod shell;
mod util;
use crate::app::App;
use crate::cmd::Run;
use crate::app::{App, Run};
use crate::error::SilentExit;
use clap::Clap;

View File

@ -60,13 +60,12 @@ mod tests {
for &resolve_symlinks in BOOLS {
for &hook in HOOKS {
for &cmd in CMDS {
let opt = Opts {
opts.push(Opts {
cmd,
hook,
echo,
resolve_symlinks,
};
opts.push(opt);
});
}
}
}
@ -84,7 +83,7 @@ mod tests {
})
}
macro_rules! generate_tests {
macro_rules! make_tests {
($N:literal) => {
seq!(i in 0..$N {
#[test]
@ -92,7 +91,7 @@ mod tests {
let opts = dbg!(&opts()[i]);
let source = Bash(opts).render().unwrap();
Command::new("bash")
.args(&["-c", &source, "--noediting", "--noprofile", "--norc"])
.args(&["--noprofile", "--norc", "-c", &source])
.assert()
.success()
.stdout("")
@ -213,7 +212,7 @@ mod tests {
let opts = dbg!(&opts()[i]);
let source = Posix(opts).render().unwrap();
let assert = Command::new("bash")
.args(&["--posix", "-c", &source, "--noediting", "--noprofile", "--norc"])
.args(&["--posix", "--noprofile", "--norc", "-c", &source])
.assert()
.success()
.stderr("");
@ -256,7 +255,6 @@ mod tests {
let opts = dbg!(&opts()[i]);
let mut source = Posix(opts).render().unwrap();
source.push('\n');
Command::new("shfmt")
.args(&["-d", "-s", "-ln", "posix", "-i", "4", "-ci", "-"])
.write_stdin(source)
@ -271,7 +269,7 @@ mod tests {
let opts = dbg!(&opts()[i]);
let source = Powershell(opts).render().unwrap();
Command::new("pwsh")
.args(&["-Command", &source, "-NoLogo", "-NonInteractive", "-NoProfile"])
.args(&["-NoLogo", "-NonInteractive", "-NoProfile", "-Command", &source])
.assert()
.success()
.stdout("")
@ -316,14 +314,19 @@ mod tests {
}
#[test]
// Xonsh complains about type-hinting here, although it works fine in practice.
// <https://github.com/xonsh/xonsh/issues/3959>
#[ignore]
fn xonsh_xonsh_#i() {
let opts = dbg!(&opts()[i]);
let source = Xonsh(opts).render().unwrap();
// We can't pass the source directly to `xonsh -c` due to
// a bug: <https://github.com/xonsh/xonsh/issues/3959>
Command::new("xonsh")
.args(&["-c", &source, "--no-rc"])
.args(&[
"-c",
"import sys; execx(sys.stdin.read(), 'exec', __xonsh__.ctx, filename='zoxide')",
"--no-rc"
])
.write_stdin(source.as_bytes())
.assert()
.success()
.stdout("")
@ -360,5 +363,5 @@ mod tests {
}
}
with_opts_size!(generate_tests);
with_opts_size!(make_tests);
}

71
tests/completion.rs Normal file
View File

@ -0,0 +1,71 @@
//! Syntax checking for auto-generated shell completions.
#![cfg(feature = "shell_tests")]
use assert_cmd::Command;
#[test]
fn completions_bash() {
let source = include_str!("../contrib/completions/zoxide.bash");
Command::new("bash")
.args(&["--noprofile", "--norc", "-c", source])
.assert()
.success()
.stdout("")
.stderr("");
}
// Elvish: the completions file uses editor commands to add completions to the
// shell. However, Elvish does not support running editor commands from a
// script, so we can't create a test for this.
// <https://github.com/elves/elvish/issues/1299>
#[test]
fn completions_fish() {
let source = include_str!("../contrib/completions/zoxide.fish");
let tempdir = tempfile::tempdir().unwrap();
let tempdir = tempdir.path().to_str().unwrap();
Command::new("fish")
.env("HOME", tempdir)
.args(&["--command", source, "--private"])
.assert()
.success()
.stdout("")
.stderr("");
}
#[test]
fn completions_powershell() {
let source = include_str!("../contrib/completions/_zoxide.ps1");
Command::new("pwsh")
.args(&[
"-NoLogo",
"-NonInteractive",
"-NoProfile",
"-Command",
source,
])
.assert()
.success()
.stdout("")
.stderr("");
}
#[test]
fn completions_zsh() {
let source = r#"
set -eu
completions='./contrib/completions'
test -d "$completions"
fpath=("$completions" $fpath)
autoload -Uz compinit
compinit -u
"#;
Command::new("zsh")
.args(&["-c", source, "--no-rcs"])
.assert()
.success()
.stdout("")
.stderr("");
}