Skip to content

Commit

Permalink
Feature/improved testing (#1)
Browse files Browse the repository at this point in the history
* update README
* simplify tests
  • Loading branch information
domsleee authored Mar 10, 2023
1 parent 68af408 commit 26c5258
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 48 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

Blazing fast tab completion for powershell.

![demo](media/demo.gif)

Features:
* Fast startup and execution using `nu-engine`
* Fast startup and execution using [nushell/nu-engine](https://crates.io/crates/nu-engine)
* Extendable using `.nu` files, with built in support for commmon tasks like `git` and `npm run`

By default, [completions.nu](./resource/completions.nu) is used. An alternative `.nu` file can be specified in the `TABCOMPLETE_FILE` environment variable.
Expand All @@ -27,6 +29,7 @@ Invoke-Expression (&tabcomplete init | Get-Content)
[crates.io]: https://crates.io/crates/starship

## Built in completions
The completions packaged with the binary in [completions.nu](./resource/completions.nu) are:
* [git completions](https://github.com/nushell/nu_scripts/blob/main/custom-completions/git/git-completions.nu). These are also combined with [git auto generated completions](https://github.com/nushell/nu_scripts/blob/main/custom-completions/auto-generate/completions/git.nu)
* [npm completions](https://github.com/nushell/nu_scripts/blob/main/custom-completions/npm/npm-completions.nu)
* [cargo completions](https://github.com/nushell/nu_scripts/blob/main/custom-completions/cargo/cargo-completions.nu)
Expand All @@ -39,7 +42,7 @@ Benchmark | Results
`benchmark/complete` - tab completion (100 branches) | tabcomplete: 71ms, posh-git: 172ms (2.42x faster)

## Aliases / Function support
Functions are supported. For example, this would add completion for `gco `, as if it was `git checkout `:
Functions are supported. For example, the completion of `gco` in the demo is:
```pwsh
function gco() { git checkout $args }
```
Expand Down
Binary file added media/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 0 additions & 6 deletions tests/test_command_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ use rstest_reuse::{self, *};
mod testenv;
pub use testenv::*;

#[template]
#[rstest]
#[case("pwsh")]
#[cfg_attr(windows, case("powershell"))]
pub fn shell_to_use(#[case] shell: &str) {}

#[apply(shell_to_use)]
fn test_command_names(shell: &str) {
let testenv = TestEnv::new(shell);
Expand Down
6 changes: 0 additions & 6 deletions tests/test_complete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ use rstest_reuse::{self, *};
mod testenv;
pub use testenv::*;

#[template]
#[rstest]
#[case("pwsh")]
#[cfg_attr(windows, case("powershell"))]
pub fn shell_to_use(#[case] shell: &str) {}

#[apply(shell_to_use)]
fn test_checkout_branches(shell: &str) {
let testenv = TestEnv::new(shell);
Expand Down
6 changes: 0 additions & 6 deletions tests/test_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@ use rstest_reuse::{self, *};
use std::io::BufRead;
pub use testenv::*;

#[template]
#[rstest]
#[case("pwsh")]
#[cfg_attr(windows, case("powershell"))]
pub fn shell_to_use(#[case] shell: &str) {}

#[apply(shell_to_use)]
fn test_init(shell: &str) {
let testenv = TestEnv::new(shell);
Expand Down
36 changes: 36 additions & 0 deletions tests/test_testenv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
mod testenv;
use std::{fs, io::BufRead, path::PathBuf};

use itertools::Itertools;
pub use testenv::*;

#[test]
pub fn test_path_is_set() {
let testenv = TestEnv::new("pwsh");
let res = testenv.run_with_profile("(gcm tabcomplete).Path").unwrap();
let lines = res.stdout.lines().map(|x| x.unwrap()).collect_vec();

let bin_invoked_path = fs::canonicalize(&lines[0]).expect("invoked path");
let binding = PathBuf::from(file!());
let project_path = binding
.as_path()
.parent()
.expect("tests directory")
.parent()
.expect("rootdirectory");
let expected_bin_path = project_path.join("target").join("debug").join(format!(
"tabcomplete{}",
if cfg!(windows) { ".exe" } else { "" }
));

let expected_bin = fs::canonicalize(expected_bin_path).expect("bin path should exist");
assert_eq!(expected_bin, bin_invoked_path);
}

#[test]
pub fn test_git_is_defined() {
let testenv = TestEnv::new("pwsh");
testenv
.run_with_profile("git -h")
.expect("git should be defined");
}
57 changes: 29 additions & 28 deletions tests/testenv/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
#![allow(unused_macros)] // FIXME
use posh_tabcomplete::TABCOMPLETE_FILE;
use regex::Regex;
use rstest_reuse::*;
use std::{
env, fs,
env,
ffi::OsString,
fs,
io::{self, Write},
path::PathBuf,
process::{Command, Output},
};
use tempfile::TempDir;

const PATH: &str = "PATH";

#[template]
#[rstest]
#[case("pwsh")]
#[cfg_attr(windows, case("powershell"))]
pub fn shell_to_use(#[case] shell: &str) {}

pub struct TestEnv {
pub shell: String,
pub temp_dir: TempDir,
Expand Down Expand Up @@ -71,11 +82,15 @@ impl TestEnv {
let file_contents = format!(". {}\n{command}", self.profile_path.to_str().unwrap());
let file_path = root.join("file.ps1");
fs::File::create(&file_path)?.write_all(file_contents.as_bytes())?;
let target_dir = debug_target_dir();
let paths_var = prepend_to_path_var(&target_dir);

println!("target_dir {target_dir:?}");
let output = Command::new(&self.shell)
.arg("-NoProfile")
.arg("-File")
.arg(file_path.to_str().unwrap())
.env(PATH, paths_var)
.current_dir(root)
.output()
.expect("failed to execute");
Expand All @@ -87,6 +102,17 @@ impl TestEnv {
}
}

fn prepend_to_path_var(path: &PathBuf) -> OsString {
let current_path = env::var_os(PATH).expect("PATH must be defined");
let split_paths = env::split_paths(&current_path);
let mut new_paths = vec![path.clone()];
for existing_path in split_paths {
new_paths.push(existing_path);
}

env::join_paths(&new_paths).expect("can join")
}

fn create_working_dir(profile_prefix_data: Vec<&str>) -> Result<(TempDir, PathBuf), io::Error> {
let temp_dir = tempfile::Builder::new()
.prefix("tabcomplete-tests")
Expand All @@ -95,22 +121,9 @@ fn create_working_dir(profile_prefix_data: Vec<&str>) -> Result<(TempDir, PathBu
let profile_path = {
let root = temp_dir.path();
println!("root: {root:?}");
let exe = find_exe();
let output = Command::new(&exe)
.arg("init")
.output()
.expect("failed to execute");
let mut init_str = profile_prefix_data.join("\n\n");
init_str.push('\n');
init_str.push_str(&String::from_utf8(output.stdout).expect("parsed as utf8"));
let re1 = Regex::new(r"tabcomplete complete").unwrap();
init_str = re1
.replace(&init_str, format!("{exe:?} complete"))
.to_string();
let re2 = Regex::new(r"tabcomplete nu\-commands").unwrap();
init_str = re2
.replace(&init_str, format!("{exe:?} nu-commands"))
.to_string();
init_str.push_str("Invoke-Expression (&tabcomplete init | Out-String)");
let profile_path = root.join("tabcompleteProfile.ps1");
fs::File::create(&profile_path)?.write_all(init_str.as_bytes())?;

Expand Down Expand Up @@ -140,18 +153,6 @@ fn create_working_dir(profile_prefix_data: Vec<&str>) -> Result<(TempDir, PathBu
Ok((temp_dir, profile_path))
}

fn find_exe() -> PathBuf {
let root = debug_target_dir();

let exe_name = if cfg!(windows) {
"tabcomplete.exe"
} else {
"tabcomplete"
};

root.join(exe_name)
}

fn debug_target_dir() -> PathBuf {
// Tests exe is in target/debug/deps, tabcomplete.exe is in `target`
let target_dir = env::current_exe()
Expand Down

0 comments on commit 26c5258

Please sign in to comment.