test: introduce env variable mocking (#1490)

This commit is contained in:
Tilmann Meyer 2020-08-07 21:13:12 +02:00 committed by GitHub
parent 8b0f589486
commit 88b603be38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
87 changed files with 4619 additions and 4688 deletions

View File

@ -125,12 +125,6 @@ jobs:
profile: minimal profile: minimal
override: true override: true
# Install dotnet at a fixed version
- name: Setup | DotNet
uses: actions/setup-dotnet@v1
with:
dotnet-version: "2.2.402"
# Install Mercurial (pre-installed on Linux and windows) # Install Mercurial (pre-installed on Linux and windows)
- name: Setup | Mercurial (macos) - name: Setup | Mercurial (macos)
if: matrix.os == 'macOS-latest' if: matrix.os == 'macOS-latest'

View File

@ -24,6 +24,52 @@ The project begins in [`main.rs`](src/main.rs), where the appropriate `print::`
Any styling that is applied to a module is inherited by its segments. Module prefixes and suffixes by default don't have any styling applied to them. Any styling that is applied to a module is inherited by its segments. Module prefixes and suffixes by default don't have any styling applied to them.
## Environment Variables and external commands
We have custom functions to be able to test our modules better. Here we show you how.
### Environment Variables
To get an environment variable we have special function to allow for mocking of vars. Here's a quick example:
```rust
use super::{Context, Module, RootModuleConfig};
use crate::configs::php::PhpConfig;
use crate::formatter::StringFormatter;
use crate::utils;
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
// Here `my_env_var` will be either the contents of the var or the function
// will exit if the variable is not set.
let my_env_var = context.get_env("MY_VAR")?;
// Then you can happily use the value
}
```
## External commands
To run a external command (e.g. to get the version of a tool) and to allow for mocking use the `utils::exec_cmd` function. Here's a quick example:
```rust
use super::{Context, Module, RootModuleConfig};
use crate::configs::php::PhpConfig;
use crate::formatter::StringFormatter;
use crate::utils;
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
// Here `my_env_var` will be either the stdout of the called command or the function
// will exit if the called program was not installed or could not be run.
let output = utils::exec_cmd("my_command", &["first_arg", "second_arg"])?.stdout;
// Then you can happily use the output
}
```
## Logging ## Logging
Debug logging in starship is done with [pretty_env_logger](https://crates.io/crates/pretty_env_logger). Debug logging in starship is done with [pretty_env_logger](https://crates.io/crates/pretty_env_logger).
@ -56,37 +102,81 @@ rustup component add rustfmt
cargo fmt cargo fmt
``` ```
## Testing ## Testing
Testing is critical to making sure starship works as intended on systems big and small. Starship interfaces with many applications and system APIs when generating the prompt, so there's a lot of room for bugs to slip in. Testing is critical to making sure starship works as intended on systems big and small. Starship interfaces with many applications and system APIs when generating the prompt, so there's a lot of room for bugs to slip in.
Unit tests and a subset of integration tests can be run with `cargo test`. Unit tests are written using the built-in Rust testing library in the same file as the implementation, as is traditionally done in Rust codebases. These tests can be run with `cargo test` and are run on GitHub as part of our GitHub Actions continuous integration to ensure consistend behavior.
The full integration test suite is run on GitHub as part of our GitHub Actions continuous integration.
### Unit Testing All tests that test the rendered output of a module should use `ModuleRenderer`. For Example:
Unit tests are written using the built-in Rust testing library in the same file as the implementation, as is traditionally done in Rust codebases. These tests can be run with `cargo test`. ```rust
use super::{Context, Module, RootModuleConfig};
use crate::configs::php::PhpConfig;
use crate::formatter::StringFormatter;
use crate::utils;
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
/* This is where your module code goes */
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test::ModuleRenderer;
use ansi_term::Color;
use std::fs::File;
use std::io;
#[test]
fn should_render() -> io::Result<()> {
// Here you setup the testing environment
let tempdir = tempfile::tempdir()?;
// Create some file needed to render the module
File::create(dir.path().join("YOUR_FILE"))?.sync_all()?;
// The output of the module
let actual = ModuleRenderer::new("YOUR_MODULE_NAME")
// For a custom path
.path(&tempdir.path())
// For a custom config
.config(toml::toml!{
[YOUR_MODULE_NAME]
val = 1
})
// For env mocking
.env("KEY","VALUE")
// Run the module and collect the output
.collect();
// The value that should be rendered by the module.
let expected = Some(format!("{} ",Color::Black.paint("THIS SHOULD BE RENDERED")));
// Assert that the actual and expected values are the same
assert_eq!(actual, expected);
// Close the tempdir
tempdir.close()
}
}
```
If a module depends on output of another program, then that output should be added to the match statement in [`utils.rs`](src/utils.rs). The match has to be exactly the same as the call to `utils::exec_cmd()`, including positional arguments and flags. The array of arguments are joined by a `" "`, so `utils::exec_cmd("program", &["arg", "more_args"])` would match with the `program arg more_args` match statement.
If the program cannot be mocked (e.g. It performs some filesystem operations, either writing or reading files) then it has to added to the project's GitHub Actions workflow file([`.github/workflows/workflow.yml`](.github/workflows/workflow.yml)) and the test has to be marked with an `#[ignored]`. This ensures that anyone can run the test suite locally without needing to pre-configure their environment. The `#[ignored]` attribute is bypassed during CI runs in GitHub Actions.
Unit tests should be fully isolated, only testing a given function's expected output given a specific input, and should be reproducible on any machine. Unit tests should not expect the computer running them to be in any particular state. This includes having any applications pre-installed, having any environment variables set, etc. Unit tests should be fully isolated, only testing a given function's expected output given a specific input, and should be reproducible on any machine. Unit tests should not expect the computer running them to be in any particular state. This includes having any applications pre-installed, having any environment variables set, etc.
The previous point should be emphasized: even seemingly innocuous ideas like "if we can see the directory, we can read it" or "nobody will have their home directory be a git repo" have bitten us in the past. Having even a single test fail can completely break installation on some platforms, so be careful with tests! The previous point should be emphasized: even seemingly innocuous ideas like "if we can see the directory, we can read it" or "nobody will have their home directory be a git repo" have bitten us in the past. Having even a single test fail can completely break installation on some platforms, so be careful with tests!
### Integration Testing
Integration tests are located in the [`tests/`](tests) directory and are also written using the built-in Rust testing library.
Integration tests should test full modules or the entire prompt. All integration tests that expect the testing environment to have pre-existing state or tests that make permanent changes to the filesystem should have the `#[ignore]` attribute added to them. All tests that don't depend on any preexisting state will be run alongside the unit tests with `cargo test`.
For tests that depend on having preexisting state, whatever needed state will have to be added to the project's GitHub Actions workflow file([`.github/workflows/workflow.yml`](.github/workflows/workflow.yml)).
### Test Programming Guidelines ### Test Programming Guidelines
Any tests that depend on File I/O should use [`sync_all()`](https://doc.rust-lang.org/std/fs/struct.File.html#method.sync_all) when creating files or after writing to files. Any tests that depend on File I/O should use [`sync_all()`](https://doc.rust-lang.org/std/fs/struct.File.html#method.sync_all) when creating files or after writing to files.
Any tests that use `tempfile::tempdir` should take care to call `dir.close()` after usage to ensure the lifecycle of the directory can be reasoned about. Any tests that use `tempfile::tempdir` should take care to call `dir.close()` after usage to ensure the lifecycle of the directory can be reasoned about. This includes `fixture_repo()` as it returns a TempDir that should be closed.
Any tests that use `create_fixture_repo()` should remove the returned directory after usage with `remove_dir_all::remove_dir_all()`.
## Running the Documentation Website Locally ## Running the Documentation Website Locally
@ -98,17 +188,20 @@ After cloning the project, you can do the following to run the VuePress website
1. `cd` into the `/docs` directory. 1. `cd` into the `/docs` directory.
2. Install the project dependencies: 2. Install the project dependencies:
```
$ npm install ```sh
``` npm install
```
3. Start the project in development mode: 3. Start the project in development mode:
```
$ npm run dev
```
Once setup is complete, you can refer to VuePress documentation on the actual implementation here: https://vuepress.vuejs.org/guide/. ```sh
npm run dev
```
### Git/GitHub workflow Once setup is complete, you can refer to VuePress documentation on the actual implementation here: <https://vuepress.vuejs.org/guide/>.
## Git/GitHub workflow
This is our preferred process for opening a PR on GitHub: This is our preferred process for opening a PR on GitHub:

1
Cargo.lock generated
View File

@ -1104,7 +1104,6 @@ dependencies = [
"quick-xml", "quick-xml",
"rayon", "rayon",
"regex", "regex",
"remove_dir_all",
"serde_json", "serde_json",
"starship_module_config_derive", "starship_module_config_derive",
"sysinfo", "sysinfo",

View File

@ -31,7 +31,7 @@ tls-vendored = ["native-tls/vendored"]
clap = "2.33.1" clap = "2.33.1"
ansi_term = "0.12.1" ansi_term = "0.12.1"
dirs-next = "1.0.1" dirs-next = "1.0.1"
git2 = { version = "0.13.8", default-features = false, features = [] } git2 = { version = "0.13.8", default-features = false }
toml = { version = "0.5.6", features = ["preserve_order"] } toml = { version = "0.5.6", features = ["preserve_order"] }
serde_json = "1.0.57" serde_json = "1.0.57"
rayon = "1.3.1" rayon = "1.3.1"
@ -64,17 +64,19 @@ attohttpc = { version = "0.15.0", optional = true, default-features = false, fea
native-tls = { version = "0.2", optional = true } native-tls = { version = "0.2", optional = true }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winuser", "securitybaseapi", "processthreadsapi", "handleapi", "impl-default"]} winapi = { version = "0.3", features = [
"winuser",
"securitybaseapi",
"processthreadsapi",
"handleapi",
"impl-default",
] }
[target.'cfg(not(windows))'.dependencies] [target.'cfg(not(windows))'.dependencies]
nix = "0.18.0" nix = "0.18.0"
[dev-dependencies] [dev-dependencies]
tempfile = "3.1.0" tempfile = "3.1.0"
# More realiable than std::fs version on Windows
# For removing temporary directories manually when needed
# This is what tempfile uses to delete temporary directories
remove_dir_all = "0.5.3"
[profile.release] [profile.release]
codegen-units = 1 codegen-units = 1

View File

@ -1,5 +1,6 @@
use crate::utils::exec_cmd; use crate::utils::exec_cmd;
use clap::crate_version;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
@ -254,5 +255,6 @@ mod tests {
let config_path = get_config_path("bash"); let config_path = get_config_path("bash");
assert_eq!("/test/home/.bashrc", config_path.unwrap().to_str().unwrap()); assert_eq!("/test/home/.bashrc", config_path.unwrap().to_str().unwrap());
env::remove_var("HOME");
} }
} }

View File

@ -7,6 +7,7 @@ use git2::{ErrorCode::UnbornBranch, Repository, RepositoryState};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::env; use std::env;
use std::ffi::OsString;
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::string::String; use std::string::String;
@ -33,6 +34,9 @@ pub struct Context<'a> {
/// The shell the user is assumed to be running /// The shell the user is assumed to be running
pub shell: Shell, pub shell: Shell,
/// A HashMap of environment variable mocks
pub env: HashMap<&'a str, String>,
} }
impl<'a> Context<'a> { impl<'a> Context<'a> {
@ -82,11 +86,30 @@ impl<'a> Context<'a> {
dir_contents: OnceCell::new(), dir_contents: OnceCell::new(),
repo: OnceCell::new(), repo: OnceCell::new(),
shell, shell,
env: HashMap::new(),
}
}
// Retrives a environment variable from the os or from a table if in testing mode
pub fn get_env<K: AsRef<str>>(&self, key: K) -> Option<String> {
if cfg!(test) {
self.env.get(key.as_ref()).map(|val| val.to_string())
} else {
env::var(key.as_ref()).ok()
}
}
// Retrives a environment variable from the os or from a table if in testing mode (os version)
pub fn get_env_os<K: AsRef<str>>(&self, key: K) -> Option<OsString> {
if cfg!(test) {
self.env.get(key.as_ref()).map(OsString::from)
} else {
env::var_os(key.as_ref())
} }
} }
/// Convert a `~` in a path to the home directory /// Convert a `~` in a path to the home directory
fn expand_tilde(dir: PathBuf) -> PathBuf { pub fn expand_tilde(dir: PathBuf) -> PathBuf {
if dir.starts_with("~") { if dir.starts_with("~") {
let without_home = dir.strip_prefix("~").unwrap(); let without_home = dir.strip_prefix("~").unwrap();
return dirs_next::home_dir().unwrap().join(without_home); return dirs_next::home_dir().unwrap().join(without_home);
@ -165,7 +188,7 @@ impl<'a> Context<'a> {
} }
fn get_shell() -> Shell { fn get_shell() -> Shell {
let shell = std::env::var("STARSHIP_SHELL").unwrap_or_default(); let shell = env::var("STARSHIP_SHELL").unwrap_or_default();
match shell.as_str() { match shell.as_str() {
"bash" => Shell::Bash, "bash" => Shell::Bash,
"fish" => Shell::Fish, "fish" => Shell::Fish,
@ -310,7 +333,7 @@ impl<'a> ScanDir<'a> {
self self
} }
/// based on the current Pathbuf check to see /// based on the current PathBuf check to see
/// if any of this criteria match or exist and returning a boolean /// if any of this criteria match or exist and returning a boolean
pub fn is_match(&self) -> bool { pub fn is_match(&self) -> bool {
self.dir_contents.has_any_extension(self.extensions) self.dir_contents.has_any_extension(self.extensions)

View File

@ -1,4 +1,5 @@
use pest::{error::Error, iterators::Pair, Parser}; use pest::{error::Error, iterators::Pair, Parser};
use pest_derive::*;
use super::model::*; use super::model::*;

View File

@ -1,6 +1,3 @@
#[macro_use]
extern crate pest_derive;
// Lib is present to allow for benchmarking // Lib is present to allow for benchmarking
pub mod config; pub mod config;
pub mod configs; pub mod configs;
@ -11,3 +8,6 @@ pub mod modules;
pub mod print; pub mod print;
pub mod segment; pub mod segment;
mod utils; mod utils;
#[cfg(test)]
mod test;

View File

@ -1,11 +1,7 @@
use clap::{crate_authors, crate_version};
use std::io; use std::io;
use std::time::SystemTime; use std::time::SystemTime;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate pest_derive;
mod bug_report; mod bug_report;
mod config; mod config;
mod configs; mod configs;
@ -19,6 +15,9 @@ mod print;
mod segment; mod segment;
mod utils; mod utils;
#[cfg(test)]
mod test;
use crate::module::ALL_MODULES; use crate::module::ALL_MODULES;
use clap::{App, AppSettings, Arg, Shell, SubCommand}; use clap::{App, AppSettings, Arg, Shell, SubCommand};

View File

@ -1,5 +1,4 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::env;
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use std::path::PathBuf; use std::path::PathBuf;
@ -13,9 +12,9 @@ use crate::formatter::StringFormatter;
type Profile = String; type Profile = String;
type Region = String; type Region = String;
fn get_aws_region_from_config(aws_profile: Option<&str>) -> Option<Region> { fn get_aws_region_from_config(context: &Context, aws_profile: Option<&str>) -> Option<Region> {
let config_location = env::var("AWS_CONFIG_FILE") let config_location = context
.ok() .get_env("AWS_CONFIG_FILE")
.and_then(|path| PathBuf::from_str(&path).ok()) .and_then(|path| PathBuf::from_str(&path).ok())
.or_else(|| { .or_else(|| {
let mut home = dirs_next::home_dir()?; let mut home = dirs_next::home_dir()?;
@ -47,19 +46,22 @@ fn get_aws_region_from_config(aws_profile: Option<&str>) -> Option<Region> {
Some(region.to_string()) Some(region.to_string())
} }
fn get_aws_profile_and_region() -> (Option<Profile>, Option<Region>) { fn get_aws_profile_and_region(context: &Context) -> (Option<Profile>, Option<Region>) {
match ( match (
env::var("AWS_VAULT") context
.or_else(|_| env::var("AWS_PROFILE")) .get_env("AWS_VAULT")
.ok(), .or_else(|| context.get_env("AWS_PROFILE")),
env::var("AWS_DEFAULT_REGION") context
.or_else(|_| env::var("AWS_REGION")) .get_env("AWS_DEFAULT_REGION")
.ok(), .or_else(|| context.get_env("AWS_REGION")),
) { ) {
(Some(p), Some(r)) => (Some(p), Some(r)), (Some(p), Some(r)) => (Some(p), Some(r)),
(None, Some(r)) => (None, Some(r)), (None, Some(r)) => (None, Some(r)),
(Some(ref p), None) => (Some(p.to_owned()), get_aws_region_from_config(Some(p))), (Some(ref p), None) => (
(None, None) => (None, get_aws_region_from_config(None)), Some(p.to_owned()),
get_aws_region_from_config(context, Some(p)),
),
(None, None) => (None, get_aws_region_from_config(context, None)),
} }
} }
@ -74,7 +76,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let mut module = context.new_module("aws"); let mut module = context.new_module("aws");
let config: AwsConfig = AwsConfig::try_load(module.config); let config: AwsConfig = AwsConfig::try_load(module.config);
let (aws_profile, aws_region) = get_aws_profile_and_region(); let (aws_profile, aws_region) = get_aws_profile_and_region(context);
if aws_profile.is_none() && aws_region.is_none() { if aws_profile.is_none() && aws_region.is_none() {
return None; return None;
} }
@ -113,3 +115,278 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
#[cfg(test)]
mod tests {
use crate::test::ModuleRenderer;
use ansi_term::Color;
use std::fs::File;
use std::io::{self, Write};
#[test]
fn no_region_set() -> io::Result<()> {
let actual = ModuleRenderer::new("aws").collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn region_set() -> io::Result<()> {
let actual = ModuleRenderer::new("aws")
.env("AWS_REGION", "ap-northeast-2")
.collect();
let expected = Some(format!(
"on {} ",
Color::Yellow.bold().paint("☁️ (ap-northeast-2)")
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn region_set_with_alias() -> io::Result<()> {
let actual = ModuleRenderer::new("aws")
.env("AWS_REGION", "ap-southeast-2")
.config(toml::toml! {
[aws.region_aliases]
ap-southeast-2 = "au"
})
.collect();
let expected = Some(format!("on {} ", Color::Yellow.bold().paint("☁️ (au)")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn default_region_set() -> io::Result<()> {
let actual = ModuleRenderer::new("aws")
.env("AWS_REGION", "ap-northeast-2")
.env("AWS_DEFAULT_REGION", "ap-northeast-1")
.collect();
let expected = Some(format!(
"on {} ",
Color::Yellow.bold().paint("☁️ (ap-northeast-1)")
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn profile_set() -> io::Result<()> {
let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts")
.collect();
let expected = Some(format!(
"on {} ",
Color::Yellow.bold().paint("☁️ astronauts")
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn profile_set_from_aws_vault() -> io::Result<()> {
let actual = ModuleRenderer::new("aws")
.env("AWS_VAULT", "astronauts-vault")
.env("AWS_PROFILE", "astronauts-profile")
.collect();
let expected = Some(format!(
"on {} ",
Color::Yellow.bold().paint("☁️ astronauts-vault")
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn profile_and_region_set() -> io::Result<()> {
let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts")
.env("AWS_REGION", "ap-northeast-2")
.collect();
let expected = Some(format!(
"on {} ",
Color::Yellow.bold().paint("☁️ astronauts(ap-northeast-2)")
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn default_profile_set() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let config_path = dir.path().join("config");
let mut file = File::create(&config_path)?;
file.write_all(
"[default]
region = us-east-1
[profile astronauts]
region = us-east-2
"
.as_bytes(),
)?;
let actual = ModuleRenderer::new("aws")
.env("AWS_CONFIG_FILE", config_path.to_string_lossy().as_ref())
.collect();
let expected = Some(format!(
"on {} ",
Color::Yellow.bold().paint("☁️ (us-east-1)")
));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn profile_and_config_set() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let config_path = dir.path().join("config");
let mut file = File::create(&config_path)?;
file.write_all(
"[default]
region = us-east-1
[profile astronauts]
region = us-east-2
"
.as_bytes(),
)?;
let actual = ModuleRenderer::new("aws")
.env("AWS_CONFIG_FILE", config_path.to_string_lossy().as_ref())
.env("AWS_PROFILE", "astronauts")
.config(toml::toml! {
[aws]
})
.collect();
let expected = Some(format!(
"on {} ",
Color::Yellow.bold().paint("☁️ astronauts(us-east-2)")
));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn profile_and_region_set_with_display_all() -> io::Result<()> {
let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts")
.env("AWS_REGION", "ap-northeast-1")
.collect();
let expected = Some(format!(
"on {} ",
Color::Yellow.bold().paint("☁️ astronauts(ap-northeast-1)")
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn profile_set_with_display_all() -> io::Result<()> {
let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts")
.collect();
let expected = Some(format!(
"on {} ",
Color::Yellow.bold().paint("☁️ astronauts")
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn region_set_with_display_all() -> io::Result<()> {
let actual = ModuleRenderer::new("aws")
.env("AWS_REGION", "ap-northeast-1")
.collect();
let expected = Some(format!(
"on {} ",
Color::Yellow.bold().paint("☁️ (ap-northeast-1)")
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn profile_and_region_set_with_display_region() -> io::Result<()> {
let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts")
.env("AWS_DEFAULT_REGION", "ap-northeast-1")
.config(toml::toml! {
[aws]
format = "on [$symbol$region]($style) "
})
.collect();
let expected = Some(format!(
"on {} ",
Color::Yellow.bold().paint("☁️ ap-northeast-1")
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn profile_and_region_set_with_display_profile() -> io::Result<()> {
let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts")
.env("AWS_REGION", "ap-northeast-1")
.config(toml::toml! {
[aws]
format = "on [$symbol$profile]($style) "
})
.collect();
let expected = Some(format!(
"on {} ",
Color::Yellow.bold().paint("☁️ astronauts")
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn region_set_with_display_profile() -> io::Result<()> {
let actual = ModuleRenderer::new("aws")
.env("AWS_REGION", "ap-northeast-1")
.config(toml::toml! {
[aws]
format = "on [$symbol$profile]($style) "
})
.collect();
let expected = Some(format!("on {} ", Color::Yellow.bold().paint("☁️ ")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn region_not_set_with_display_region() -> io::Result<()> {
let actual = ModuleRenderer::new("aws")
.config(toml::toml! {
[aws]
format = "on [$symbol$region]($style) "
})
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
}

View File

@ -68,3 +68,142 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
#[cfg(test)]
mod test {
use crate::context::Shell;
use crate::test::ModuleRenderer;
use ansi_term::Color;
use std::io;
#[test]
fn success_status() -> io::Result<()> {
let expected = Some(format!("{} ", Color::Green.bold().paint("")));
// Status code 0
let actual = ModuleRenderer::new("character").status(0).collect();
assert_eq!(expected, actual);
// No status code
let actual = ModuleRenderer::new("character").collect();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn failure_status() -> io::Result<()> {
let expected = Some(format!("{} ", Color::Red.bold().paint("")));
let exit_values = [1, 54321, -5000];
for status in &exit_values {
let actual = ModuleRenderer::new("character").status(*status).collect();
assert_eq!(expected, actual);
}
Ok(())
}
#[test]
fn custom_symbol() -> io::Result<()> {
let expected_fail = Some(format!("{} ", Color::Red.bold().paint("")));
let expected_success = Some(format!("{} ", Color::Green.bold().paint("")));
let exit_values = [1, 54321, -5000];
// Test failure values
for status in &exit_values {
let actual = ModuleRenderer::new("character")
.config(toml::toml! {
[character]
success_symbol = "[➜](bold green)"
error_symbol = "[✖](bold red)"
})
.status(*status)
.collect();
assert_eq!(expected_fail, actual);
}
// Test success
let actual = ModuleRenderer::new("character")
.config(toml::toml! {
[character]
success_symbol = "[➜](bold green)"
error_symbol = "[✖](bold red)"
})
.status(0)
.collect();
assert_eq!(expected_success, actual);
Ok(())
}
#[test]
fn zsh_keymap() -> io::Result<()> {
let expected_vicmd = Some(format!("{} ", Color::Green.bold().paint("")));
let expected_specified = Some(format!("{} ", Color::Green.bold().paint("V")));
let expected_other = Some(format!("{} ", Color::Green.bold().paint("")));
// zle keymap is vicmd
let actual = ModuleRenderer::new("character")
.shell(Shell::Zsh)
.keymap("vicmd")
.collect();
assert_eq!(expected_vicmd, actual);
// specified vicmd character
let actual = ModuleRenderer::new("character")
.config(toml::toml! {
[character]
vicmd_symbol = "[V](bold green)"
})
.shell(Shell::Zsh)
.keymap("vicmd")
.collect();
assert_eq!(expected_specified, actual);
// zle keymap is other
let actual = ModuleRenderer::new("character")
.shell(Shell::Zsh)
.keymap("visual")
.collect();
assert_eq!(expected_other, actual);
Ok(())
}
#[test]
fn fish_keymap() -> io::Result<()> {
let expected_vicmd = Some(format!("{} ", Color::Green.bold().paint("")));
let expected_specified = Some(format!("{} ", Color::Green.bold().paint("V")));
let expected_other = Some(format!("{} ", Color::Green.bold().paint("")));
// fish keymap is default
let actual = ModuleRenderer::new("character")
.shell(Shell::Fish)
.keymap("default")
.collect();
assert_eq!(expected_vicmd, actual);
// specified vicmd character
let actual = ModuleRenderer::new("character")
.config(toml::toml! {
[character]
vicmd_symbol = "[V](bold green)"
})
.shell(Shell::Fish)
.keymap("default")
.collect();
assert_eq!(expected_specified, actual);
// fish keymap is other
let actual = ModuleRenderer::new("character")
.shell(Shell::Fish)
.keymap("visual")
.collect();
assert_eq!(expected_other, actual);
Ok(())
}
}

View File

@ -58,7 +58,7 @@ fn format_cmake_version(cmake_version: &str) -> Option<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -66,7 +66,7 @@ mod tests {
#[test] #[test]
fn folder_without_cmake_lists() -> io::Result<()> { fn folder_without_cmake_lists() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("cmake", dir.path(), None); let actual = ModuleRenderer::new("cmake").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -76,7 +76,7 @@ mod tests {
fn folder_with_cmake_lists() -> io::Result<()> { fn folder_with_cmake_lists() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("CMakeLists.txt"))?.sync_all()?; File::create(dir.path().join("CMakeLists.txt"))?.sync_all()?;
let actual = render_module("cmake", dir.path(), None); let actual = ModuleRenderer::new("cmake").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Blue.bold().paint("🛆 v3.17.3"))); let expected = Some(format!("via {} ", Color::Blue.bold().paint("🛆 v3.17.3")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()

View File

@ -89,6 +89,9 @@ fn render_time_component((component, suffix): (&u128, &&str)) -> String {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::test::ModuleRenderer;
use ansi_term::Color;
use std::io;
#[test] #[test]
fn test_500ms() { fn test_500ms() {
@ -110,4 +113,86 @@ mod tests {
fn test_1d() { fn test_1d() {
assert_eq!(render_time(86_400_000 as u128, true), "1d") assert_eq!(render_time(86_400_000 as u128, true), "1d")
} }
#[test]
fn config_blank_duration_1s() -> io::Result<()> {
let actual = ModuleRenderer::new("cmd_duration")
.cmd_duration(1000)
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_blank_duration_5s() -> io::Result<()> {
let actual = ModuleRenderer::new("cmd_duration")
.cmd_duration(5000)
.collect();
let expected = Some(format!("took {} ", Color::Yellow.bold().paint("5s")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_5s_duration_3s() -> io::Result<()> {
let actual = ModuleRenderer::new("cmd_duration")
.config(toml::toml! {
[cmd_duration]
min_time = 5000
})
.cmd_duration(3000)
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_5s_duration_10s() -> io::Result<()> {
let actual = ModuleRenderer::new("cmd_duration")
.config(toml::toml! {
[cmd_duration]
min_time = 5000
})
.cmd_duration(10000)
.collect();
let expected = Some(format!("took {} ", Color::Yellow.bold().paint("10s")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_1s_duration_prefix_underwent() -> io::Result<()> {
let actual = ModuleRenderer::new("cmd_duration")
.config(toml::toml! {
[cmd_duration]
format = "underwent [$duration]($style) "
})
.cmd_duration(1000)
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_5s_duration_prefix_underwent() -> io::Result<()> {
let actual = ModuleRenderer::new("cmd_duration")
.config(toml::toml! {
[cmd_duration]
format = "underwent [$duration]($style) "
})
.cmd_duration(5000)
.collect();
let expected = Some(format!("underwent {} ", Color::Yellow.bold().paint("5s")));
assert_eq!(expected, actual);
Ok(())
}
} }

View File

@ -1,5 +1,3 @@
use std::env;
use super::{Context, Module, RootModuleConfig}; use super::{Context, Module, RootModuleConfig};
use super::utils::directory::truncate; use super::utils::directory::truncate;
@ -11,7 +9,9 @@ use crate::formatter::StringFormatter;
/// Will display the Conda environment iff `$CONDA_DEFAULT_ENV` is set. /// Will display the Conda environment iff `$CONDA_DEFAULT_ENV` is set.
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
// Reference implementation: https://github.com/denysdovhan/spaceship-prompt/blob/master/sections/conda.zsh // Reference implementation: https://github.com/denysdovhan/spaceship-prompt/blob/master/sections/conda.zsh
let conda_env = env::var("CONDA_DEFAULT_ENV").unwrap_or_else(|_| "".into()); let conda_env = context
.get_env("CONDA_DEFAULT_ENV")
.unwrap_or_else(|| "".into());
if conda_env.trim().is_empty() { if conda_env.trim().is_empty() {
return None; return None;
} }
@ -52,3 +52,63 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
#[cfg(test)]
mod tests {
use crate::test::ModuleRenderer;
use ansi_term::Color;
use std::io;
#[test]
fn not_in_env() -> io::Result<()> {
let actual = ModuleRenderer::new("conda").collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn ignore_base() -> io::Result<()> {
let actual = ModuleRenderer::new("conda")
.env("CONDA_DEFAULT_ENV", "base")
.config(toml::toml! {
[conda]
ignore_base = true
})
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn env_set() -> io::Result<()> {
let actual = ModuleRenderer::new("conda")
.env("CONDA_DEFAULT_ENV", "astronauts")
.collect();
let expected = Some(format!(
"via {} ",
Color::Green.bold().paint("🅒 astronauts")
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn truncate() -> io::Result<()> {
let actual = ModuleRenderer::new("conda")
.env("CONDA_DEFAULT_ENV", "/some/really/long/and/really/annoying/path/that/shouldnt/be/displayed/fully/conda/my_env")
.collect();
let expected = Some(format!("via {} ", Color::Green.bold().paint("🅒 my_env")));
assert_eq!(expected, actual);
Ok(())
}
}

View File

@ -68,7 +68,7 @@ fn format_crystal_version(crystal_version: &str) -> Option<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -76,7 +76,7 @@ mod tests {
#[test] #[test]
fn folder_without_crystal_files() -> io::Result<()> { fn folder_without_crystal_files() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("crystal", dir.path(), None); let actual = ModuleRenderer::new("crystal").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -88,7 +88,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("shard.yml"))?.sync_all()?; File::create(dir.path().join("shard.yml"))?.sync_all()?;
let actual = render_module("crystal", dir.path(), None); let actual = ModuleRenderer::new("crystal").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Red.bold().paint("🔮 v0.35.1"))); let expected = Some(format!("via {} ", Color::Red.bold().paint("🔮 v0.35.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -100,7 +100,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.cr"))?.sync_all()?; File::create(dir.path().join("main.cr"))?.sync_all()?;
let actual = render_module("crystal", dir.path(), None); let actual = ModuleRenderer::new("crystal").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Red.bold().paint("🔮 v0.35.1"))); let expected = Some(format!("via {} ", Color::Red.bold().paint("🔮 v0.35.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);

View File

@ -68,7 +68,7 @@ fn parse_dart_version(dart_version: &str) -> Option<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::parse_dart_version; use super::parse_dart_version;
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io; use std::io;
@ -82,7 +82,7 @@ mod tests {
#[test] #[test]
fn folder_without_dart_file() -> io::Result<()> { fn folder_without_dart_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("dart", dir.path(), None); let actual = ModuleRenderer::new("dart").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -93,7 +93,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.dart"))?.sync_all()?; File::create(dir.path().join("any.dart"))?.sync_all()?;
let actual = render_module("dart", dir.path(), None); let actual = ModuleRenderer::new("dart").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Blue.bold().paint("🎯 v2.8.4"))); let expected = Some(format!("via {} ", Color::Blue.bold().paint("🎯 v2.8.4")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -104,7 +104,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
fs::create_dir_all(dir.path().join(".dart_tool"))?; fs::create_dir_all(dir.path().join(".dart_tool"))?;
let actual = render_module("dart", dir.path(), None); let actual = ModuleRenderer::new("dart").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Blue.bold().paint("🎯 v2.8.4"))); let expected = Some(format!("via {} ", Color::Blue.bold().paint("🎯 v2.8.4")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -115,7 +115,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("pubspec.yaml"))?.sync_all()?; File::create(dir.path().join("pubspec.yaml"))?.sync_all()?;
let actual = render_module("dart", dir.path(), None); let actual = ModuleRenderer::new("dart").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Blue.bold().paint("🎯 v2.8.4"))); let expected = Some(format!("via {} ", Color::Blue.bold().paint("🎯 v2.8.4")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -126,7 +126,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("pubspec.yml"))?.sync_all()?; File::create(dir.path().join("pubspec.yml"))?.sync_all()?;
let actual = render_module("dart", dir.path(), None); let actual = ModuleRenderer::new("dart").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Blue.bold().paint("🎯 v2.8.4"))); let expected = Some(format!("via {} ", Color::Blue.bold().paint("🎯 v2.8.4")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -137,7 +137,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("pubspec.lock"))?.sync_all()?; File::create(dir.path().join("pubspec.lock"))?.sync_all()?;
let actual = render_module("dart", dir.path(), None); let actual = ModuleRenderer::new("dart").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Blue.bold().paint("🎯 v2.8.4"))); let expected = Some(format!("via {} ", Color::Blue.bold().paint("🎯 v2.8.4")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()

View File

@ -18,9 +18,11 @@ use crate::formatter::StringFormatter;
/// Creates a module with the current directory /// Creates a module with the current directory
/// ///
/// Will perform path contraction, substitution, and truncation. /// Will perform path contraction, substitution, and truncation.
///
/// **Contraction** /// **Contraction**
/// - Paths beginning with the home directory or with a git repo right ///
/// inside the home directory will be contracted to `~` /// - Paths beginning with the home directory or with a git repo right inside
/// the home directory will be contracted to `~`
/// - Paths containing a git repo will contract to begin at the repo root /// - Paths containing a git repo will contract to begin at the repo root
/// ///
/// **Substitution** /// **Substitution**
@ -37,10 +39,10 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
// Using environment PWD is the standard approach for determining logical path // Using environment PWD is the standard approach for determining logical path
// If this is None for any reason, we fall back to reading the os-provided path // If this is None for any reason, we fall back to reading the os-provided path
let physical_current_dir = if config.use_logical_path { let physical_current_dir = if config.use_logical_path {
match std::env::var("PWD") { match context.get_env("PWD") {
Ok(x) => Some(PathBuf::from(x)), Some(x) => Some(PathBuf::from(x)),
Err(e) => { None => {
log::debug!("Error getting PWD environment variable: {}", e); log::debug!("Error getting PWD environment variable!");
None None
} }
} }
@ -269,6 +271,17 @@ fn to_fish_style(pwd_dir_length: usize, dir_string: String, truncated_dir_string
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::test::ModuleRenderer;
use ansi_term::Color;
use dirs_next::home_dir;
#[cfg(not(target_os = "windows"))]
use std::os::unix::fs::symlink;
#[cfg(target_os = "windows")]
use std::os::windows::fs::symlink_dir as symlink;
use std::path::Path;
use std::process::Command;
use std::{fs, io};
use tempfile::TempDir;
#[test] #[test]
fn contract_home_directory() { fn contract_home_directory() {
@ -355,7 +368,7 @@ mod tests {
#[test] #[test]
fn fish_style_with_no_contracted_path() { fn fish_style_with_no_contracted_path() {
// `truncatation_length = 2` // `truncation_length = 2`
let path = "/absolute/Path/not/in_a/repo/but_nested"; let path = "/absolute/Path/not/in_a/repo/but_nested";
let output = to_fish_style(1, path.to_string(), "repo/but_nested"); let output = to_fish_style(1, path.to_string(), "repo/but_nested");
assert_eq!(output, "/a/P/n/i/"); assert_eq!(output, "/a/P/n/i/");
@ -363,7 +376,7 @@ mod tests {
#[test] #[test]
fn fish_style_with_pwd_dir_len_no_contracted_path() { fn fish_style_with_pwd_dir_len_no_contracted_path() {
// `truncatation_length = 2` // `truncation_length = 2`
let path = "/absolute/Path/not/in_a/repo/but_nested"; let path = "/absolute/Path/not/in_a/repo/but_nested";
let output = to_fish_style(2, path.to_string(), "repo/but_nested"); let output = to_fish_style(2, path.to_string(), "repo/but_nested");
assert_eq!(output, "/ab/Pa/no/in/"); assert_eq!(output, "/ab/Pa/no/in/");
@ -382,4 +395,777 @@ mod tests {
let output = to_fish_style(1, path.to_string(), "目录"); let output = to_fish_style(1, path.to_string(), "目录");
assert_eq!(output, "~/s/t/目/a̐/"); assert_eq!(output, "~/s/t/目/a̐/");
} }
fn init_repo(path: &Path) -> io::Result<()> {
Command::new("git")
.args(&["init"])
.current_dir(path)
.output()
.map(|_| ())
}
fn make_known_tempdir(root: &Path) -> io::Result<(TempDir, String)> {
fs::create_dir_all(root)?;
let dir = TempDir::new_in(root)?;
let path = dir
.path()
.file_name()
.unwrap()
.to_string_lossy()
.to_string();
Ok((dir, path))
}
#[cfg(not(target_os = "windows"))]
mod linux {
use super::*;
use std::sync::atomic::{AtomicBool, Ordering};
// As tests are run in parallel we have to keep a lock on which of the
// two tests are currently running as they both modify `HOME` which can
// override the other value resulting in inconsistent runs which is why
// we only run one of these tests at once.
static LOCK: AtomicBool = AtomicBool::new(false);
#[test]
#[ignore]
fn symlinked_subdirectory_git_repo_out_of_tree() -> io::Result<()> {
while LOCK.load(Ordering::Relaxed) {}
LOCK.store(true, Ordering::Relaxed);
let tmp_dir = TempDir::new_in(home_dir().unwrap().as_path())?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let src_dir = repo_dir.join("src/meters/fuel-gauge");
let symlink_dir = tmp_dir.path().join("fuel-gauge");
fs::create_dir_all(&src_dir)?;
init_repo(&repo_dir)?;
symlink(&src_dir, &symlink_dir)?;
// We can't mock `HOME` since dirs-next uses it which does not care about our mocking
let previous_home = home_dir().unwrap();
std::env::set_var("HOME", tmp_dir.path());
let actual = ModuleRenderer::new("directory").path(symlink_dir).collect();
let expected = Some(format!("{} ", Color::Cyan.bold().paint("~/fuel-gauge")));
std::env::set_var("HOME", previous_home.as_path());
assert_eq!(expected, actual);
LOCK.store(false, Ordering::Relaxed);
tmp_dir.close()
}
#[test]
#[ignore]
fn git_repo_in_home_directory_truncate_to_repo_true() -> io::Result<()> {
while LOCK.load(Ordering::Relaxed) {}
LOCK.store(true, Ordering::Relaxed);
let tmp_dir = TempDir::new_in(home_dir().unwrap().as_path())?;
let dir = tmp_dir.path().join("src/fuel-gauge");
fs::create_dir_all(&dir)?;
init_repo(&tmp_dir.path())?;
// We can't mock `HOME` since dirs-next uses it which does not care about our mocking
let previous_home = home_dir().unwrap();
std::env::set_var("HOME", tmp_dir.path());
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
// `truncate_to_repo = true` should attempt to display the truncated path
truncate_to_repo = true
truncation_length = 5
})
.path(dir)
.collect();
let expected = Some(format!("{} ", Color::Cyan.bold().paint("~/src/fuel-gauge")));
std::env::set_var("HOME", previous_home.as_path());
assert_eq!(expected, actual);
LOCK.store(false, Ordering::Relaxed);
tmp_dir.close()
}
#[test]
fn directory_in_root() -> io::Result<()> {
let actual = ModuleRenderer::new("directory").path("/etc").collect();
let expected = Some(format!(
"{}{} ",
Color::Cyan.bold().paint("/etc"),
Color::Red.normal().paint("🔒")
));
assert_eq!(expected, actual);
Ok(())
}
}
#[test]
fn home_directory() -> io::Result<()> {
let actual = ModuleRenderer::new("directory")
.path(home_dir().unwrap())
.config(toml::toml! { // Necessary if homedir is a git repo
[directory]
truncate_to_repo = false
})
.collect();
let expected = Some(format!("{} ", Color::Cyan.bold().paint("~")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn substituted_truncated_path() -> io::Result<()> {
let actual = ModuleRenderer::new("directory")
.path("/some/long/network/path/workspace/a/b/c/dev")
.config(toml::toml! {
[directory]
truncation_length = 4
[directory.substitutions]
"/some/long/network/path" = "/some/net"
"a/b/c" = "d"
})
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan.bold().paint("net/workspace/d/dev")
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn strange_substitution() -> io::Result<()> {
let strange_sub = "/\\/;,!";
let actual = ModuleRenderer::new("directory")
.path("/foo/bar/regular/path")
.config(toml::toml! {
[directory]
truncation_length = 0
fish_style_pwd_dir_length = 2 // Overridden by substitutions
[directory.substitutions]
"regular" = strange_sub
})
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan
.bold()
.paint(format!("/foo/bar/{}/path", strange_sub))
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn directory_in_home() -> io::Result<()> {
let (tmp_dir, name) = make_known_tempdir(home_dir().unwrap().as_path())?;
let dir = tmp_dir.path().join("starship");
fs::create_dir_all(&dir)?;
let actual = ModuleRenderer::new("directory").path(dir).collect();
let expected = Some(format!(
"{} ",
Color::Cyan.bold().paint(format!("~/{}/starship", name))
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
fn truncated_directory_in_home() -> io::Result<()> {
let (tmp_dir, name) = make_known_tempdir(home_dir().unwrap().as_path())?;
let dir = tmp_dir.path().join("engine/schematics");
fs::create_dir_all(&dir)?;
let actual = ModuleRenderer::new("directory").path(dir).collect();
let expected = Some(format!(
"{} ",
Color::Cyan
.bold()
.paint(format!("{}/engine/schematics", name))
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
fn fish_directory_in_home() -> io::Result<()> {
let (tmp_dir, name) = make_known_tempdir(home_dir().unwrap().as_path())?;
let dir = tmp_dir.path().join("starship/schematics");
fs::create_dir_all(&dir)?;
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
truncation_length = 1
fish_style_pwd_dir_length = 2
})
.path(&dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan
.bold()
.paint(format!("~/{}/st/schematics", name.split_at(3).0))
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
fn root_directory() -> io::Result<()> {
let actual = ModuleRenderer::new("directory").path("/").collect();
#[cfg(not(target_os = "windows"))]
let expected = Some(format!(
"{}{} ",
Color::Cyan.bold().paint("/"),
Color::Red.normal().paint("🔒")
));
#[cfg(target_os = "windows")]
let expected = Some(format!("{} ", Color::Cyan.bold().paint("/")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn truncated_directory_in_root() -> io::Result<()> {
let (tmp_dir, name) = make_known_tempdir(Path::new("/tmp"))?;
let dir = tmp_dir.path().join("thrusters/rocket");
fs::create_dir_all(&dir)?;
let actual = ModuleRenderer::new("directory").path(dir).collect();
let expected = Some(format!(
"{} ",
Color::Cyan
.bold()
.paint(format!("{}/thrusters/rocket", name))
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
fn truncated_directory_config_large() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let dir = tmp_dir.path().join("thrusters/rocket");
fs::create_dir_all(&dir)?;
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
truncation_length = 100
})
.path(&dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan
.bold()
.paint(truncate(dir.to_slash_lossy(), 100))
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
fn fish_style_directory_config_large() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let dir = tmp_dir.path().join("thrusters/rocket");
fs::create_dir_all(&dir)?;
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
truncation_length = 1
fish_style_pwd_dir_length = 100
})
.path(&dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan
.bold()
.paint(to_fish_style(100, dir.to_slash_lossy(), ""))
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
fn truncated_directory_config_small() -> io::Result<()> {
let (tmp_dir, name) = make_known_tempdir(Path::new("/tmp"))?;
let dir = tmp_dir.path().join("rocket");
fs::create_dir_all(&dir)?;
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
truncation_length = 2
})
.path(dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan.bold().paint(format!("{}/rocket", name))
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
fn fish_directory_config_small() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let dir = tmp_dir.path().join("thrusters/rocket");
fs::create_dir_all(&dir)?;
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
truncation_length = 2
fish_style_pwd_dir_length = 1
})
.path(&dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan.bold().paint(format!(
"{}/thrusters/rocket",
to_fish_style(1, dir.to_slash_lossy(), "/thrusters/rocket")
))
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn git_repo_root() -> io::Result<()> {
let tmp_dir = TempDir::new()?;
let repo_dir = tmp_dir.path().join("rocket-controls");
fs::create_dir(&repo_dir)?;
init_repo(&repo_dir).unwrap();
let actual = ModuleRenderer::new("directory").path(repo_dir).collect();
let expected = Some(format!("{} ", Color::Cyan.bold().paint("rocket-controls")));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn directory_in_git_repo() -> io::Result<()> {
let tmp_dir = TempDir::new()?;
let repo_dir = tmp_dir.path().join("rocket-controls");
let dir = repo_dir.join("src");
fs::create_dir_all(&dir)?;
init_repo(&repo_dir).unwrap();
let actual = ModuleRenderer::new("directory").path(dir).collect();
let expected = Some(format!(
"{} ",
Color::Cyan.bold().paint("rocket-controls/src")
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn truncated_directory_in_git_repo() -> io::Result<()> {
let tmp_dir = TempDir::new()?;
let repo_dir = tmp_dir.path().join("rocket-controls");
let dir = repo_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&dir)?;
init_repo(&repo_dir).unwrap();
let actual = ModuleRenderer::new("directory").path(dir).collect();
let expected = Some(format!(
"{} ",
Color::Cyan.bold().paint("src/meters/fuel-gauge")
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn directory_in_git_repo_truncate_to_repo_false() -> io::Result<()> {
let tmp_dir = TempDir::new()?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let dir = repo_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&dir)?;
init_repo(&repo_dir).unwrap();
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
// Don't truncate the path at all.
truncation_length = 5
truncate_to_repo = false
})
.path(dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan
.bold()
.paint("above-repo/rocket-controls/src/meters/fuel-gauge")
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn fish_path_directory_in_git_repo_truncate_to_repo_false() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let dir = repo_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&dir)?;
init_repo(&repo_dir).unwrap();
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
// Don't truncate the path at all.
truncation_length = 5
truncate_to_repo = false
fish_style_pwd_dir_length = 1
})
.path(dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan.bold().paint(format!(
"{}/above-repo/rocket-controls/src/meters/fuel-gauge",
to_fish_style(1, tmp_dir.path().to_slash_lossy(), "")
))
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn fish_path_directory_in_git_repo_truncate_to_repo_true() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let dir = repo_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&dir)?;
init_repo(&repo_dir).unwrap();
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
// `truncate_to_repo = true` should display the truncated path
truncation_length = 5
truncate_to_repo = true
fish_style_pwd_dir_length = 1
})
.path(dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan.bold().paint(format!(
"{}/rocket-controls/src/meters/fuel-gauge",
to_fish_style(1, tmp_dir.path().join("above-repo").to_slash_lossy(), "")
))
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn directory_in_git_repo_truncate_to_repo_true() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let dir = repo_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&dir)?;
init_repo(&repo_dir).unwrap();
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
// `truncate_to_repo = true` should display the truncated path
truncation_length = 5
truncate_to_repo = true
})
.path(dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan
.bold()
.paint("rocket-controls/src/meters/fuel-gauge")
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn symlinked_git_repo_root() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let repo_dir = tmp_dir.path().join("rocket-controls");
let symlink_dir = tmp_dir.path().join("rocket-controls-symlink");
fs::create_dir(&repo_dir)?;
init_repo(&repo_dir).unwrap();
symlink(&repo_dir, &symlink_dir)?;
let actual = ModuleRenderer::new("directory").path(symlink_dir).collect();
let expected = Some(format!(
"{} ",
Color::Cyan.bold().paint("rocket-controls-symlink")
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn directory_in_symlinked_git_repo() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let repo_dir = tmp_dir.path().join("rocket-controls");
let src_dir = repo_dir.join("src");
let symlink_dir = tmp_dir.path().join("rocket-controls-symlink");
let symlink_src_dir = symlink_dir.join("src");
fs::create_dir_all(&src_dir)?;
init_repo(&repo_dir).unwrap();
symlink(&repo_dir, &symlink_dir)?;
let actual = ModuleRenderer::new("directory")
.path(symlink_src_dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan.bold().paint("rocket-controls-symlink/src")
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn truncated_directory_in_symlinked_git_repo() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let repo_dir = tmp_dir.path().join("rocket-controls");
let src_dir = repo_dir.join("src/meters/fuel-gauge");
let symlink_dir = tmp_dir.path().join("rocket-controls-symlink");
let symlink_src_dir = symlink_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&src_dir)?;
init_repo(&repo_dir).unwrap();
symlink(&repo_dir, &symlink_dir)?;
let actual = ModuleRenderer::new("directory")
.path(symlink_src_dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan.bold().paint("src/meters/fuel-gauge")
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn directory_in_symlinked_git_repo_truncate_to_repo_false() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let src_dir = repo_dir.join("src/meters/fuel-gauge");
let symlink_dir = tmp_dir
.path()
.join("above-repo")
.join("rocket-controls-symlink");
let symlink_src_dir = symlink_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&src_dir)?;
init_repo(&repo_dir).unwrap();
symlink(&repo_dir, &symlink_dir)?;
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
// Don't truncate the path at all.
truncation_length = 5
truncate_to_repo = false
})
.path(symlink_src_dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan
.bold()
.paint("above-repo/rocket-controls-symlink/src/meters/fuel-gauge")
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn fish_path_directory_in_symlinked_git_repo_truncate_to_repo_false() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let src_dir = repo_dir.join("src/meters/fuel-gauge");
let symlink_dir = tmp_dir
.path()
.join("above-repo")
.join("rocket-controls-symlink");
let symlink_src_dir = symlink_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&src_dir)?;
init_repo(&repo_dir).unwrap();
symlink(&repo_dir, &symlink_dir)?;
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
// Don't truncate the path at all.
truncation_length = 5
truncate_to_repo = false
fish_style_pwd_dir_length = 1
})
.path(symlink_src_dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan.bold().paint(format!(
"{}/above-repo/rocket-controls-symlink/src/meters/fuel-gauge",
to_fish_style(1, tmp_dir.path().to_slash_lossy(), "")
))
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn fish_path_directory_in_symlinked_git_repo_truncate_to_repo_true() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let src_dir = repo_dir.join("src/meters/fuel-gauge");
let symlink_dir = tmp_dir
.path()
.join("above-repo")
.join("rocket-controls-symlink");
let symlink_src_dir = symlink_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&src_dir)?;
init_repo(&repo_dir).unwrap();
symlink(&repo_dir, &symlink_dir)?;
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
// `truncate_to_repo = true` should display the truncated path
truncation_length = 5
truncate_to_repo = true
fish_style_pwd_dir_length = 1
})
.path(symlink_src_dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan.bold().paint(format!(
"{}/rocket-controls-symlink/src/meters/fuel-gauge",
to_fish_style(1, tmp_dir.path().join("above-repo").to_slash_lossy(), "")
))
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn directory_in_symlinked_git_repo_truncate_to_repo_true() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let src_dir = repo_dir.join("src/meters/fuel-gauge");
let symlink_dir = tmp_dir
.path()
.join("above-repo")
.join("rocket-controls-symlink");
let symlink_src_dir = symlink_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&src_dir)?;
init_repo(&repo_dir).unwrap();
symlink(&repo_dir, &symlink_dir)?;
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
// `truncate_to_repo = true` should display the truncated path
truncation_length = 5
truncate_to_repo = true
})
.path(symlink_src_dir)
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan
.bold()
.paint("rocket-controls-symlink/src/meters/fuel-gauge")
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn symlinked_directory_in_git_repo() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let repo_dir = tmp_dir.path().join("rocket-controls");
let dir = repo_dir.join("src");
fs::create_dir_all(&dir)?;
init_repo(&repo_dir).unwrap();
symlink(&dir, repo_dir.join("src/loop"))?;
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
// `truncate_to_repo = true` should display the truncated path
truncation_length = 5
truncate_to_repo = true
})
.path(repo_dir.join("src/loop/loop"))
.collect();
let expected = Some(format!(
"{} ",
Color::Cyan.bold().paint("rocket-controls/src/loop/loop")
));
assert_eq!(expected, actual);
tmp_dir.close()
}
} }

View File

@ -1,4 +1,3 @@
use std::env;
use std::path::PathBuf; use std::path::PathBuf;
use super::{Context, Module, RootModuleConfig}; use super::{Context, Module, RootModuleConfig};
@ -27,7 +26,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
return None; return None;
} }
let docker_config = PathBuf::from( let docker_config = PathBuf::from(
&env::var_os("DOCKER_CONFIG") &context
.get_env_os("DOCKER_CONFIG")
.unwrap_or(dirs_next::home_dir()?.join(".docker").into_os_string()), .unwrap_or(dirs_next::home_dir()?.join(".docker").into_os_string()),
) )
.join("config.json"); .join("config.json");

View File

@ -3,7 +3,7 @@ use quick_xml::Reader;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::iter::Iterator; use std::iter::Iterator;
use std::ops::Deref; use std::ops::Deref;
use std::path::Path; use std::path::{Path, PathBuf};
use std::str; use std::str;
use super::{Context, Module, RootModuleConfig}; use super::{Context, Module, RootModuleConfig};
@ -89,12 +89,12 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
fn find_current_tfm<'a>(files: &[DotNetFile<'a>]) -> Option<String> { fn find_current_tfm(files: &[DotNetFile]) -> Option<String> {
let get_file_of_type = |t: FileType| files.iter().find(|f| f.file_type == t); let get_file_of_type = |t: FileType| files.iter().find(|f| f.file_type == t);
let relevant_file = get_file_of_type(FileType::ProjectFile)?; let relevant_file = get_file_of_type(FileType::ProjectFile)?;
get_tfm_from_project_file(relevant_file.path) get_tfm_from_project_file(relevant_file.path.as_path())
} }
fn get_tfm_from_project_file(path: &Path) -> Option<String> { fn get_tfm_from_project_file(path: &Path) -> Option<String> {
@ -135,8 +135,8 @@ fn get_tfm_from_project_file(path: &Path) -> Option<String> {
None None
} }
fn estimate_dotnet_version<'a>( fn estimate_dotnet_version(
files: &[DotNetFile<'a>], files: &[DotNetFile],
current_dir: &Path, current_dir: &Path,
repo_root: Option<&Path>, repo_root: Option<&Path>,
) -> Option<Version> { ) -> Option<Version> {
@ -149,9 +149,8 @@ fn estimate_dotnet_version<'a>(
.or_else(|| files.iter().next())?; .or_else(|| files.iter().next())?;
match relevant_file.file_type { match relevant_file.file_type {
FileType::GlobalJson => { FileType::GlobalJson => get_pinned_sdk_version_from_file(relevant_file.path.as_path())
get_pinned_sdk_version_from_file(relevant_file.path).or_else(get_latest_sdk_from_cli) .or_else(get_latest_sdk_from_cli),
}
FileType::SolutionFile => { FileType::SolutionFile => {
// With this heuristic, we'll assume that a "global.json" won't // With this heuristic, we'll assume that a "global.json" won't
// be found in any directory above the solution file. // be found in any directory above the solution file.
@ -251,13 +250,13 @@ fn get_pinned_sdk_version(json: &str) -> Option<Version> {
} }
} }
fn get_local_dotnet_files<'a>(context: &'a Context) -> Result<Vec<DotNetFile<'a>>, std::io::Error> { fn get_local_dotnet_files<'a>(context: &'a Context) -> Result<Vec<DotNetFile>, std::io::Error> {
Ok(context Ok(context
.dir_contents()? .dir_contents()?
.files() .files()
.filter_map(|p| { .filter_map(|p| {
get_dotnet_file_type(p).map(|t| DotNetFile { get_dotnet_file_type(p).map(|t| DotNetFile {
path: p.as_ref(), path: context.current_dir.join(p),
file_type: t, file_type: t,
}) })
}) })
@ -331,8 +330,8 @@ fn get_latest_sdk_from_cli() -> Option<Version> {
} }
} }
struct DotNetFile<'a> { struct DotNetFile {
path: &'a Path, path: PathBuf,
file_type: FileType, file_type: FileType,
} }
@ -354,8 +353,265 @@ impl Deref for Version {
} }
} }
#[test] #[cfg(test)]
fn should_parse_version_from_global_json() { mod tests {
use super::*;
use crate::test::ModuleRenderer;
use ansi_term::Color;
use std::fs::{self, OpenOptions};
use std::io::{self, Write};
use std::process::Command;
use tempfile::{self, TempDir};
#[test]
fn shows_nothing_in_directory_with_zero_relevant_files() -> io::Result<()> {
let workspace = create_workspace(false)?;
expect_output(&workspace.path(), None)?;
workspace.close()
}
#[test]
fn shows_latest_in_directory_with_directory_build_props_file() -> io::Result<()> {
let workspace = create_workspace(false)?;
touch_path(&workspace, "Directory.Build.props", None)?;
expect_output(
&workspace.path(),
Some(format!("{} ", Color::Blue.bold().paint("•NET v3.1.103"))),
)?;
workspace.close()
}
#[test]
fn shows_latest_in_directory_with_directory_build_targets_file() -> io::Result<()> {
let workspace = create_workspace(false)?;
touch_path(&workspace, "Directory.Build.targets", None)?;
expect_output(
&workspace.path(),
Some(format!("{} ", Color::Blue.bold().paint("•NET v3.1.103"))),
)?;
workspace.close()
}
#[test]
fn shows_latest_in_directory_with_packages_props_file() -> io::Result<()> {
let workspace = create_workspace(false)?;
touch_path(&workspace, "Packages.props", None)?;
expect_output(
&workspace.path(),
Some(format!("{} ", Color::Blue.bold().paint("•NET v3.1.103"))),
)?;
workspace.close()
}
#[test]
fn shows_latest_in_directory_with_solution() -> io::Result<()> {
let workspace = create_workspace(false)?;
touch_path(&workspace, "solution.sln", None)?;
expect_output(
&workspace.path(),
Some(format!("{} ", Color::Blue.bold().paint("•NET v3.1.103"))),
)?;
workspace.close()
}
#[test]
fn shows_latest_in_directory_with_csproj() -> io::Result<()> {
let workspace = create_workspace(false)?;
let csproj = make_csproj_with_tfm("TargetFramework", "netstandard2.0");
touch_path(&workspace, "project.csproj", Some(&csproj))?;
expect_output(
&workspace.path(),
Some(format!(
"{} ",
Color::Blue.bold().paint("•NET v3.1.103 🎯 netstandard2.0")
)),
)?;
workspace.close()
}
#[test]
fn shows_latest_in_directory_with_fsproj() -> io::Result<()> {
let workspace = create_workspace(false)?;
touch_path(&workspace, "project.fsproj", None)?;
expect_output(
&workspace.path(),
Some(format!("{} ", Color::Blue.bold().paint("•NET v3.1.103"))),
)?;
workspace.close()
}
#[test]
fn shows_latest_in_directory_with_xproj() -> io::Result<()> {
let workspace = create_workspace(false)?;
touch_path(&workspace, "project.xproj", None)?;
expect_output(
&workspace.path(),
Some(format!("{} ", Color::Blue.bold().paint("•NET v3.1.103"))),
)?;
workspace.close()
}
#[test]
fn shows_latest_in_directory_with_project_json() -> io::Result<()> {
let workspace = create_workspace(false)?;
touch_path(&workspace, "project.json", None)?;
expect_output(
&workspace.path(),
Some(format!("{} ", Color::Blue.bold().paint("•NET v3.1.103"))),
)?;
workspace.close()
}
#[test]
fn shows_pinned_in_directory_with_global_json() -> io::Result<()> {
let workspace = create_workspace(false)?;
let global_json = make_pinned_sdk_json("1.2.3");
touch_path(&workspace, "global.json", Some(&global_json))?;
expect_output(
&workspace.path(),
Some(format!("{} ", Color::Blue.bold().paint("•NET v1.2.3"))),
)?;
workspace.close()
}
#[test]
fn shows_pinned_in_project_below_root_with_global_json() -> io::Result<()> {
let workspace = create_workspace(false)?;
let global_json = make_pinned_sdk_json("1.2.3");
let csproj = make_csproj_with_tfm("TargetFramework", "netstandard2.0");
touch_path(&workspace, "global.json", Some(&global_json))?;
touch_path(&workspace, "project/project.csproj", Some(&csproj))?;
expect_output(
&workspace.path().join("project"),
Some(format!(
"{} ",
Color::Blue.bold().paint("•NET v1.2.3 🎯 netstandard2.0")
)),
)?;
workspace.close()
}
#[test]
fn shows_pinned_in_deeply_nested_project_within_repository() -> io::Result<()> {
let workspace = create_workspace(true)?;
let global_json = make_pinned_sdk_json("1.2.3");
let csproj = make_csproj_with_tfm("TargetFramework", "netstandard2.0");
touch_path(&workspace, "global.json", Some(&global_json))?;
touch_path(
&workspace,
"deep/path/to/project/project.csproj",
Some(&csproj),
)?;
expect_output(
&workspace.path().join("deep/path/to/project"),
Some(format!(
"{} ",
Color::Blue.bold().paint("•NET v1.2.3 🎯 netstandard2.0")
)),
)?;
workspace.close()
}
#[test]
fn shows_single_tfm() -> io::Result<()> {
let workspace = create_workspace(false)?;
let csproj = make_csproj_with_tfm("TargetFramework", "netstandard2.0");
touch_path(&workspace, "project.csproj", Some(&csproj))?;
expect_output(
workspace.path(),
Some(format!(
"{} ",
Color::Blue.bold().paint("•NET v3.1.103 🎯 netstandard2.0")
)),
)?;
workspace.close()
}
#[test]
fn shows_multiple_tfms() -> io::Result<()> {
let workspace = create_workspace(false)?;
let csproj = make_csproj_with_tfm("TargetFrameworks", "netstandard2.0;net461");
touch_path(&workspace, "project.csproj", Some(&csproj))?;
expect_output(
workspace.path(),
Some(format!(
"{} ",
Color::Blue
.bold()
.paint("•NET v3.1.103 🎯 netstandard2.0;net461")
)),
)?;
workspace.close()
}
fn create_workspace(is_repo: bool) -> io::Result<TempDir> {
let repo_dir = tempfile::tempdir()?;
if is_repo {
Command::new("git")
.args(&["init", "--quiet"])
.current_dir(repo_dir.path())
.output()?;
}
Ok(repo_dir)
}
fn touch_path(
workspace: &TempDir,
relative_path: &str,
contents: Option<&str>,
) -> io::Result<()> {
let path = workspace.path().join(relative_path);
fs::create_dir_all(
path.parent()
.expect("Expected relative_path to be a file in a directory"),
)?;
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&path)?;
write!(file, "{}", contents.unwrap_or(""))?;
file.sync_data()
}
fn make_pinned_sdk_json(version: &str) -> String {
let json_text = r#"
{
"sdk": {
"version": "INSERT_VERSION"
}
}
"#;
json_text.replace("INSERT_VERSION", version)
}
fn make_csproj_with_tfm(tfm_element: &str, tfm: &str) -> String {
let json_text = r#"
<Project>
<PropertyGroup>
<TFM_ELEMENT>TFM_VALUE</TFM_ELEMENT>
</PropertyGroup>
</Project>
"#;
json_text
.replace("TFM_ELEMENT", tfm_element)
.replace("TFM_VALUE", tfm)
}
fn expect_output(dir: &Path, expected: Option<String>) -> io::Result<()> {
let actual = ModuleRenderer::new("dotnet").path(dir).collect();
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn should_parse_version_from_global_json() {
let json_text = r#" let json_text = r#"
{ {
"sdk": { "sdk": {
@ -366,12 +622,13 @@ fn should_parse_version_from_global_json() {
let version = get_pinned_sdk_version(json_text).unwrap(); let version = get_pinned_sdk_version(json_text).unwrap();
assert_eq!("v1.2.3", version.0); assert_eq!("v1.2.3", version.0);
} }
#[test] #[test]
fn should_ignore_empty_global_json() { fn should_ignore_empty_global_json() {
let json_text = "{}"; let json_text = "{}";
let version = get_pinned_sdk_version(json_text); let version = get_pinned_sdk_version(json_text);
assert!(version.is_none()); assert!(version.is_none());
}
} }

View File

@ -73,7 +73,7 @@ fn parse_elixir_version(version: &str) -> Option<(String, String)> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -97,7 +97,7 @@ Elixir 1.10 (compiled with Erlang/OTP 22)
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let expected = None; let expected = None;
let output = render_module("elixir", dir.path(), None); let output = ModuleRenderer::new("elixir").path(dir.path()).collect();
assert_eq!(output, expected); assert_eq!(output, expected);
@ -113,7 +113,7 @@ Elixir 1.10 (compiled with Erlang/OTP 22)
"via {} ", "via {} ",
Color::Purple.bold().paint("💧 1.10 (OTP 22)") Color::Purple.bold().paint("💧 1.10 (OTP 22)")
)); ));
let output = render_module("elixir", dir.path(), None); let output = ModuleRenderer::new("elixir").path(dir.path()).collect();
assert_eq!(output, expected); assert_eq!(output, expected);

View File

@ -60,7 +60,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io; use std::io;
@ -68,7 +68,7 @@ mod tests {
#[test] #[test]
fn folder_without_elm() -> io::Result<()> { fn folder_without_elm() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("elm", dir.path(), None); let actual = ModuleRenderer::new("elm").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -78,7 +78,7 @@ mod tests {
fn folder_with_elm_json() -> io::Result<()> { fn folder_with_elm_json() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("elm.json"))?.sync_all()?; File::create(dir.path().join("elm.json"))?.sync_all()?;
let actual = render_module("elm", dir.path(), None); let actual = ModuleRenderer::new("elm").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🌳 v0.19.1"))); let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🌳 v0.19.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -88,7 +88,7 @@ mod tests {
fn folder_with_elm_package_json() -> io::Result<()> { fn folder_with_elm_package_json() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("elm-package.json"))?.sync_all()?; File::create(dir.path().join("elm-package.json"))?.sync_all()?;
let actual = render_module("elm", dir.path(), None); let actual = ModuleRenderer::new("elm").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🌳 v0.19.1"))); let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🌳 v0.19.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -98,7 +98,7 @@ mod tests {
fn folder_with_elm_version() -> io::Result<()> { fn folder_with_elm_version() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join(".elm-version"))?.sync_all()?; File::create(dir.path().join(".elm-version"))?.sync_all()?;
let actual = render_module("elm", dir.path(), None); let actual = ModuleRenderer::new("elm").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🌳 v0.19.1"))); let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🌳 v0.19.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -109,7 +109,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let elmstuff = dir.path().join("elm-stuff"); let elmstuff = dir.path().join("elm-stuff");
fs::create_dir_all(&elmstuff)?; fs::create_dir_all(&elmstuff)?;
let actual = render_module("elm", dir.path(), None); let actual = ModuleRenderer::new("elm").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🌳 v0.19.1"))); let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🌳 v0.19.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -119,7 +119,7 @@ mod tests {
fn folder_with_elm_file() -> io::Result<()> { fn folder_with_elm_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.elm"))?.sync_all()?; File::create(dir.path().join("main.elm"))?.sync_all()?;
let actual = render_module("elm", dir.path(), None); let actual = ModuleRenderer::new("elm").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🌳 v0.19.1"))); let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🌳 v0.19.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()

View File

@ -1,5 +1,3 @@
use std::env;
use super::{Context, Module}; use super::{Context, Module};
use crate::config::RootModuleConfig; use crate::config::RootModuleConfig;
@ -16,7 +14,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let mut module = context.new_module("env_var"); let mut module = context.new_module("env_var");
let config: EnvVarConfig = EnvVarConfig::try_load(module.config); let config: EnvVarConfig = EnvVarConfig::try_load(module.config);
let env_value = get_env_value(config.variable?, config.default)?; let env_value = get_env_value(context, config.variable?, config.default)?;
let parsed = StringFormatter::new(config.format).and_then(|formatter| { let parsed = StringFormatter::new(config.format).and_then(|formatter| {
formatter formatter
.map_meta(|var, _| match var { .map_meta(|var, _| match var {
@ -45,12 +43,153 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
fn get_env_value(name: &str, default: Option<&str>) -> Option<String> { fn get_env_value(context: &Context, name: &str, default: Option<&str>) -> Option<String> {
match env::var_os(name) { match context.get_env(name) {
Some(os_value) => match os_value.into_string() { Some(value) => Some(value),
Ok(value) => Some(value),
Err(_error) => None,
},
None => default.map(|value| value.to_owned()), None => default.map(|value| value.to_owned()),
} }
} }
#[cfg(test)]
mod test {
use crate::test::ModuleRenderer;
use ansi_term::{Color, Style};
use std::io;
const TEST_VAR_VALUE: &str = "astronauts";
#[test]
fn empty_config() -> io::Result<()> {
let actual = ModuleRenderer::new("env_var")
.config(toml::toml! {
[env_var]
})
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn defined_variable() -> io::Result<()> {
let actual = ModuleRenderer::new("env_var")
.config(toml::toml! {
[env_var]
variable = "TEST_VAR"
})
.env("TEST_VAR", TEST_VAR_VALUE)
.collect();
let expected = Some(format!("with {} ", style().paint(TEST_VAR_VALUE)));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn undefined_variable() -> io::Result<()> {
let actual = ModuleRenderer::new("env_var")
.config(toml::toml! {
[env_var]
variable = "TEST_VAR"
})
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn default_has_no_effect() -> io::Result<()> {
let actual = ModuleRenderer::new("env_var")
.config(toml::toml! {
[env_var]
variable = "TEST_VAR"
default = "N/A"
})
.env("TEST_VAR", TEST_VAR_VALUE)
.collect();
let expected = Some(format!("with {} ", style().paint(TEST_VAR_VALUE)));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn default_takes_effect() -> io::Result<()> {
let actual = ModuleRenderer::new("env_var")
.config(toml::toml! {
[env_var]
variable = "UNDEFINED_TEST_VAR"
default = "N/A"
})
.collect();
let expected = Some(format!("with {} ", style().paint("N/A")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn symbol() -> io::Result<()> {
let actual = ModuleRenderer::new("env_var")
.config(toml::toml! {
[env_var]
variable = "TEST_VAR"
format = "with [■ $env_value](black bold dimmed) "
})
.env("TEST_VAR", TEST_VAR_VALUE)
.collect();
let expected = Some(format!(
"with {} ",
style().paint(format!("{}", TEST_VAR_VALUE))
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn prefix() -> io::Result<()> {
let actual = ModuleRenderer::new("env_var")
.config(toml::toml! {
[env_var]
variable = "TEST_VAR"
format = "with [_$env_value](black bold dimmed) "
})
.env("TEST_VAR", TEST_VAR_VALUE)
.collect();
let expected = Some(format!(
"with {} ",
style().paint(format!("_{}", TEST_VAR_VALUE))
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn suffix() -> io::Result<()> {
let actual = ModuleRenderer::new("env_var")
.config(toml::toml! {
[env_var]
variable = "TEST_VAR"
format = "with [${env_value}_](black bold dimmed) "
})
.env("TEST_VAR", TEST_VAR_VALUE)
.collect();
let expected = Some(format!(
"with {} ",
style().paint(format!("{}_", TEST_VAR_VALUE))
));
assert_eq!(expected, actual);
Ok(())
}
fn style() -> Style {
// default style
Color::Black.bold().dimmed()
}
}

View File

@ -67,7 +67,7 @@ fn get_erlang_version() -> Option<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -77,7 +77,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let expected = None; let expected = None;
let output = render_module("erlang", dir.path(), None); let output = ModuleRenderer::new("erlang").path(dir.path()).collect();
assert_eq!(output, expected); assert_eq!(output, expected);
@ -90,7 +90,7 @@ mod tests {
File::create(dir.path().join("rebar.config"))?.sync_all()?; File::create(dir.path().join("rebar.config"))?.sync_all()?;
let expected = Some(format!("via {} ", Color::Red.bold().paint("🖧 22.1.3"))); let expected = Some(format!("via {} ", Color::Red.bold().paint("🖧 22.1.3")));
let output = render_module("erlang", dir.path(), None); let output = ModuleRenderer::new("erlang").path(dir.path()).collect();
assert_eq!(output, expected); assert_eq!(output, expected);

View File

@ -1,5 +1,4 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::env;
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader, Error, ErrorKind}; use std::io::{BufRead, BufReader, Error, ErrorKind};
use std::path::PathBuf; use std::path::PathBuf;
@ -68,16 +67,16 @@ fn get_active_config(config_root: &PathBuf) -> Option<String> {
} }
} }
fn get_current_config_path() -> Option<PathBuf> { fn get_current_config_path(context: &Context) -> Option<PathBuf> {
let config_dir = get_config_dir()?; let config_dir = get_config_dir(context)?;
let active_config = get_active_config(&config_dir)?; let active_config = get_active_config(&config_dir)?;
let current_config = config_dir.join(format!("configurations/config_{}", active_config)); let current_config = config_dir.join(format!("configurations/config_{}", active_config));
Some(current_config) Some(current_config)
} }
fn get_config_dir() -> Option<PathBuf> { fn get_config_dir(context: &Context) -> Option<PathBuf> {
let config_dir = env::var("CLOUDSDK_CONFIG") let config_dir = context
.ok() .get_env("CLOUDSDK_CONFIG")
.and_then(|path| PathBuf::from_str(&path).ok()) .and_then(|path| PathBuf::from_str(&path).ok())
.or_else(|| { .or_else(|| {
let mut home = dirs_next::home_dir()?; let mut home = dirs_next::home_dir()?;
@ -98,11 +97,11 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let mut module = context.new_module("gcloud"); let mut module = context.new_module("gcloud");
let config: GcloudConfig = GcloudConfig::try_load(module.config); let config: GcloudConfig = GcloudConfig::try_load(module.config);
let config_path = get_current_config_path()?; let config_path = get_current_config_path(context)?;
let gcloud_account = get_gcloud_account_from_config(&config_path); let gcloud_account = get_gcloud_account_from_config(&config_path);
let gcloud_project = get_gcloud_project_from_config(&config_path); let gcloud_project = get_gcloud_project_from_config(&config_path);
let gcloud_region = get_gcloud_region_from_config(&config_path); let gcloud_region = get_gcloud_region_from_config(&config_path);
let config_dir = get_config_dir()?; let config_dir = get_config_dir(context)?;
let gcloud_active: Option<Active> = get_active_config(&config_dir); let gcloud_active: Option<Active> = get_active_config(&config_dir);
if gcloud_account.is_none() if gcloud_account.is_none()
@ -149,3 +148,172 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
#[cfg(test)]
mod tests {
use std::fs::{create_dir, File};
use std::io::{self, Write};
use ansi_term::Color;
use crate::test::ModuleRenderer;
#[test]
fn account_set() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let active_config_path = dir.path().join("active_config");
let mut active_config_file = File::create(&active_config_path)?;
active_config_file.write_all(b"default")?;
create_dir(dir.path().join("configurations"))?;
let config_default_path = dir.path().join("configurations/config_default");
let mut config_default_file = File::create(&config_default_path)?;
config_default_file.write_all(
b"[core]
account = foo@example.com
",
)?;
let actual = ModuleRenderer::new("gcloud")
.env("CLOUDSDK_CONFIG", dir.path().to_string_lossy())
.collect();
let expected = Some(format!(
"on {} ",
Color::Blue.bold().paint("☁️ foo@example.com")
));
assert_eq!(actual, expected);
dir.close()
}
#[test]
fn account_and_region_set() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let active_config_path = dir.path().join("active_config");
let mut active_config_file = File::create(&active_config_path)?;
active_config_file.write_all(b"default")?;
create_dir(dir.path().join("configurations"))?;
let config_default_path = dir.path().join("configurations/config_default");
let mut config_default_file = File::create(&config_default_path)?;
config_default_file.write_all(
b"[core]
account = foo@example.com
[compute]
region = us-central1
",
)?;
let actual = ModuleRenderer::new("gcloud")
.env("CLOUDSDK_CONFIG", dir.path().to_string_lossy())
.collect();
let expected = Some(format!(
"on {} ",
Color::Blue.bold().paint("☁️ foo@example.com(us-central1)")
));
assert_eq!(actual, expected);
dir.close()
}
#[test]
fn account_and_region_set_with_alias() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let active_config_path = dir.path().join("active_config");
let mut active_config_file = File::create(&active_config_path)?;
active_config_file.write_all(b"default")?;
create_dir(dir.path().join("configurations"))?;
let config_default_path = dir.path().join("configurations/config_default");
let mut config_default_file = File::create(&config_default_path)?;
config_default_file.write_all(
b"[core]
account = foo@example.com
[compute]
region = us-central1
",
)?;
let actual = ModuleRenderer::new("gcloud")
.env("CLOUDSDK_CONFIG", dir.path().to_string_lossy())
.config(toml::toml! {
[gcloud.region_aliases]
us-central1 = "uc1"
})
.collect();
let expected = Some(format!(
"on {} ",
Color::Blue.bold().paint("☁️ foo@example.com(uc1)")
));
assert_eq!(actual, expected);
dir.close()
}
#[test]
fn active_set() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let active_config_path = dir.path().join("active_config");
let mut active_config_file = File::create(&active_config_path)?;
active_config_file.write_all(b"default1")?;
let actual = ModuleRenderer::new("gcloud")
.env("CLOUDSDK_CONFIG", dir.path().to_string_lossy())
.config(toml::toml! {
[gcloud]
format = "on [$symbol$active]($style) "
})
.collect();
let expected = Some(format!("on {} ", Color::Blue.bold().paint("☁️ default1")));
assert_eq!(actual, expected);
dir.close()
}
#[test]
fn project_set() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let active_config_path = dir.path().join("active_config");
let mut active_config_file = File::create(&active_config_path)?;
active_config_file.write_all(b"default")?;
create_dir(dir.path().join("configurations"))?;
let config_default_path = dir.path().join("configurations/config_default");
let mut config_default_file = File::create(&config_default_path)?;
config_default_file.write_all(
b"[core]
project = abc
",
)?;
let actual = ModuleRenderer::new("gcloud")
.env("CLOUDSDK_CONFIG", dir.path().to_string_lossy())
.config(toml::toml! {
[gcloud]
format = "on [$symbol$project]($style) "
})
.collect();
let expected = Some(format!("on {} ", Color::Blue.bold().paint("☁️ abc")));
assert_eq!(actual, expected);
dir.close()
}
#[test]
fn region_not_set_with_display_region() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let actual = ModuleRenderer::new("gcloud")
.env("CLOUDSDK_CONFIG", dir.path().to_string_lossy())
.config(toml::toml! {
[gcloud]
format = "on [$symbol$region]($style) "
})
.collect();
let expected = None;
assert_eq!(expected, actual);
dir.close()
}
}

View File

@ -29,7 +29,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let repo = context.get_repo().ok()?; let repo = context.get_repo().ok()?;
let branch_name = repo.branch.as_ref()?; let branch_name = repo.branch.as_ref()?;
let mut graphemes = get_graphemes(&branch_name); let mut graphemes: Vec<&str> = branch_name.graphemes(true).collect();
let trunc_len = len.min(graphemes.len()); let trunc_len = len.min(graphemes.len());
if trunc_len < graphemes.len() { if trunc_len < graphemes.len() {
@ -72,6 +72,295 @@ fn get_first_grapheme(text: &str) -> &str {
.unwrap_or("") .unwrap_or("")
} }
fn get_graphemes(text: &str) -> Vec<&str> { #[cfg(test)]
UnicodeSegmentation::graphemes(text, true).collect() mod tests {
use ansi_term::Color;
use std::io;
use std::process::Command;
use crate::test::{fixture_repo, FixtureProvider, ModuleRenderer};
#[test]
fn show_nothing_on_empty_dir() -> io::Result<()> {
let repo_dir = tempfile::tempdir()?;
let actual = ModuleRenderer::new("git_branch")
.path(repo_dir.path())
.collect();
let expected = None;
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn test_changed_truncation_symbol() -> io::Result<()> {
test_truncate_length_with_config(
"1337_hello_world",
15,
"1337_hello_worl",
"%",
"truncation_symbol = \"%\"",
)
}
#[test]
fn test_no_truncation_symbol() -> io::Result<()> {
test_truncate_length_with_config(
"1337_hello_world",
15,
"1337_hello_worl",
"",
"truncation_symbol = \"\"",
)
}
#[test]
fn test_multi_char_truncation_symbol() -> io::Result<()> {
test_truncate_length_with_config(
"1337_hello_world",
15,
"1337_hello_worl",
"a",
"truncation_symbol = \"apple\"",
)
}
#[test]
fn test_ascii_boundary_below() -> io::Result<()> {
test_truncate_length("1337_hello_world", 15, "1337_hello_worl", "")
}
#[test]
fn test_ascii_boundary_on() -> io::Result<()> {
test_truncate_length("1337_hello_world", 16, "1337_hello_world", "")
}
#[test]
fn test_ascii_boundary_above() -> io::Result<()> {
test_truncate_length("1337_hello_world", 17, "1337_hello_world", "")
}
#[test]
fn test_one() -> io::Result<()> {
test_truncate_length("1337_hello_world", 1, "1", "")
}
#[test]
fn test_zero() -> io::Result<()> {
test_truncate_length("1337_hello_world", 0, "1337_hello_world", "")
}
#[test]
fn test_negative() -> io::Result<()> {
test_truncate_length("1337_hello_world", -1, "1337_hello_world", "")
}
#[test]
fn test_hindi_truncation() -> io::Result<()> {
test_truncate_length("नमस्ते", 3, "नमस्", "")
}
#[test]
fn test_hindi_truncation2() -> io::Result<()> {
test_truncate_length("नमस्त", 3, "नमस्", "")
}
#[test]
fn test_japanese_truncation() -> io::Result<()> {
test_truncate_length("がんばってね", 4, "がんばっ", "")
}
#[test]
fn test_format_no_branch() -> io::Result<()> {
test_format("1337_hello_world", "no_branch", "", "no_branch")
}
#[test]
fn test_format_just_branch_name() -> io::Result<()> {
test_format("1337_hello_world", "$branch", "", "1337_hello_world")
}
#[test]
fn test_format_just_branch_name_color() -> io::Result<()> {
test_format(
"1337_hello_world",
"[$branch](bold blue)",
"",
Color::Blue.bold().paint("1337_hello_world").to_string(),
)
}
#[test]
fn test_format_mixed_colors() -> io::Result<()> {
test_format(
"1337_hello_world",
"branch: [$branch](bold blue) [THE COLORS](red) ",
"",
format!(
"branch: {} {} ",
Color::Blue.bold().paint("1337_hello_world").to_string(),
Color::Red.paint("THE COLORS").to_string()
),
)
}
#[test]
fn test_format_symbol_style() -> io::Result<()> {
test_format(
"1337_hello_world",
"$symbol[$branch]($style)",
r#"
symbol = "git: "
style = "green"
"#,
format!(
"git: {}",
Color::Green.paint("1337_hello_world").to_string(),
),
)
}
#[test]
fn test_works_with_unborn_default_branch() -> io::Result<()> {
let repo_dir = tempfile::tempdir()?;
Command::new("git")
.args(&["init"])
.current_dir(&repo_dir)
.output()?;
Command::new("git")
.args(&["symbolic-ref", "HEAD", "refs/heads/main"])
.current_dir(&repo_dir)
.output()?;
let actual = ModuleRenderer::new("git_branch")
.path(&repo_dir.path())
.collect();
let expected = Some(format!(
"on {} ",
Color::Purple.bold().paint(format!("\u{e0a0} {}", "main")),
));
assert_eq!(expected, actual);
repo_dir.close()
}
// This test is not possible until we switch to `git status --porcelain`
// where we can mock the env for the specific git process. This is because
// git2 does not care about our mocking and when we set the real `GIT_DIR`
// variable it will interfere with the other tests.
// #[test]
// fn test_git_dir_env_variable() -> io::Result<()> {let repo_dir =
// tempfile::tempdir()?;
// Command::new("git")
// .args(&["init"])
// .current_dir(&repo_dir)
// .output()?;
// // git2 does not care about our mocking
// std::env::set_var("GIT_DIR", repo_dir.path().join(".git"));
// let actual = ModuleRenderer::new("git_branch").collect();
// std::env::remove_var("GIT_DIR");
// let expected = Some(format!(
// "on {} ",
// Color::Purple.bold().paint(format!("\u{e0a0} {}", "master")),
// ));
// assert_eq!(expected, actual);
// repo_dir.close()
// }
fn test_truncate_length(
branch_name: &str,
truncate_length: i64,
expected_name: &str,
truncation_symbol: &str,
) -> io::Result<()> {
test_truncate_length_with_config(
branch_name,
truncate_length,
expected_name,
truncation_symbol,
"",
)
}
fn test_truncate_length_with_config(
branch_name: &str,
truncate_length: i64,
expected_name: &str,
truncation_symbol: &str,
config_options: &str,
) -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
Command::new("git")
.args(&["checkout", "-b", branch_name])
.current_dir(repo_dir.path())
.output()?;
let actual = ModuleRenderer::new("git_branch")
.config(
toml::from_str(&format!(
"
[git_branch]
truncation_length = {}
{}
",
truncate_length, config_options
))
.unwrap(),
)
.path(repo_dir.path())
.collect();
let expected = Some(format!(
"on {} ",
Color::Purple
.bold()
.paint(format!("\u{e0a0} {}{}", expected_name, truncation_symbol)),
));
assert_eq!(expected, actual);
repo_dir.close()
}
fn test_format<T: Into<String>>(
branch_name: &str,
format: &str,
config_options: &str,
expected: T,
) -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
Command::new("git")
.args(&["checkout", "-b", branch_name])
.current_dir(repo_dir.path())
.output()?;
let actual = ModuleRenderer::new("git_branch")
.config(
toml::from_str(&format!(
r#"
[git_branch]
format = "{}"
{}
"#,
format, config_options
))
.unwrap(),
)
.path(repo_dir.path())
.collect();
assert_eq!(Some(expected.into()), actual);
repo_dir.close()
}
} }

View File

@ -62,3 +62,138 @@ pub fn id_to_hex_abbrev(bytes: &[u8], len: usize) -> String {
.take(len) .take(len)
.collect() .collect()
} }
#[cfg(test)]
mod tests {
use ansi_term::Color;
use std::process::Command;
use std::{io, str};
use crate::test::{fixture_repo, FixtureProvider, ModuleRenderer};
#[test]
fn show_nothing_on_empty_dir() -> io::Result<()> {
let repo_dir = tempfile::tempdir()?;
let actual = ModuleRenderer::new("git_commit")
.path(&repo_dir.path())
.collect();
let expected = None;
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn test_render_commit_hash() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
let mut git_output = Command::new("git")
.args(&["rev-parse", "HEAD"])
.current_dir(&repo_dir.path())
.output()?
.stdout;
git_output.truncate(7);
let expected_hash = str::from_utf8(&git_output).unwrap();
let actual = ModuleRenderer::new("git_commit")
.config(toml::toml! {
[git_commit]
only_detached = false
})
.path(&repo_dir.path())
.collect();
let expected = Some(format!(
"{} ",
Color::Green
.bold()
.paint(format!("({})", expected_hash))
.to_string()
));
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn test_render_commit_hash_len_override() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
let mut git_output = Command::new("git")
.args(&["rev-parse", "HEAD"])
.current_dir(&repo_dir.path())
.output()?
.stdout;
git_output.truncate(14);
let expected_hash = str::from_utf8(&git_output).unwrap();
let actual = ModuleRenderer::new("git_commit")
.config(toml::toml! {
[git_commit]
only_detached = false
commit_hash_length = 14
})
.path(&repo_dir.path())
.collect();
let expected = Some(format!(
"{} ",
Color::Green
.bold()
.paint(format!("({})", expected_hash))
.to_string()
));
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn test_render_commit_hash_only_detached_on_branch() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
let actual = ModuleRenderer::new("git_commit")
.path(&repo_dir.path())
.collect();
let expected = None;
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn test_render_commit_hash_only_detached_on_detached() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
Command::new("git")
.args(&["checkout", "@~1"])
.current_dir(&repo_dir.path())
.output()?;
let mut git_output = Command::new("git")
.args(&["rev-parse", "HEAD"])
.current_dir(&repo_dir.path())
.output()?
.stdout;
git_output.truncate(7);
let expected_hash = str::from_utf8(&git_output).unwrap();
let actual = ModuleRenderer::new("git_commit")
.path(&repo_dir.path())
.collect();
let expected = Some(format!(
"{} ",
Color::Green
.bold()
.paint(format!("({})", expected_hash))
.to_string()
));
assert_eq!(expected, actual);
repo_dir.close()
}
}

View File

@ -153,3 +153,195 @@ struct StateDescription<'a> {
current: Option<String>, current: Option<String>,
total: Option<String>, total: Option<String>,
} }
#[cfg(test)]
mod tests {
use ansi_term::Color;
use std::ffi::OsStr;
use std::fs::OpenOptions;
use std::io::{self, Error, ErrorKind, Write};
use std::path::Path;
use std::process::{Command, Stdio};
use crate::test::ModuleRenderer;
#[test]
fn show_nothing_on_empty_dir() -> io::Result<()> {
let repo_dir = tempfile::tempdir()?;
let actual = ModuleRenderer::new("git_state")
.path(repo_dir.path())
.collect();
let expected = None;
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_rebasing() -> io::Result<()> {
let repo_dir = create_repo_with_conflict()?;
let path = repo_dir.path();
run_git_cmd(&["rebase", "other-branch"], Some(path), false)?;
let actual = ModuleRenderer::new("git_state").path(path).collect();
let expected = Some(format!("{} ", Color::Yellow.bold().paint("(REBASING 1/1)")));
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_merging() -> io::Result<()> {
let repo_dir = create_repo_with_conflict()?;
let path = repo_dir.path();
run_git_cmd(&["merge", "other-branch"], Some(path), false)?;
let actual = ModuleRenderer::new("git_state").path(path).collect();
let expected = Some(format!("{} ", Color::Yellow.bold().paint("(MERGING)")));
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_cherry_picking() -> io::Result<()> {
let repo_dir = create_repo_with_conflict()?;
let path = repo_dir.path();
run_git_cmd(&["cherry-pick", "other-branch"], Some(path), false)?;
let actual = ModuleRenderer::new("git_state").path(path).collect();
let expected = Some(format!(
"{} ",
Color::Yellow.bold().paint("(CHERRY-PICKING)")
));
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_bisecting() -> io::Result<()> {
let repo_dir = create_repo_with_conflict()?;
let path = repo_dir.path();
run_git_cmd(&["bisect", "start"], Some(path), false)?;
let actual = ModuleRenderer::new("git_state").path(path).collect();
let expected = Some(format!("{} ", Color::Yellow.bold().paint("(BISECTING)")));
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_reverting() -> io::Result<()> {
let repo_dir = create_repo_with_conflict()?;
let path = repo_dir.path();
run_git_cmd(&["revert", "--no-commit", "HEAD~1"], Some(path), false)?;
let actual = ModuleRenderer::new("git_state").path(path).collect();
let expected = Some(format!("{} ", Color::Yellow.bold().paint("(REVERTING)")));
assert_eq!(expected, actual);
repo_dir.close()
}
fn run_git_cmd<A, S>(args: A, dir: Option<&Path>, should_succeed: bool) -> io::Result<()>
where
A: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let mut command = Command::new("git");
command
.args(args)
.stdout(Stdio::null())
.stderr(Stdio::null())
.stdin(Stdio::null());
if let Some(dir) = dir {
command.current_dir(dir);
}
let status = command.status()?;
if should_succeed && !status.success() {
Err(Error::from(ErrorKind::Other))
} else {
Ok(())
}
}
fn create_repo_with_conflict() -> io::Result<tempfile::TempDir> {
let repo_dir = tempfile::tempdir()?;
let path = repo_dir.path();
let conflicted_file = repo_dir.path().join("the_file");
let write_file = |text: &str| {
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&conflicted_file)?;
write!(file, "{}", text)
};
// Initialize a new git repo
run_git_cmd(
&[
"init",
"--quiet",
path.to_str().expect("Path was not UTF-8"),
],
None,
true,
)?;
// Set local author info
run_git_cmd(
&["config", "--local", "user.email", "starship@example.com"],
Some(path),
true,
)?;
run_git_cmd(
&["config", "--local", "user.name", "starship"],
Some(path),
true,
)?;
// Write a file on master and commit it
write_file("Version A")?;
run_git_cmd(&["add", "the_file"], Some(path), true)?;
run_git_cmd(&["commit", "--message", "Commit A"], Some(path), true)?;
// Switch to another branch, and commit a change to the file
run_git_cmd(&["checkout", "-b", "other-branch"], Some(path), true)?;
write_file("Version B")?;
run_git_cmd(
&["commit", "--all", "--message", "Commit B"],
Some(path),
true,
)?;
// Switch back to master, and commit a third change to the file
run_git_cmd(&["checkout", "master"], Some(path), true)?;
write_file("Version C")?;
run_git_cmd(
&["commit", "--all", "--message", "Commit C"],
Some(path),
true,
)?;
Ok(repo_dir)
}
}

View File

@ -371,3 +371,599 @@ fn format_count(format_str: &str, config_path: &str, count: usize) -> Option<Vec
_ => None, _ => None,
}) })
} }
#[cfg(test)]
mod tests {
use ansi_term::{ANSIStrings, Color};
use std::fs::{self, File};
use std::io;
use std::path::Path;
use std::process::Command;
use crate::test::{fixture_repo, FixtureProvider, ModuleRenderer};
/// Right after the calls to git the filesystem state may not have finished
/// updating yet causing some of the tests to fail. These barriers are placed
/// after each call to git.
/// This barrier is windows-specific though other operating systems may need it
/// in the future.
#[cfg(not(windows))]
fn barrier() {}
#[cfg(windows)]
fn barrier() {
std::thread::sleep(std::time::Duration::from_millis(500));
}
fn format_output(symbols: &str) -> Option<String> {
Some(format!(
"{} ",
Color::Red.bold().paint(format!("[{}]", symbols))
))
}
#[test]
fn show_nothing_on_empty_dir() -> io::Result<()> {
let repo_dir = tempfile::tempdir()?;
let actual = ModuleRenderer::new("git_status")
.path(repo_dir.path())
.collect();
let expected = None;
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_behind() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
behind(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.path(repo_dir.path())
.collect();
let expected = format_output("");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_behind_with_count() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
behind(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.config(toml::toml! {
[git_status]
behind = "⇣$count"
})
.path(repo_dir.path())
.collect();
let expected = format_output("⇣1");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_ahead() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
File::create(repo_dir.path().join("readme.md"))?.sync_all()?;
ahead(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.path(&repo_dir.path())
.collect();
let expected = format_output("");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_ahead_with_count() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
File::create(repo_dir.path().join("readme.md"))?.sync_all()?;
ahead(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.config(toml::toml! {
[git_status]
ahead="⇡$count"
})
.path(&repo_dir.path())
.collect();
let expected = format_output("⇡1");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_diverged() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
diverge(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.path(&repo_dir.path())
.collect();
let expected = format_output("");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_diverged_with_count() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
diverge(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.config(toml::toml! {
[git_status]
diverged=r"⇕⇡$ahead_count⇣$behind_count"
})
.path(&repo_dir.path())
.collect();
let expected = format_output("⇕⇡1⇣1");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_conflicted() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
create_conflict(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.path(&repo_dir.path())
.collect();
let expected = format_output("=");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_conflicted_with_count() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
create_conflict(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.config(toml::toml! {
[git_status]
conflicted = "=$count"
})
.path(&repo_dir.path())
.collect();
let expected = format_output("=1");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_untracked_file() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
create_untracked(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.path(&repo_dir.path())
.collect();
let expected = format_output("?");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_untracked_file_with_count() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
create_untracked(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.config(toml::toml! {
[git_status]
untracked = "?$count"
})
.path(&repo_dir.path())
.collect();
let expected = format_output("?1");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn doesnt_show_untracked_file_if_disabled() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
create_untracked(&repo_dir.path())?;
Command::new("git")
.args(&["config", "status.showUntrackedFiles", "no"])
.current_dir(repo_dir.path())
.output()?;
barrier();
let actual = ModuleRenderer::new("git_status")
.path(&repo_dir.path())
.collect();
let expected = None;
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_stashed() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
barrier();
create_stash(&repo_dir.path())?;
Command::new("git")
.args(&["reset", "--hard", "HEAD"])
.current_dir(repo_dir.path())
.output()?;
barrier();
let actual = ModuleRenderer::new("git_status")
.path(&repo_dir.path())
.collect();
let expected = format_output("$");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_stashed_with_count() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
barrier();
create_stash(&repo_dir.path())?;
barrier();
Command::new("git")
.args(&["reset", "--hard", "HEAD"])
.current_dir(repo_dir.path())
.output()?;
barrier();
let actual = ModuleRenderer::new("git_status")
.config(toml::toml! {
[git_status]
stashed = r"\$$count"
})
.path(&repo_dir.path())
.collect();
let expected = format_output("$1");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_modified() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
create_modified(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.path(&repo_dir.path())
.collect();
let expected = format_output("!");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_modified_with_count() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
create_modified(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.config(toml::toml! {
[git_status]
modified = "!$count"
})
.path(&repo_dir.path())
.collect();
let expected = format_output("!1");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_staged_file() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
create_staged(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.path(&repo_dir.path())
.collect();
let expected = format_output("+");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_staged_file_with_count() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
create_staged(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.config(toml::toml! {
[git_status]
staged = "+[$count](green)"
})
.path(&repo_dir.path())
.collect();
let expected = Some(format!(
"{} ",
ANSIStrings(&[
Color::Red.bold().paint("[+"),
Color::Green.paint("1"),
Color::Red.bold().paint("]"),
])
));
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_renamed_file() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
create_renamed(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.path(&repo_dir.path())
.collect();
let expected = format_output("»");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_renamed_file_with_count() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
create_renamed(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.config(toml::toml! {
[git_status]
renamed = "»$count"
})
.path(&repo_dir.path())
.collect();
let expected = format_output("»1");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_deleted_file() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
create_deleted(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.path(&repo_dir.path())
.collect();
let expected = format_output("");
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_deleted_file_with_count() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
create_deleted(&repo_dir.path())?;
let actual = ModuleRenderer::new("git_status")
.config(toml::toml! {
[git_status]
deleted = "✘$count"
})
.path(&repo_dir.path())
.collect();
let expected = format_output("✘1");
assert_eq!(expected, actual);
repo_dir.close()
}
// Whenever a file is manually renamed, git itself ('git status') does not treat such file as renamed,
// but as untracked instead. The following test checks if manually deleted and manually renamed
// files are tracked by git_status module in the same way 'git status' does.
#[test]
#[ignore]
fn ignore_manually_renamed() -> io::Result<()> {
let repo_dir = fixture_repo(FixtureProvider::GIT)?;
File::create(repo_dir.path().join("a"))?.sync_all()?;
File::create(repo_dir.path().join("b"))?.sync_all()?;
Command::new("git")
.args(&["add", "--all"])
.current_dir(&repo_dir.path())
.output()?;
Command::new("git")
.args(&["commit", "-m", "add new files"])
.current_dir(&repo_dir.path())
.output()?;
fs::remove_file(repo_dir.path().join("a"))?;
fs::rename(repo_dir.path().join("b"), repo_dir.path().join("c"))?;
barrier();
let actual = ModuleRenderer::new("git_status")
.path(&repo_dir.path())
.config(toml::toml! {
[git_status]
ahead = "A"
deleted = "D"
untracked = "U"
renamed = "R"
})
.collect();
let expected = format_output("DUA");
assert_eq!(actual, expected);
repo_dir.close()
}
fn ahead(repo_dir: &Path) -> io::Result<()> {
File::create(repo_dir.join("readme.md"))?.sync_all()?;
Command::new("git")
.args(&["commit", "-am", "Update readme"])
.current_dir(&repo_dir)
.output()?;
barrier();
Ok(())
}
fn behind(repo_dir: &Path) -> io::Result<()> {
Command::new("git")
.args(&["reset", "--hard", "HEAD^"])
.current_dir(repo_dir)
.output()?;
barrier();
Ok(())
}
fn diverge(repo_dir: &Path) -> io::Result<()> {
Command::new("git")
.args(&["reset", "--hard", "HEAD^"])
.current_dir(repo_dir)
.output()?;
barrier();
fs::write(repo_dir.join("Cargo.toml"), " ")?;
Command::new("git")
.args(&["commit", "-am", "Update readme"])
.current_dir(repo_dir)
.output()?;
barrier();
Ok(())
}
fn create_conflict(repo_dir: &Path) -> io::Result<()> {
Command::new("git")
.args(&["reset", "--hard", "HEAD^"])
.current_dir(repo_dir)
.output()?;
barrier();
fs::write(repo_dir.join("readme.md"), "# goodbye")?;
Command::new("git")
.args(&["add", "."])
.current_dir(repo_dir)
.output()?;
barrier();
Command::new("git")
.args(&["commit", "-m", "Change readme"])
.current_dir(repo_dir)
.output()?;
barrier();
Command::new("git")
.args(&["pull", "--rebase"])
.current_dir(repo_dir)
.output()?;
barrier();
Ok(())
}
fn create_stash(repo_dir: &Path) -> io::Result<()> {
File::create(repo_dir.join("readme.md"))?.sync_all()?;
barrier();
Command::new("git")
.args(&["stash", "--all"])
.current_dir(repo_dir)
.output()?;
barrier();
Ok(())
}
fn create_untracked(repo_dir: &Path) -> io::Result<()> {
File::create(repo_dir.join("license"))?.sync_all()?;
Ok(())
}
fn create_modified(repo_dir: &Path) -> io::Result<()> {
File::create(repo_dir.join("readme.md"))?.sync_all()?;
Ok(())
}
fn create_staged(repo_dir: &Path) -> io::Result<()> {
File::create(repo_dir.join("license"))?.sync_all()?;
Command::new("git")
.args(&["add", "."])
.current_dir(repo_dir)
.output()?;
barrier();
Ok(())
}
fn create_renamed(repo_dir: &Path) -> io::Result<()> {
Command::new("git")
.args(&["mv", "readme.md", "readme.md.bak"])
.current_dir(repo_dir)
.output()?;
barrier();
Command::new("git")
.args(&["add", "-A"])
.current_dir(repo_dir)
.output()?;
barrier();
Ok(())
}
fn create_deleted(repo_dir: &Path) -> io::Result<()> {
fs::remove_file(repo_dir.join("readme.md"))?;
Ok(())
}
}

View File

@ -86,7 +86,7 @@ fn format_go_version(go_stdout: &str) -> Option<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io; use std::io;
@ -95,7 +95,7 @@ mod tests {
fn folder_without_go_files() -> io::Result<()> { fn folder_without_go_files() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("golang", dir.path(), None); let actual = ModuleRenderer::new("golang").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -107,7 +107,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.go"))?.sync_all()?; File::create(dir.path().join("main.go"))?.sync_all()?;
let actual = render_module("golang", dir.path(), None); let actual = ModuleRenderer::new("golang").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1"))); let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -119,7 +119,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("go.mod"))?.sync_all()?; File::create(dir.path().join("go.mod"))?.sync_all()?;
let actual = render_module("golang", dir.path(), None); let actual = ModuleRenderer::new("golang").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1"))); let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -131,7 +131,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("go.sum"))?.sync_all()?; File::create(dir.path().join("go.sum"))?.sync_all()?;
let actual = render_module("golang", dir.path(), None); let actual = ModuleRenderer::new("golang").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1"))); let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -144,7 +144,7 @@ mod tests {
let godeps = dir.path().join("Godeps"); let godeps = dir.path().join("Godeps");
fs::create_dir_all(&godeps)?; fs::create_dir_all(&godeps)?;
let actual = render_module("golang", dir.path(), None); let actual = ModuleRenderer::new("golang").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1"))); let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -156,7 +156,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("glide.yaml"))?.sync_all()?; File::create(dir.path().join("glide.yaml"))?.sync_all()?;
let actual = render_module("golang", dir.path(), None); let actual = ModuleRenderer::new("golang").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1"))); let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -168,7 +168,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("Gopkg.yml"))?.sync_all()?; File::create(dir.path().join("Gopkg.yml"))?.sync_all()?;
let actual = render_module("golang", dir.path(), None); let actual = ModuleRenderer::new("golang").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1"))); let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -179,7 +179,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("Gopkg.lock"))?.sync_all()?; File::create(dir.path().join("Gopkg.lock"))?.sync_all()?;
let actual = render_module("golang", dir.path(), None); let actual = ModuleRenderer::new("golang").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1"))); let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -189,7 +189,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join(".go-version"))?.sync_all()?; File::create(dir.path().join(".go-version"))?.sync_all()?;
let actual = render_module("golang", dir.path(), None); let actual = ModuleRenderer::new("golang").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1"))); let expected = Some(format!("via {} ", Color::Cyan.bold().paint("🐹 v1.12.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()

View File

@ -77,7 +77,7 @@ fn format_helm_version(helm_stdout: &str) -> Option<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -86,7 +86,7 @@ mod tests {
fn folder_without_helm_files() -> io::Result<()> { fn folder_without_helm_files() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("helm", dir.path(), None); let actual = ModuleRenderer::new("helm").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -98,7 +98,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("helmfile.yaml"))?.sync_all()?; File::create(dir.path().join("helmfile.yaml"))?.sync_all()?;
let actual = render_module("helm", dir.path(), None); let actual = ModuleRenderer::new("helm").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::White.bold().paint("⎈ v3.1.1"))); let expected = Some(format!("via {} ", Color::White.bold().paint("⎈ v3.1.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -110,7 +110,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("Chart.yaml"))?.sync_all()?; File::create(dir.path().join("Chart.yaml"))?.sync_all()?;
let actual = render_module("helm", dir.path(), None); let actual = ModuleRenderer::new("helm").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::White.bold().paint("⎈ v3.1.1"))); let expected = Some(format!("via {} ", Color::White.bold().paint("⎈ v3.1.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);

View File

@ -92,3 +92,228 @@ fn get_graphemes(text: &str, length: usize) -> String {
fn graphemes_len(text: &str) -> usize { fn graphemes_len(text: &str) -> usize {
UnicodeSegmentation::graphemes(&text[..], true).count() UnicodeSegmentation::graphemes(&text[..], true).count()
} }
#[cfg(test)]
mod tests {
use ansi_term::{Color, Style};
use std::fs;
use std::io;
use std::path::Path;
use std::process::Command;
use crate::test::{fixture_repo, FixtureProvider, ModuleRenderer};
enum Expect<'a> {
BranchName(&'a str),
Empty,
NoTruncation,
Symbol(&'a str),
Style(Style),
TruncationSymbol(&'a str),
}
#[test]
fn show_nothing_on_empty_dir() -> io::Result<()> {
let repo_dir = tempfile::tempdir()?;
let actual = ModuleRenderer::new("hg_branch")
.path(repo_dir.path())
.collect();
let expected = None;
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
#[ignore]
fn test_hg_get_branch_fails() -> io::Result<()> {
let tempdir = tempfile::tempdir()?;
// Create a fake corrupted mercurial repo.
let hgdir = tempdir.path().join(".hg");
fs::create_dir(&hgdir)?;
fs::write(&hgdir.join("requires"), "fake-corrupted-repo")?;
expect_hg_branch_with_config(
tempdir.path(),
None,
&[Expect::BranchName(&"default"), Expect::NoTruncation],
)?;
tempdir.close()
}
#[test]
#[ignore]
fn test_hg_get_branch_autodisabled() -> io::Result<()> {
let tempdir = tempfile::tempdir()?;
expect_hg_branch_with_config(tempdir.path(), None, &[Expect::Empty])?;
tempdir.close()
}
#[test]
#[ignore]
fn test_hg_bookmark() -> io::Result<()> {
let tempdir = fixture_repo(FixtureProvider::HG)?;
let repo_dir = tempdir.path();
run_hg(&["bookmark", "bookmark-101"], &repo_dir)?;
expect_hg_branch_with_config(
&repo_dir,
None,
&[Expect::BranchName(&"bookmark-101"), Expect::NoTruncation],
)?;
tempdir.close()
}
#[test]
#[ignore]
fn test_default_truncation_symbol() -> io::Result<()> {
let tempdir = fixture_repo(FixtureProvider::HG)?;
let repo_dir = tempdir.path();
run_hg(&["branch", "-f", "branch-name-101"], &repo_dir)?;
run_hg(
&[
"commit",
"-m",
"empty commit 101",
"-u",
"fake user <fake@user>",
],
&repo_dir,
)?;
expect_hg_branch_with_config(
&repo_dir,
Some(toml::toml! {
[hg_branch]
truncation_length = 14
}),
&[Expect::BranchName(&"branch-name-10")],
)?;
tempdir.close()
}
#[test]
#[ignore]
fn test_configured_symbols() -> io::Result<()> {
let tempdir = fixture_repo(FixtureProvider::HG)?;
let repo_dir = tempdir.path();
run_hg(&["branch", "-f", "branch-name-121"], &repo_dir)?;
run_hg(
&[
"commit",
"-m",
"empty commit 121",
"-u",
"fake user <fake@user>",
],
&repo_dir,
)?;
expect_hg_branch_with_config(
&repo_dir,
Some(toml::toml! {
[hg_branch]
symbol = "B "
truncation_length = 14
truncation_symbol = "%"
}),
&[
Expect::BranchName(&"branch-name-12"),
Expect::Symbol(&"B"),
Expect::TruncationSymbol(&"%"),
],
)?;
tempdir.close()
}
#[test]
#[ignore]
fn test_configured_style() -> io::Result<()> {
let tempdir = fixture_repo(FixtureProvider::HG)?;
let repo_dir = tempdir.path();
run_hg(&["branch", "-f", "branch-name-131"], &repo_dir)?;
run_hg(
&[
"commit",
"-m",
"empty commit 131",
"-u",
"fake user <fake@user>",
],
&repo_dir,
)?;
expect_hg_branch_with_config(
&repo_dir,
Some(toml::toml! {
[hg_branch]
style = "underline blue"
}),
&[
Expect::BranchName(&"branch-name-131"),
Expect::Style(Color::Blue.underline()),
Expect::TruncationSymbol(&""),
],
)?;
tempdir.close()
}
fn expect_hg_branch_with_config(
repo_dir: &Path,
config: Option<toml::Value>,
expectations: &[Expect],
) -> io::Result<()> {
let actual = ModuleRenderer::new("hg_branch")
.path(repo_dir.to_str().unwrap())
.config(config.unwrap_or_else(|| {
toml::toml! {
[hg_branch]
}
}))
.collect();
let mut expect_branch_name = "default";
let mut expect_style = Color::Purple.bold();
let mut expect_symbol = "\u{e0a0}";
let mut expect_truncation_symbol = "";
for expect in expectations {
match expect {
Expect::Empty => {
assert_eq!(None, actual);
return Ok(());
}
Expect::Symbol(symbol) => {
expect_symbol = symbol;
}
Expect::TruncationSymbol(truncation_symbol) => {
expect_truncation_symbol = truncation_symbol;
}
Expect::NoTruncation => {
expect_truncation_symbol = "";
}
Expect::BranchName(branch_name) => {
expect_branch_name = branch_name;
}
Expect::Style(style) => expect_style = *style,
}
}
let expected = Some(format!(
"on {} ",
expect_style.paint(format!(
"{} {}{}",
expect_symbol, expect_branch_name, expect_truncation_symbol
)),
));
assert_eq!(expected, actual);
Ok(())
}
fn run_hg(args: &[&str], repo_dir: &Path) -> io::Result<()> {
Command::new("hg")
.args(args)
.current_dir(&repo_dir)
.output()?;
Ok(())
}
}

View File

@ -1,5 +1,3 @@
use std::env;
use super::{Context, Module}; use super::{Context, Module};
use std::ffi::OsString; use std::ffi::OsString;
@ -16,7 +14,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let mut module = context.new_module("hostname"); let mut module = context.new_module("hostname");
let config: HostnameConfig = HostnameConfig::try_load(module.config); let config: HostnameConfig = HostnameConfig::try_load(module.config);
let ssh_connection = env::var("SSH_CONNECTION").ok(); let ssh_connection = context.get_env("SSH_CONNECTION");
if config.ssh_only && ssh_connection.is_none() { if config.ssh_only && ssh_connection.is_none() {
return None; return None;
} }
@ -66,3 +64,108 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
#[cfg(test)]
mod tests {
use crate::test::ModuleRenderer;
use ansi_term::{Color, Style};
use std::io;
macro_rules! get_hostname {
() => {
if let Some(hostname) = gethostname::gethostname().into_string().ok() {
hostname
} else {
println!(
"hostname was not tested because gethostname failed! \
This could be caused by your hostname containing invalid UTF."
);
return Ok(());
}
};
}
#[test]
fn ssh_only_false() -> io::Result<()> {
let hostname = get_hostname!();
let actual = ModuleRenderer::new("hostname")
.config(toml::toml! {
[hostname]
ssh_only = false
trim_at = ""
})
.collect();
let expected = Some(format!("on {} ", style().paint(hostname)));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn no_ssh() -> io::Result<()> {
let actual = ModuleRenderer::new("hostname")
.config(toml::toml! {
[hostname]
ssh_only = true
})
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn ssh() -> io::Result<()> {
let hostname = get_hostname!();
let actual = ModuleRenderer::new("hostname")
.config(toml::toml! {
[hostname]
ssh_only = true
trim_at = ""
})
.env("SSH_CONNECTION", "something")
.collect();
let expected = Some(format!("on {} ", style().paint(hostname)));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn no_trim_at() -> io::Result<()> {
let hostname = get_hostname!();
let actual = ModuleRenderer::new("hostname")
.config(toml::toml! {
[hostname]
ssh_only = false
trim_at = ""
})
.collect();
let expected = Some(format!("on {} ", style().paint(hostname)));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn trim_at() -> io::Result<()> {
let hostname = get_hostname!();
let (remainder, trim_at) = hostname.split_at(1);
let actual = ModuleRenderer::new("hostname")
.config(toml::toml! {
[hostname]
ssh_only = false
trim_at = trim_at
})
.collect();
let expected = Some(format!("on {} ", style().paint(remainder)));
assert_eq!(expected, actual);
Ok(())
}
fn style() -> Style {
Color::Green.bold().dimmed()
}
}

View File

@ -24,7 +24,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
return None; return None;
} }
let java_version = get_java_version()?; let java_version = get_java_version(context)?;
let mut module = context.new_module("java"); let mut module = context.new_module("java");
let config: JavaConfig = JavaConfig::try_load(module.config); let config: JavaConfig = JavaConfig::try_load(module.config);
@ -57,10 +57,10 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
fn get_java_version() -> Option<String> { fn get_java_version(context: &Context) -> Option<String> {
let java_command = match std::env::var("JAVA_HOME") { let java_command = match context.get_env("JAVA_HOME") {
Ok(java_home) => format!("{}/bin/java", java_home), Some(java_home) => format!("{}/bin/java", java_home),
Err(_) => String::from("java"), None => String::from("java"),
}; };
let output = utils::exec_cmd(&java_command.as_str(), &["-Xinternalversion"])?; let output = utils::exec_cmd(&java_command.as_str(), &["-Xinternalversion"])?;
@ -84,7 +84,7 @@ fn parse_java_version(java_version: &str) -> Option<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -156,7 +156,7 @@ mod tests {
#[test] #[test]
fn folder_without_java_file() -> io::Result<()> { fn folder_without_java_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("java", dir.path(), None); let actual = ModuleRenderer::new("java").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -166,7 +166,7 @@ mod tests {
fn folder_with_java_file() -> io::Result<()> { fn folder_with_java_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("Main.java"))?.sync_all()?; File::create(dir.path().join("Main.java"))?.sync_all()?;
let actual = render_module("java", dir.path(), None); let actual = ModuleRenderer::new("java").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2"))); let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -176,7 +176,7 @@ mod tests {
fn folder_with_class_file() -> io::Result<()> { fn folder_with_class_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("Main.class"))?.sync_all()?; File::create(dir.path().join("Main.class"))?.sync_all()?;
let actual = render_module("java", dir.path(), None); let actual = ModuleRenderer::new("java").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2"))); let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -186,7 +186,7 @@ mod tests {
fn folder_with_gradle_file() -> io::Result<()> { fn folder_with_gradle_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("build.gradle"))?.sync_all()?; File::create(dir.path().join("build.gradle"))?.sync_all()?;
let actual = render_module("java", dir.path(), None); let actual = ModuleRenderer::new("java").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2"))); let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -196,7 +196,7 @@ mod tests {
fn folder_with_jar_archive() -> io::Result<()> { fn folder_with_jar_archive() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("test.jar"))?.sync_all()?; File::create(dir.path().join("test.jar"))?.sync_all()?;
let actual = render_module("java", dir.path(), None); let actual = ModuleRenderer::new("java").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2"))); let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -206,7 +206,7 @@ mod tests {
fn folder_with_pom_file() -> io::Result<()> { fn folder_with_pom_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("pom.xml"))?.sync_all()?; File::create(dir.path().join("pom.xml"))?.sync_all()?;
let actual = render_module("java", dir.path(), None); let actual = ModuleRenderer::new("java").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2"))); let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -216,7 +216,7 @@ mod tests {
fn folder_with_gradle_kotlin_build_file() -> io::Result<()> { fn folder_with_gradle_kotlin_build_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("build.gradle.kts"))?.sync_all()?; File::create(dir.path().join("build.gradle.kts"))?.sync_all()?;
let actual = render_module("java", dir.path(), None); let actual = ModuleRenderer::new("java").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2"))); let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -226,7 +226,7 @@ mod tests {
fn folder_with_sbt_build_file() -> io::Result<()> { fn folder_with_sbt_build_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("build.gradle.kts"))?.sync_all()?; File::create(dir.path().join("build.gradle.kts"))?.sync_all()?;
let actual = render_module("java", dir.path(), None); let actual = ModuleRenderer::new("java").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2"))); let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -236,7 +236,7 @@ mod tests {
fn folder_with_java_version_file() -> io::Result<()> { fn folder_with_java_version_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join(".java-version"))?.sync_all()?; File::create(dir.path().join(".java-version"))?.sync_all()?;
let actual = render_module("java", dir.path(), None); let actual = ModuleRenderer::new("java").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2"))); let expected = Some(format!("via {} ", Color::Red.dimmed().paint("☕ v13.0.2")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()

View File

@ -52,3 +52,67 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
#[cfg(test)]
mod test {
use crate::test::ModuleRenderer;
use ansi_term::Color;
use std::io;
#[test]
fn config_blank_job_0() -> io::Result<()> {
let actual = ModuleRenderer::new("jobs").jobs(0).collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_blank_job_1() -> io::Result<()> {
let actual = ModuleRenderer::new("jobs").jobs(1).collect();
let expected = Some(format!("{} ", Color::Blue.bold().paint("")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_blank_job_2() -> io::Result<()> {
let actual = ModuleRenderer::new("jobs").jobs(2).collect();
let expected = Some(format!("{} ", Color::Blue.bold().paint("✦2")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_2_job_2() -> io::Result<()> {
let actual = ModuleRenderer::new("jobs")
.config(toml::toml! {
[jobs]
threshold = 2
})
.jobs(2)
.collect();
let expected = Some(format!("{} ", Color::Blue.bold().paint("")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_2_job_3() -> io::Result<()> {
let actual = ModuleRenderer::new("jobs")
.config(toml::toml! {
[jobs]
threshold = 2
})
.jobs(3)
.collect();
let expected = Some(format!("{} ", Color::Blue.bold().paint("✦3")));
assert_eq!(expected, actual);
Ok(())
}
}

View File

@ -72,7 +72,7 @@ fn format_julia_version(julia_stdout: &str) -> Option<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -81,7 +81,7 @@ mod tests {
fn folder_without_julia_file() -> io::Result<()> { fn folder_without_julia_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("julia", dir.path(), None); let actual = ModuleRenderer::new("julia").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -93,7 +93,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("hello.jl"))?.sync_all()?; File::create(dir.path().join("hello.jl"))?.sync_all()?;
let actual = render_module("julia", dir.path(), None); let actual = ModuleRenderer::new("julia").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Purple.bold().paint("ஃ v1.4.0"))); let expected = Some(format!("via {} ", Color::Purple.bold().paint("ஃ v1.4.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -105,7 +105,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("Project.toml"))?.sync_all()?; File::create(dir.path().join("Project.toml"))?.sync_all()?;
let actual = render_module("julia", dir.path(), None); let actual = ModuleRenderer::new("julia").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Purple.bold().paint("ஃ v1.4.0"))); let expected = Some(format!("via {} ", Color::Purple.bold().paint("ஃ v1.4.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -117,7 +117,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("Manifest.toml"))?.sync_all()?; File::create(dir.path().join("Manifest.toml"))?.sync_all()?;
let actual = render_module("julia", dir.path(), None); let actual = ModuleRenderer::new("julia").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Purple.bold().paint("ஃ v1.4.0"))); let expected = Some(format!("via {} ", Color::Purple.bold().paint("ஃ v1.4.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);

View File

@ -42,11 +42,11 @@ fn parse_kubectl_file(filename: &path::PathBuf) -> Option<(String, String)> {
} }
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let kube_cfg = match env::var("KUBECONFIG") { let kube_cfg = match context.get_env("KUBECONFIG") {
Ok(paths) => env::split_paths(&paths) Some(paths) => env::split_paths(&paths)
.filter_map(|filename| parse_kubectl_file(&filename)) .filter_map(|filename| parse_kubectl_file(&filename))
.next(), .next(),
Err(_) => { None => {
let filename = dirs_next::home_dir()?.join(".kube").join("config"); let filename = dirs_next::home_dir()?.join(".kube").join("config");
parse_kubectl_file(&filename) parse_kubectl_file(&filename)
} }

View File

@ -68,7 +68,7 @@ fn parse_nim_version(version_cmd_output: &str) -> Option<&str> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::parse_nim_version; use super::parse_nim_version;
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -100,7 +100,7 @@ mod tests {
fn folder_without_nim() -> io::Result<()> { fn folder_without_nim() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("nim.txt"))?.sync_all()?; File::create(dir.path().join("nim.txt"))?.sync_all()?;
let actual = render_module("nim", dir.path(), None); let actual = ModuleRenderer::new("nim").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -110,7 +110,7 @@ mod tests {
fn folder_with_nimble_file() -> io::Result<()> { fn folder_with_nimble_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.nimble"))?.sync_all()?; File::create(dir.path().join("main.nimble"))?.sync_all()?;
let actual = render_module("nim", dir.path(), None); let actual = ModuleRenderer::new("nim").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("👑 v1.2.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("👑 v1.2.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -120,7 +120,7 @@ mod tests {
fn folder_with_nim_file() -> io::Result<()> { fn folder_with_nim_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.nim"))?.sync_all()?; File::create(dir.path().join("main.nim"))?.sync_all()?;
let actual = render_module("nim", dir.path(), None); let actual = ModuleRenderer::new("nim").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("👑 v1.2.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("👑 v1.2.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -130,7 +130,7 @@ mod tests {
fn folder_with_nims_file() -> io::Result<()> { fn folder_with_nims_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.nims"))?.sync_all()?; File::create(dir.path().join("main.nims"))?.sync_all()?;
let actual = render_module("nim", dir.path(), None); let actual = ModuleRenderer::new("nim").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("👑 v1.2.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("👑 v1.2.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -140,7 +140,7 @@ mod tests {
fn folder_with_cfg_file() -> io::Result<()> { fn folder_with_cfg_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("cfg.nim"))?.sync_all()?; File::create(dir.path().join("cfg.nim"))?.sync_all()?;
let actual = render_module("nim", dir.path(), None); let actual = ModuleRenderer::new("nim").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("👑 v1.2.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("👑 v1.2.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()

View File

@ -1,5 +1,3 @@
use std::env;
use super::{Context, Module, RootModuleConfig}; use super::{Context, Module, RootModuleConfig};
use crate::configs::nix_shell::NixShellConfig; use crate::configs::nix_shell::NixShellConfig;
@ -23,8 +21,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let mut module = context.new_module("nix_shell"); let mut module = context.new_module("nix_shell");
let config: NixShellConfig = NixShellConfig::try_load(module.config); let config: NixShellConfig = NixShellConfig::try_load(module.config);
let shell_name = env::var("name").ok(); let shell_name = context.get_env("name");
let shell_type = env::var("IN_NIX_SHELL").ok()?; let shell_type = context.get_env("IN_NIX_SHELL")?;
let shell_type_format = match shell_type.as_ref() { let shell_type_format = match shell_type.as_ref() {
"impure" => config.impure_msg, "impure" => config.impure_msg,
"pure" => config.pure_msg, "pure" => config.pure_msg,
@ -61,3 +59,82 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
#[cfg(test)]
mod tests {
use crate::test::ModuleRenderer;
use ansi_term::Color;
use std::io;
#[test]
fn no_env_variables() -> io::Result<()> {
let actual = ModuleRenderer::new("nix_shell").collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn invalid_env_variables() -> io::Result<()> {
let actual = ModuleRenderer::new("nix_shell")
.env("IN_NIX_SHELL", "something_wrong")
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn pure_shell() -> io::Result<()> {
let actual = ModuleRenderer::new("nix_shell")
.env("IN_NIX_SHELL", "pure")
.collect();
let expected = Some(format!("via {} ", Color::Blue.bold().paint("❄️ pure")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn impure_shell() -> io::Result<()> {
let actual = ModuleRenderer::new("nix_shell")
.env("IN_NIX_SHELL", "impure")
.collect();
let expected = Some(format!("via {} ", Color::Blue.bold().paint("❄️ impure")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn pure_shell_name() -> io::Result<()> {
let actual = ModuleRenderer::new("nix_shell")
.env("IN_NIX_SHELL", "pure")
.env("name", "starship")
.collect();
let expected = Some(format!(
"via {} ",
Color::Blue.bold().paint("❄️ pure (starship)")
));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn impure_shell_name() -> io::Result<()> {
let actual = ModuleRenderer::new("nix_shell")
.env("IN_NIX_SHELL", "impure")
.env("name", "starship")
.collect();
let expected = Some(format!(
"via {} ",
Color::Blue.bold().paint("❄️ impure (starship)")
));
assert_eq!(expected, actual);
Ok(())
}
}

View File

@ -61,7 +61,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io; use std::io;
@ -69,7 +69,7 @@ mod tests {
#[test] #[test]
fn folder_without_node_files() -> io::Result<()> { fn folder_without_node_files() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("nodejs", dir.path(), None); let actual = ModuleRenderer::new("nodejs").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -80,7 +80,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("package.json"))?.sync_all()?; File::create(dir.path().join("package.json"))?.sync_all()?;
let actual = render_module("nodejs", dir.path(), None); let actual = ModuleRenderer::new("nodejs").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0"))); let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -93,7 +93,7 @@ mod tests {
let esy_lock = dir.path().join("esy.lock"); let esy_lock = dir.path().join("esy.lock");
fs::create_dir_all(&esy_lock)?; fs::create_dir_all(&esy_lock)?;
let actual = render_module("nodejs", dir.path(), None); let actual = ModuleRenderer::new("nodejs").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -104,7 +104,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join(".node-version"))?.sync_all()?; File::create(dir.path().join(".node-version"))?.sync_all()?;
let actual = render_module("nodejs", dir.path(), None); let actual = ModuleRenderer::new("nodejs").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0"))); let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -115,7 +115,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("index.js"))?.sync_all()?; File::create(dir.path().join("index.js"))?.sync_all()?;
let actual = render_module("nodejs", dir.path(), None); let actual = ModuleRenderer::new("nodejs").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0"))); let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -126,7 +126,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("index.mjs"))?.sync_all()?; File::create(dir.path().join("index.mjs"))?.sync_all()?;
let actual = render_module("nodejs", dir.path(), None); let actual = ModuleRenderer::new("nodejs").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0"))); let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -137,7 +137,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("index.cjs"))?.sync_all()?; File::create(dir.path().join("index.cjs"))?.sync_all()?;
let actual = render_module("nodejs", dir.path(), None); let actual = ModuleRenderer::new("nodejs").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0"))); let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -148,7 +148,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("index.ts"))?.sync_all()?; File::create(dir.path().join("index.ts"))?.sync_all()?;
let actual = render_module("nodejs", dir.path(), None); let actual = ModuleRenderer::new("nodejs").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0"))); let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -160,7 +160,7 @@ mod tests {
let node_modules = dir.path().join("node_modules"); let node_modules = dir.path().join("node_modules");
fs::create_dir_all(&node_modules)?; fs::create_dir_all(&node_modules)?;
let actual = render_module("nodejs", dir.path(), None); let actual = ModuleRenderer::new("nodejs").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0"))); let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()

View File

@ -69,7 +69,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io; use std::io;
@ -77,7 +77,7 @@ mod tests {
#[test] #[test]
fn folder_without_ocaml_file() -> io::Result<()> { fn folder_without_ocaml_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("ocaml", dir.path(), None); let actual = ModuleRenderer::new("ocaml").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -88,7 +88,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.opam"))?.sync_all()?; File::create(dir.path().join("any.opam"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None); let actual = ModuleRenderer::new("ocaml").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -99,7 +99,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
fs::create_dir_all(dir.path().join("_opam"))?; fs::create_dir_all(dir.path().join("_opam"))?;
let actual = render_module("ocaml", dir.path(), None); let actual = ModuleRenderer::new("ocaml").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -114,7 +114,7 @@ mod tests {
dir.path().join("package.lock"), dir.path().join("package.lock"),
"{\"dependencies\": {\"ocaml\": \"4.8.1000\"}}", "{\"dependencies\": {\"ocaml\": \"4.8.1000\"}}",
)?; )?;
let actual = render_module("ocaml", dir.path(), None); let actual = ModuleRenderer::new("ocaml").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.08.1"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.08.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -125,7 +125,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("dune"))?.sync_all()?; File::create(dir.path().join("dune"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None); let actual = ModuleRenderer::new("ocaml").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -136,7 +136,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("dune-project"))?.sync_all()?; File::create(dir.path().join("dune-project"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None); let actual = ModuleRenderer::new("ocaml").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -147,7 +147,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("jbuild"))?.sync_all()?; File::create(dir.path().join("jbuild"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None); let actual = ModuleRenderer::new("ocaml").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -158,7 +158,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("jbuild-ignore"))?.sync_all()?; File::create(dir.path().join("jbuild-ignore"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None); let actual = ModuleRenderer::new("ocaml").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -169,7 +169,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join(".merlin"))?.sync_all()?; File::create(dir.path().join(".merlin"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None); let actual = ModuleRenderer::new("ocaml").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -180,7 +180,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.ml"))?.sync_all()?; File::create(dir.path().join("any.ml"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None); let actual = ModuleRenderer::new("ocaml").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -191,7 +191,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.mli"))?.sync_all()?; File::create(dir.path().join("any.mli"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None); let actual = ModuleRenderer::new("ocaml").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -202,7 +202,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.re"))?.sync_all()?; File::create(dir.path().join("any.re"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None); let actual = ModuleRenderer::new("ocaml").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -213,7 +213,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.rei"))?.sync_all()?; File::create(dir.path().join("any.rei"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None); let actual = ModuleRenderer::new("ocaml").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()

View File

@ -198,7 +198,7 @@ fn format_version(version: &str) -> String {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -741,12 +741,15 @@ end";
contains: Option<&str>, contains: Option<&str>,
config: Option<toml::Value>, config: Option<toml::Value>,
) -> io::Result<()> { ) -> io::Result<()> {
let starship_config = Some(config.unwrap_or(toml::toml! { let starship_config = config.unwrap_or(toml::toml! {
[package] [package]
disabled = false disabled = false
})); });
let actual = render_module("package", project_dir.path(), starship_config); let actual = ModuleRenderer::new("package")
.path(project_dir.path())
.config(starship_config)
.collect();
let text = String::from(contains.unwrap_or("")); let text = String::from(contains.unwrap_or(""));
let expected = Some(format!( let expected = Some(format!(
"is {} ", "is {} ",

View File

@ -64,7 +64,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -73,7 +73,7 @@ mod tests {
fn folder_without_perl_files() -> io::Result<()> { fn folder_without_perl_files() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("perl", dir.path(), None); let actual = ModuleRenderer::new("perl").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -85,7 +85,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("Makefile.PL"))?.sync_all()?; File::create(dir.path().join("Makefile.PL"))?.sync_all()?;
let actual = render_module("perl", dir.path(), None); let actual = ModuleRenderer::new("perl").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
@ -100,7 +100,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("Build.PL"))?.sync_all()?; File::create(dir.path().join("Build.PL"))?.sync_all()?;
let actual = render_module("perl", dir.path(), None); let actual = ModuleRenderer::new("perl").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
@ -115,7 +115,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("cpanfile"))?.sync_all()?; File::create(dir.path().join("cpanfile"))?.sync_all()?;
let actual = render_module("perl", dir.path(), None); let actual = ModuleRenderer::new("perl").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
@ -130,7 +130,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("cpanfile.snapshot"))?.sync_all()?; File::create(dir.path().join("cpanfile.snapshot"))?.sync_all()?;
let actual = render_module("perl", dir.path(), None); let actual = ModuleRenderer::new("perl").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
@ -145,7 +145,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("META.json"))?.sync_all()?; File::create(dir.path().join("META.json"))?.sync_all()?;
let actual = render_module("perl", dir.path(), None); let actual = ModuleRenderer::new("perl").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
@ -160,7 +160,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("META.yml"))?.sync_all()?; File::create(dir.path().join("META.yml"))?.sync_all()?;
let actual = render_module("perl", dir.path(), None); let actual = ModuleRenderer::new("perl").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
@ -175,7 +175,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join(".perl-version"))?.sync_all()?; File::create(dir.path().join(".perl-version"))?.sync_all()?;
let actual = render_module("perl", dir.path(), None); let actual = ModuleRenderer::new("perl").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
@ -190,7 +190,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.pl"))?.sync_all()?; File::create(dir.path().join("any.pl"))?.sync_all()?;
let actual = render_module("perl", dir.path(), None); let actual = ModuleRenderer::new("perl").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
@ -205,7 +205,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.pm"))?.sync_all()?; File::create(dir.path().join("any.pm"))?.sync_all()?;
let actual = render_module("perl", dir.path(), None); let actual = ModuleRenderer::new("perl").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
@ -220,7 +220,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.pod"))?.sync_all()?; File::create(dir.path().join("any.pod"))?.sync_all()?;
let actual = render_module("perl", dir.path(), None); let actual = ModuleRenderer::new("perl").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",

View File

@ -72,7 +72,7 @@ fn format_php_version(php_version: &str) -> Option<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -87,7 +87,7 @@ mod tests {
fn folder_without_php_files() -> io::Result<()> { fn folder_without_php_files() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("php", dir.path(), None); let actual = ModuleRenderer::new("php").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -99,7 +99,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("composer.json"))?.sync_all()?; File::create(dir.path().join("composer.json"))?.sync_all()?;
let actual = render_module("php", dir.path(), None); let actual = ModuleRenderer::new("php").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
@ -114,7 +114,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join(".php-version"))?.sync_all()?; File::create(dir.path().join(".php-version"))?.sync_all()?;
let actual = render_module("php", dir.path(), None); let actual = ModuleRenderer::new("php").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
@ -129,7 +129,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.php"))?.sync_all()?; File::create(dir.path().join("any.php"))?.sync_all()?;
let actual = render_module("php", dir.path(), None); let actual = ModuleRenderer::new("php").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",

View File

@ -55,7 +55,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -63,7 +63,7 @@ mod tests {
#[test] #[test]
fn folder_without_purescript_files() -> io::Result<()> { fn folder_without_purescript_files() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("purescript", dir.path(), None); let actual = ModuleRenderer::new("purescript").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -74,7 +74,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("Main.purs"))?.sync_all()?; File::create(dir.path().join("Main.purs"))?.sync_all()?;
let actual = render_module("purescript", dir.path(), None); let actual = ModuleRenderer::new("purescript").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::White.bold().paint("<=> v0.13.5"))); let expected = Some(format!("via {} ", Color::White.bold().paint("<=> v0.13.5")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -85,7 +85,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("spago.dhall"))?.sync_all()?; File::create(dir.path().join("spago.dhall"))?.sync_all()?;
let actual = render_module("purescript", dir.path(), None); let actual = ModuleRenderer::new("purescript").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::White.bold().paint("<=> v0.13.5"))); let expected = Some(format!("via {} ", Color::White.bold().paint("<=> v0.13.5")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()

View File

@ -1,4 +1,3 @@
use std::env;
use std::path::Path; use std::path::Path;
use super::{Context, Module, RootModuleConfig}; use super::{Context, Module, RootModuleConfig};
@ -36,7 +35,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
} }
}; };
let is_venv = env::var("VIRTUAL_ENV").ok().is_some(); let is_venv = context.get_env("VIRTUAL_ENV").is_some();
if !is_py_project && !is_venv { if !is_py_project && !is_venv {
return None; return None;
@ -48,7 +47,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let version = get_python_version(&config.python_binary)?; let version = get_python_version(&config.python_binary)?;
format_python_version(&version) format_python_version(&version)
}; };
let virtual_env = get_python_virtual_env(); let virtual_env = get_python_virtual_env(context);
let parsed = StringFormatter::new(config.format).and_then(|formatter| { let parsed = StringFormatter::new(config.format).and_then(|formatter| {
formatter formatter
@ -102,8 +101,8 @@ fn format_python_version(python_stdout: &str) -> String {
) )
} }
fn get_python_virtual_env() -> Option<String> { fn get_python_virtual_env(context: &Context) -> Option<String> {
env::var("VIRTUAL_ENV").ok().and_then(|venv| { context.get_env("VIRTUAL_ENV").and_then(|venv| {
Path::new(&venv) Path::new(&venv)
.file_name() .file_name()
.map(|filename| String::from(filename.to_str().unwrap_or(""))) .map(|filename| String::from(filename.to_str().unwrap_or("")))
@ -113,7 +112,7 @@ fn get_python_virtual_env() -> Option<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -133,7 +132,7 @@ mod tests {
#[test] #[test]
fn folder_without_python_files() -> io::Result<()> { fn folder_without_python_files() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("python", dir.path(), None); let actual = ModuleRenderer::new("python").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -230,7 +229,10 @@ mod tests {
[python] [python]
scan_for_pyfiles = false scan_for_pyfiles = false
}; };
let actual = render_module("python", dir.path(), Some(config)); let actual = ModuleRenderer::new("python")
.path(dir.path())
.config(config)
.collect();
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
} }
@ -258,19 +260,68 @@ mod tests {
dir.close() dir.close()
} }
#[test]
fn with_virtual_env() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.py"))?.sync_all()?;
let actual = ModuleRenderer::new("python")
.path(dir.path())
.env("VIRTUAL_ENV", "/foo/bar/my_venv")
.collect();
let expected = Some(format!(
"via {} ",
Color::Yellow.bold().paint("🐍 v2.7.17 (my_venv)")
));
assert_eq!(actual, expected);
dir.close()
}
#[test]
fn with_active_venv() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let actual = ModuleRenderer::new("python")
.path(dir.path())
.env("VIRTUAL_ENV", "/foo/bar/my_venv")
.collect();
let expected = Some(format!(
"via {} ",
Color::Yellow.bold().paint("🐍 v2.7.17 (my_venv)")
));
assert_eq!(actual, expected);
dir.close()
}
fn check_python2_renders(dir: &tempfile::TempDir, starship_config: Option<toml::Value>) { fn check_python2_renders(dir: &tempfile::TempDir, starship_config: Option<toml::Value>) {
let actual = render_module("python", dir.path(), starship_config); let config = starship_config.unwrap_or(toml::toml! {
[python]
python_binary = "python"
});
let actual = ModuleRenderer::new("python")
.path(dir.path())
.config(config)
.collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐍 v2.7.17"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐍 v2.7.17")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
} }
fn check_python3_renders(dir: &tempfile::TempDir, starship_config: Option<toml::Value>) { fn check_python3_renders(dir: &tempfile::TempDir, starship_config: Option<toml::Value>) {
let config = Some(starship_config.unwrap_or(toml::toml! { let config = starship_config.unwrap_or(toml::toml! {
[python] [python]
python_binary = "python3" python_binary = "python3"
})); });
let actual = ModuleRenderer::new("python")
.path(dir.path())
.config(config)
.collect();
let actual = render_module("python", dir.path(), config);
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐍 v3.8.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐍 v3.8.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
} }

View File

@ -72,7 +72,7 @@ fn format_ruby_version(ruby_version: &str) -> Option<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -81,7 +81,7 @@ mod tests {
fn folder_without_ruby_files() -> io::Result<()> { fn folder_without_ruby_files() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let actual = render_module("ruby", dir.path(), None); let actual = ModuleRenderer::new("ruby").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -93,7 +93,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("Gemfile"))?.sync_all()?; File::create(dir.path().join("Gemfile"))?.sync_all()?;
let actual = render_module("ruby", dir.path(), None); let actual = ModuleRenderer::new("ruby").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Red.bold().paint("💎 v2.5.1"))); let expected = Some(format!("via {} ", Color::Red.bold().paint("💎 v2.5.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -105,7 +105,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join(".ruby-version"))?.sync_all()?; File::create(dir.path().join(".ruby-version"))?.sync_all()?;
let actual = render_module("ruby", dir.path(), None); let actual = ModuleRenderer::new("ruby").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Red.bold().paint("💎 v2.5.1"))); let expected = Some(format!("via {} ", Color::Red.bold().paint("💎 v2.5.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -117,7 +117,7 @@ mod tests {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.rb"))?.sync_all()?; File::create(dir.path().join("any.rb"))?.sync_all()?;
let actual = render_module("ruby", dir.path(), None); let actual = ModuleRenderer::new("ruby").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Red.bold().paint("💎 v2.5.1"))); let expected = Some(format!("via {} ", Color::Red.bold().paint("💎 v2.5.1")));
assert_eq!(expected, actual); assert_eq!(expected, actual);

View File

@ -1,6 +1,6 @@
use std::fs;
use std::path::Path; use std::path::Path;
use std::process::{Command, Output}; use std::process::{Command, Output};
use std::{env, fs};
use super::{Context, Module, RootModuleConfig}; use super::{Context, Module, RootModuleConfig};
@ -72,7 +72,7 @@ fn get_module_version(context: &Context) -> Option<String> {
// - `rustup show` // - `rustup show`
// - `rustup show active-toolchain` // - `rustup show active-toolchain`
// - `rustup which` // - `rustup which`
let module_version = if let Some(toolchain) = env_rustup_toolchain() let module_version = if let Some(toolchain) = env_rustup_toolchain(context)
.or_else(|| execute_rustup_override_list(&context.current_dir)) .or_else(|| execute_rustup_override_list(&context.current_dir))
.or_else(|| find_rust_toolchain_file(&context)) .or_else(|| find_rust_toolchain_file(&context))
{ {
@ -93,8 +93,8 @@ fn get_module_version(context: &Context) -> Option<String> {
Some(module_version) Some(module_version)
} }
fn env_rustup_toolchain() -> Option<String> { fn env_rustup_toolchain(context: &Context) -> Option<String> {
let val = env::var("RUSTUP_TOOLCHAIN").ok()?; let val = context.get_env("RUSTUP_TOOLCHAIN")?;
Some(val.trim().to_owned()) Some(val.trim().to_owned())
} }

View File

@ -1,5 +1,3 @@
use std::env;
use super::{Context, Module}; use super::{Context, Module};
use crate::config::RootModuleConfig; use crate::config::RootModuleConfig;
@ -9,7 +7,7 @@ use crate::formatter::StringFormatter;
const SHLVL_ENV_VAR: &str = "SHLVL"; const SHLVL_ENV_VAR: &str = "SHLVL";
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let shlvl = get_shlvl_value()?; let shlvl = context.get_env(SHLVL_ENV_VAR)?.parse::<i64>().ok()?;
let mut module = context.new_module("shlvl"); let mut module = context.new_module("shlvl");
let config: ShLvlConfig = ShLvlConfig::try_load(module.config); let config: ShLvlConfig = ShLvlConfig::try_load(module.config);
@ -47,7 +45,155 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
#[cfg(test)]
mod tests {
use ansi_term::{Color, Style};
use std::io;
fn get_shlvl_value() -> Option<i64> { use crate::test::ModuleRenderer;
env::var(SHLVL_ENV_VAR).ok()?.parse::<i64>().ok()
use super::SHLVL_ENV_VAR;
fn style() -> Style {
// default style
Color::Yellow.bold()
}
#[test]
fn empty_config() -> io::Result<()> {
let actual = ModuleRenderer::new("shlvl")
.config(toml::toml! {
[shlvl]
})
.env(SHLVL_ENV_VAR, "2")
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn enabled() -> io::Result<()> {
let actual = ModuleRenderer::new("shlvl")
.config(toml::toml! {
[shlvl]
disabled = false
})
.env(SHLVL_ENV_VAR, "2")
.collect();
let expected = Some(format!("{} ", style().paint("↕️ 2")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn no_level() -> io::Result<()> {
let actual = ModuleRenderer::new("shlvl")
.config(toml::toml! {
[shlvl]
disabled = false
})
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn enabled_config_level_1() -> io::Result<()> {
let actual = ModuleRenderer::new("shlvl")
.config(toml::toml! {
[shlvl]
disabled = false
})
.env(SHLVL_ENV_VAR, "1")
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn lower_threshold() -> io::Result<()> {
let actual = ModuleRenderer::new("shlvl")
.config(toml::toml! {
[shlvl]
threshold = 1
disabled = false
})
.env(SHLVL_ENV_VAR, "1")
.collect();
let expected = Some(format!("{} ", style().paint("↕️ 1")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn higher_threshold() -> io::Result<()> {
let actual = ModuleRenderer::new("shlvl")
.config(toml::toml! {
[shlvl]
threshold = 3
disabled = false
})
.env(SHLVL_ENV_VAR, "1")
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn custom_style() -> io::Result<()> {
let actual = ModuleRenderer::new("shlvl")
.config(toml::toml! {
[shlvl]
style = "Red Underline"
disabled = false
})
.env(SHLVL_ENV_VAR, "2")
.collect();
let expected = Some(format!("{} ", Color::Red.underline().paint("↕️ 2")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn custom_symbol() -> io::Result<()> {
let actual = ModuleRenderer::new("shlvl")
.config(toml::toml! {
[shlvl]
symbol = "shlvl is "
disabled = false
})
.env(SHLVL_ENV_VAR, "2")
.collect();
let expected = Some(format!("{} ", style().paint("shlvl is 2")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn formatting() -> io::Result<()> {
let actual = ModuleRenderer::new("shlvl")
.config(toml::toml! {
[shlvl]
format = "$symbol going down [$shlvl]($style) GOING UP "
disabled = false
})
.env(SHLVL_ENV_VAR, "2")
.collect();
let expected = Some(format!("↕️ going down {} GOING UP ", style().paint("2")));
assert_eq!(expected, actual);
Ok(())
}
} }

View File

@ -1,5 +1,3 @@
use std::env;
use super::{Context, Module, RootModuleConfig}; use super::{Context, Module, RootModuleConfig};
use crate::configs::singularity::SingularityConfig; use crate::configs::singularity::SingularityConfig;
@ -9,7 +7,7 @@ use crate::formatter::StringFormatter;
/// ///
/// Will display the Singularity image if `$SINGULARITY_NAME` is set. /// Will display the Singularity image if `$SINGULARITY_NAME` is set.
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let singularity_env = env::var("SINGULARITY_NAME").ok(); let singularity_env = context.get_env("SINGULARITY_NAME");
singularity_env.as_ref()?; singularity_env.as_ref()?;
let mut module = context.new_module("singularity"); let mut module = context.new_module("singularity");
@ -42,3 +40,33 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
#[cfg(test)]
mod tests {
use crate::test::ModuleRenderer;
use ansi_term::Color;
use std::io;
#[test]
fn no_env_set() -> io::Result<()> {
let actual = ModuleRenderer::new("singularity").collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn env_set() -> io::Result<()> {
let actual = ModuleRenderer::new("singularity")
.env("SINGULARITY_NAME", "centos.img")
.collect();
let expected = Some(format!(
"{} ",
Color::Blue.bold().dimmed().paint("[centos.img]")
));
assert_eq!(expected, actual);
Ok(())
}
}

View File

@ -65,7 +65,7 @@ fn parse_swift_version(swift_version: &str) -> Option<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::parse_swift_version; use super::parse_swift_version;
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -80,7 +80,7 @@ mod tests {
fn folder_without_swift_files() -> io::Result<()> { fn folder_without_swift_files() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("swift.txt"))?.sync_all()?; File::create(dir.path().join("swift.txt"))?.sync_all()?;
let actual = render_module("swift", dir.path(), None); let actual = ModuleRenderer::new("swift").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -90,7 +90,7 @@ mod tests {
fn folder_with_package_file() -> io::Result<()> { fn folder_with_package_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("Package.swift"))?.sync_all()?; File::create(dir.path().join("Package.swift"))?.sync_all()?;
let actual = render_module("swift", dir.path(), None); let actual = ModuleRenderer::new("swift").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
Color::Fixed(202).bold().paint("🐦 v5.2.2") Color::Fixed(202).bold().paint("🐦 v5.2.2")
@ -103,7 +103,7 @@ mod tests {
fn folder_with_swift_file() -> io::Result<()> { fn folder_with_swift_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.swift"))?.sync_all()?; File::create(dir.path().join("main.swift"))?.sync_all()?;
let actual = render_module("swift", dir.path(), None); let actual = ModuleRenderer::new("swift").path(dir.path()).collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
Color::Fixed(202).bold().paint("🐦 v5.2.2") Color::Fixed(202).bold().paint("🐦 v5.2.2")

View File

@ -4,7 +4,6 @@ use crate::configs::terraform::TerraformConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils; use crate::utils;
use std::env;
use std::io; use std::io;
use std::path::PathBuf; use std::path::PathBuf;
@ -42,7 +41,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
&utils::exec_cmd("terraform", &["version"])?.stdout.as_str(), &utils::exec_cmd("terraform", &["version"])?.stdout.as_str(),
) )
.map(Ok), .map(Ok),
"workspace" => get_terraform_workspace(&context.current_dir).map(Ok), "workspace" => get_terraform_workspace(context).map(Ok),
_ => None, _ => None,
}) })
.parse(None) .parse(None)
@ -60,17 +59,17 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
} }
// Determines the currently selected workspace (see https://github.com/hashicorp/terraform/blob/master/command/meta.go for the original implementation) // Determines the currently selected workspace (see https://github.com/hashicorp/terraform/blob/master/command/meta.go for the original implementation)
fn get_terraform_workspace(cwd: &PathBuf) -> Option<String> { fn get_terraform_workspace(context: &Context) -> Option<String> {
// Workspace can be explicitly overwritten by an env var // Workspace can be explicitly overwritten by an env var
let workspace_override = env::var("TF_WORKSPACE"); let workspace_override = context.get_env("TF_WORKSPACE");
if workspace_override.is_ok() { if workspace_override.is_some() {
return workspace_override.ok(); return workspace_override;
} }
// Data directory containing current workspace can be overwritten by an env var // Data directory containing current workspace can be overwritten by an env var
let datadir = match env::var("TF_DATA_DIR") { let datadir = match context.get_env("TF_DATA_DIR") {
Ok(s) => PathBuf::from(s), Some(s) => PathBuf::from(s),
Err(_) => cwd.join(".terraform"), None => context.current_dir.join(".terraform"),
}; };
match utils::read_file(datadir.join("environment")) { match utils::read_file(datadir.join("environment")) {
Err(ref e) if e.kind() == io::ErrorKind::NotFound => Some("default".to_string()), Err(ref e) if e.kind() == io::ErrorKind::NotFound => Some("default".to_string()),
@ -97,10 +96,10 @@ fn format_terraform_version(version: &str) -> Option<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::Write; use std::io::{self, Write};
#[test] #[test]
fn test_format_terraform_version_release() { fn test_format_terraform_version_release() {
@ -149,21 +148,20 @@ is 0.12.14. You can update by downloading from www.terraform.io/downloads.html
let tf_dir = dir.path().join(".terraform"); let tf_dir = dir.path().join(".terraform");
fs::create_dir(&tf_dir)?; fs::create_dir(&tf_dir)?;
let actual = render_module( let actual = ModuleRenderer::new("terraform")
"terraform", .path(dir.path())
dir.path(), .config(toml::toml! {
Some(toml::toml! {
[terraform] [terraform]
format = "via [$symbol$version$workspace]($style) " format = "via [$symbol$version$workspace]($style) "
}), })
); .collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
Color::Fixed(105).bold().paint("💠 v0.12.14 default") Color::Fixed(105).bold().paint("💠 v0.12.14 default")
)); ));
assert_eq!(expected, actual); assert_eq!(expected, actual);
Ok(()) dir.close()
} }
#[test] #[test]
@ -175,20 +173,122 @@ is 0.12.14. You can update by downloading from www.terraform.io/downloads.html
file.write_all(b"development")?; file.write_all(b"development")?;
file.sync_all()?; file.sync_all()?;
let actual = render_module( let actual = ModuleRenderer::new("terraform")
"terraform", .path(dir.path())
dir.path(), .config(toml::toml! {
Some(toml::toml! {
[terraform] [terraform]
format = "via [$symbol$version$workspace]($style) " format = "via [$symbol$version$workspace]($style) "
}), })
); .collect();
let expected = Some(format!( let expected = Some(format!(
"via {} ", "via {} ",
Color::Fixed(105).bold().paint("💠 v0.12.14 development") Color::Fixed(105).bold().paint("💠 v0.12.14 development")
)); ));
assert_eq!(expected, actual); assert_eq!(expected, actual);
Ok(()) dir.close()
}
#[test]
fn folder_without_dotterraform() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let actual = ModuleRenderer::new("terraform").path(dir.path()).collect();
let expected = None;
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_tf_file() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.tf"))?;
let actual = ModuleRenderer::new("terraform").path(dir.path()).collect();
let expected = Some(format!(
"via {} ",
Color::Fixed(105).bold().paint("💠 default")
));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_workspace_override() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.tf"))?;
let actual = ModuleRenderer::new("terraform")
.path(dir.path())
.env("TF_WORKSPACE", "development")
.collect();
let expected = Some(format!(
"via {} ",
Color::Fixed(105).bold().paint("💠 development")
));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_datadir_override() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.tf"))?;
let datadir = tempfile::tempdir()?;
let mut file = File::create(datadir.path().join("environment"))?;
file.write_all(b"development")?;
file.sync_all()?;
let actual = ModuleRenderer::new("terraform")
.path(dir.path())
.env("TF_DATA_DIR", datadir.path().to_str().unwrap())
.collect();
let expected = Some(format!(
"via {} ",
Color::Fixed(105).bold().paint("💠 development")
));
assert_eq!(expected, actual);
dir.close()?;
datadir.close()
}
#[test]
fn folder_with_dotterraform_no_environment() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let tf_dir = dir.path().join(".terraform");
fs::create_dir(&tf_dir)?;
let actual = ModuleRenderer::new("terraform").path(dir.path()).collect();
let expected = Some(format!(
"via {} ",
Color::Fixed(105).bold().paint("💠 default")
));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_dotterraform_with_environment() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let tf_dir = dir.path().join(".terraform");
fs::create_dir(&tf_dir)?;
let mut file = File::create(tf_dir.join("environment"))?;
file.write_all(b"development")?;
file.sync_all()?;
let actual = ModuleRenderer::new("terraform").path(dir.path()).collect();
let expected = Some(format!(
"via {} ",
Color::Fixed(105).bold().paint("💠 development")
));
assert_eq!(expected, actual);
dir.close()
} }
} }

View File

@ -151,7 +151,9 @@ tests become extra important */
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::test::ModuleRenderer;
use chrono::offset::TimeZone; use chrono::offset::TimeZone;
use std::io;
const FMT_12: &str = "%r"; const FMT_12: &str = "%r";
const FMT_24: &str = "%T"; const FMT_24: &str = "%T";
@ -464,4 +466,50 @@ mod tests {
assert_eq!(is_inside_time_range(time_now2, time_start, time_end), false); assert_eq!(is_inside_time_range(time_now2, time_start, time_end), false);
assert_eq!(is_inside_time_range(time_now3, time_start, time_end), true); assert_eq!(is_inside_time_range(time_now3, time_start, time_end), true);
} }
#[test]
fn config_enabled() -> io::Result<()> {
let actual = ModuleRenderer::new("time")
.config(toml::toml! {
[time]
disabled = false
})
.collect();
// We can't test what it actually is...but we can assert that it is something
assert!(actual.is_some());
Ok(())
}
#[test]
fn config_blank() -> io::Result<()> {
let actual = ModuleRenderer::new("time").collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_check_prefix_and_suffix() -> io::Result<()> {
let actual = ModuleRenderer::new("time")
.config(toml::toml! {
[time]
disabled = false
format = "at [\\[$time\\]]($style) "
time_format = "%T"
})
.collect()
.unwrap();
// This is the prefix with "at ", the color code, then the prefix char [
let col_prefix = format!("at {}{}[", '\u{1b}', "[1;33m");
// This is the suffix with suffix char ']', then color codes, then a space
let col_suffix = format!("]{}{} ", '\u{1b}', "[0m");
assert!(actual.starts_with(&col_prefix));
assert!(actual.ends_with(&col_suffix));
Ok(())
}
} }

View File

@ -1,5 +1,3 @@
use std::env;
use super::{Context, Module, RootModuleConfig}; use super::{Context, Module, RootModuleConfig};
use crate::configs::username::UsernameConfig; use crate::configs::username::UsernameConfig;
@ -13,9 +11,9 @@ use crate::utils;
/// - The current user is root (UID = 0) /// - The current user is root (UID = 0)
/// - The user is currently connected as an SSH session (`$SSH_CONNECTION`) /// - The user is currently connected as an SSH session (`$SSH_CONNECTION`)
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let user = env::var("USER").ok(); let user = context.get_env("USER");
let logname = env::var("LOGNAME").ok(); let logname = context.get_env("LOGNAME");
let ssh_connection = env::var("SSH_CONNECTION").ok(); let ssh_connection = context.get_env("SSH_CONNECTION");
const ROOT_UID: Option<u32> = Some(0); const ROOT_UID: Option<u32> = Some(0);
let user_uid = get_uid(); let user_uid = get_uid();
@ -64,3 +62,85 @@ fn get_uid() -> Option<u32> {
.parse::<u32>() .parse::<u32>()
.ok() .ok()
} }
#[cfg(test)]
mod tests {
use crate::test::ModuleRenderer;
use ansi_term::Color;
use std::io;
// TODO: Add tests for if root user (UID == 0)
// Requires mocking
#[test]
fn no_env_variables() -> io::Result<()> {
let actual = ModuleRenderer::new("username").collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn logname_equals_user() -> io::Result<()> {
let actual = ModuleRenderer::new("username")
.env("LOGNAME", "astronaut")
.env("USER", "astronaut")
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn ssh_wo_username() -> io::Result<()> {
// SSH connection w/o username
let actual = ModuleRenderer::new("username")
.env("SSH_CONNECTION", "192.168.223.17 36673 192.168.223.229 22")
.collect();
let expected = None;
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn current_user_not_logname() -> io::Result<()> {
let actual = ModuleRenderer::new("username")
.env("LOGNAME", "astronaut")
.env("USER", "cosmonaut")
.collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("cosmonaut")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn ssh_connection() -> io::Result<()> {
let actual = ModuleRenderer::new("username")
.env("USER", "astronaut")
.env("SSH_CONNECTION", "192.168.223.17 36673 192.168.223.229 22")
.collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("astronaut")));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn show_always() -> io::Result<()> {
let actual = ModuleRenderer::new("username")
.env("USER", "astronaut")
.config(toml::toml! {
[username]
show_always = true
})
.collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("astronaut")));
assert_eq!(expected, actual);
Ok(())
}
}

View File

@ -5,6 +5,3 @@ pub mod directory_win;
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
pub mod directory_nix; pub mod directory_nix;
#[cfg(test)]
pub mod test;

View File

@ -1,16 +0,0 @@
use crate::config::StarshipConfig;
use crate::context::{Context, Shell};
use std::path::Path;
/// Render a specific starship module by name
pub fn render_module(
module_name: &str,
path: &Path,
config: Option<toml::Value>,
) -> Option<String> {
let mut context = Context::new_with_dir(clap::ArgMatches::default(), path);
context.config = StarshipConfig { config };
context.shell = Shell::Unknown;
crate::print::get_module(module_name, context)
}

View File

@ -57,7 +57,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::modules::utils::test::render_module; use crate::test::ModuleRenderer;
use ansi_term::Color; use ansi_term::Color;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@ -66,7 +66,7 @@ mod tests {
fn folder_without_zig() -> io::Result<()> { fn folder_without_zig() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("zig.txt"))?.sync_all()?; File::create(dir.path().join("zig.txt"))?.sync_all()?;
let actual = render_module("zig", dir.path(), None); let actual = ModuleRenderer::new("zig").path(dir.path()).collect();
let expected = None; let expected = None;
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()
@ -76,7 +76,7 @@ mod tests {
fn folder_with_zig_file() -> io::Result<()> { fn folder_with_zig_file() -> io::Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.zig"))?.sync_all()?; File::create(dir.path().join("main.zig"))?.sync_all()?;
let actual = render_module("zig", dir.path(), None); let actual = ModuleRenderer::new("zig").path(dir.path()).collect();
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("↯ v0.6.0"))); let expected = Some(format!("via {} ", Color::Yellow.bold().paint("↯ v0.6.0")));
assert_eq!(expected, actual); assert_eq!(expected, actual);
dir.close() dir.close()

138
src/test/mod.rs Normal file
View File

@ -0,0 +1,138 @@
use crate::config::StarshipConfig;
use crate::context::{Context, Shell};
use once_cell::sync::Lazy;
use std::io;
use std::path::PathBuf;
use std::process::Command;
use tempfile::TempDir;
static FIXTURE_DIR: Lazy<PathBuf> =
Lazy::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/test/fixtures/"));
static GIT_FIXTURE: Lazy<PathBuf> = Lazy::new(|| FIXTURE_DIR.join("git-repo.bundle"));
static HG_FIXTURE: Lazy<PathBuf> = Lazy::new(|| FIXTURE_DIR.join("hg-repo.bundle"));
/// Render a specific starship module by name
pub struct ModuleRenderer<'a> {
name: &'a str,
context: Context<'a>,
}
impl<'a> ModuleRenderer<'a> {
/// Creates a new ModuleRenderer
pub fn new(name: &'a str) -> Self {
let mut context = Context::new_with_dir(clap::ArgMatches::default(), PathBuf::new());
context.shell = Shell::Unknown;
context.config = StarshipConfig { config: None };
Self { name, context }
}
pub fn path<T>(mut self, path: T) -> Self
where
T: Into<PathBuf>,
{
self.context.current_dir = path.into();
self
}
/// Sets the config of the underlying context
pub fn config(mut self, config: toml::Value) -> Self {
self.context.config = StarshipConfig {
config: Some(config),
};
self
}
/// Adds the variable to the env_mocks of the underlying context
pub fn env<V: Into<String>>(mut self, key: &'a str, val: V) -> Self {
self.context.env.insert(key, val.into());
self
}
pub fn shell(mut self, shell: Shell) -> Self {
self.context.shell = shell;
self
}
pub fn jobs(mut self, jobs: u64) -> Self {
self.context.properties.insert("jobs", jobs.to_string());
self
}
pub fn cmd_duration(mut self, duration: u64) -> Self {
self.context
.properties
.insert("cmd_duration", duration.to_string());
self
}
pub fn keymap<T>(mut self, keymap: T) -> Self
where
T: Into<String>,
{
self.context.properties.insert("keymap", keymap.into());
self
}
pub fn status(mut self, status: i32) -> Self {
self.context
.properties
.insert("status_code", status.to_string());
self
}
/// Renders the module returning its output
pub fn collect(self) -> Option<String> {
crate::print::get_module(self.name, self.context)
}
}
pub enum FixtureProvider {
GIT,
HG,
}
pub fn fixture_repo(provider: FixtureProvider) -> io::Result<TempDir> {
match provider {
FixtureProvider::GIT => {
let path = tempfile::tempdir()?;
Command::new("git")
.current_dir(path.path())
.args(&["clone", "-b", "master"])
.arg(GIT_FIXTURE.as_os_str())
.arg(&path.path())
.output()?;
Command::new("git")
.args(&["config", "--local", "user.email", "starship@example.com"])
.current_dir(&path.path())
.output()?;
Command::new("git")
.args(&["config", "--local", "user.name", "starship"])
.current_dir(&path.path())
.output()?;
Command::new("git")
.args(&["reset", "--hard", "HEAD"])
.current_dir(&path.path())
.output()?;
Ok(path)
}
FixtureProvider::HG => {
let path = tempfile::tempdir()?;
Command::new("hg")
.current_dir(path.path())
.arg("clone")
.arg(HG_FIXTURE.as_os_str())
.arg(&path.path())
.output()?;
Ok(path)
}
}
}

View File

@ -26,7 +26,7 @@ impl PartialEq for CommandOutput {
} }
} }
/// Execute a command and return the output on stdout and stderr if sucessful /// Execute a command and return the output on stdout and stderr if successful
#[cfg(not(test))] #[cfg(not(test))]
pub fn exec_cmd(cmd: &str, args: &[&str]) -> Option<CommandOutput> { pub fn exec_cmd(cmd: &str, args: &[&str]) -> Option<CommandOutput> {
internal_exec_cmd(&cmd, &args) internal_exec_cmd(&cmd, &args)
@ -158,6 +158,14 @@ CMake suite maintained and supported by Kitware (kitware.com/cmake).\n",
), ),
stderr: String::default(), stderr: String::default(),
}), }),
"dotnet --version" => Some(CommandOutput {
stdout: String::from("3.1.103"),
stderr: String::default(),
}),
"dotnet --list-sdks" => Some(CommandOutput {
stdout: String::from("3.1.103 [/usr/share/dotnet/sdk]"),
stderr: String::default(),
}),
"terraform version" => Some(CommandOutput { "terraform version" => Some(CommandOutput {
stdout: String::from("Terraform v0.12.14\n"), stdout: String::from("Terraform v0.12.14\n"),
stderr: String::default(), stderr: String::default(),

View File

View File

@ -1,250 +0,0 @@
use std::fs::File;
use std::io::{self, Write};
use ansi_term::Color;
use crate::common::{self, TestCommand};
#[test]
#[ignore]
fn no_region_set() -> io::Result<()> {
let output = common::render_module("aws")
.env("PATH", env!("PATH"))
.output()?;
let expected = "";
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn region_set() -> io::Result<()> {
let output = common::render_module("aws")
.env("AWS_REGION", "ap-northeast-2")
.output()?;
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ (ap-northeast-2)"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn region_set_with_alias() -> io::Result<()> {
let output = common::render_module("aws")
.env("AWS_REGION", "ap-southeast-2")
.use_config(toml::toml! {
[aws.region_aliases]
ap-southeast-2 = "au"
})
.output()?;
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ (au)"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn default_region_set() -> io::Result<()> {
let output = common::render_module("aws")
.env("AWS_REGION", "ap-northeast-2")
.env("AWS_DEFAULT_REGION", "ap-northeast-1")
.output()?;
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ (ap-northeast-1)"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn profile_set() -> io::Result<()> {
let output = common::render_module("aws")
.env("AWS_PROFILE", "astronauts")
.output()?;
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ astronauts"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn profile_set_from_aws_vault() -> io::Result<()> {
let output = common::render_module("aws")
.env("AWS_VAULT", "astronauts-vault")
.env("AWS_PROFILE", "astronauts-profile")
.output()?;
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ astronauts-vault"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn profile_and_region_set() -> io::Result<()> {
let output = common::render_module("aws")
.env("AWS_PROFILE", "astronauts")
.env("AWS_REGION", "ap-northeast-2")
.output()?;
let expected = format!(
"on {} ",
Color::Yellow.bold().paint("☁️ astronauts(ap-northeast-2)")
);
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn default_profile_set() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let config_path = dir.path().join("config");
let mut file = File::create(&config_path)?;
file.write_all(
"[default]
region = us-east-1
[profile astronauts]
region = us-east-2
"
.as_bytes(),
)?;
let output = common::render_module("aws")
.env("AWS_CONFIG_FILE", config_path.to_string_lossy().as_ref())
.output()?;
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ (us-east-1)"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn profile_and_config_set() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let config_path = dir.path().join("config");
let mut file = File::create(&config_path)?;
file.write_all(
"[default]
region = us-east-1
[profile astronauts]
region = us-east-2
"
.as_bytes(),
)?;
let output = common::render_module("aws")
.env("AWS_CONFIG_FILE", config_path.to_string_lossy().as_ref())
.env("AWS_PROFILE", "astronauts")
.use_config(toml::toml! {
[aws]
})
.output()?;
let expected = format!(
"on {} ",
Color::Yellow.bold().paint("☁️ astronauts(us-east-2)")
);
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn profile_and_region_set_with_display_all() -> io::Result<()> {
let output = common::render_module("aws")
.env("AWS_PROFILE", "astronauts")
.env("AWS_REGION", "ap-northeast-1")
.output()?;
let expected = format!(
"on {} ",
Color::Yellow.bold().paint("☁️ astronauts(ap-northeast-1)")
);
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn profile_set_with_display_all() -> io::Result<()> {
let output = common::render_module("aws")
.env("AWS_PROFILE", "astronauts")
.output()?;
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ astronauts"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn region_set_with_display_all() -> io::Result<()> {
let output = common::render_module("aws")
.env("AWS_REGION", "ap-northeast-1")
.output()?;
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ (ap-northeast-1)"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn profile_and_region_set_with_display_region() -> io::Result<()> {
let output = common::render_module("aws")
.env("AWS_PROFILE", "astronauts")
.env("AWS_DEFAULT_REGION", "ap-northeast-1")
.use_config(toml::toml! {
[aws]
format = "on [$symbol$region]($style) "
})
.output()?;
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ ap-northeast-1"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn profile_and_region_set_with_display_profile() -> io::Result<()> {
let output = common::render_module("aws")
.env("AWS_PROFILE", "astronauts")
.env("AWS_REGION", "ap-northeast-1")
.use_config(toml::toml! {
[aws]
format = "on [$symbol$profile]($style) "
})
.output()?;
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ astronauts"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn region_set_with_display_profile() -> io::Result<()> {
let output = common::render_module("aws")
.env("AWS_REGION", "ap-northeast-1")
.use_config(toml::toml! {
[aws]
format = "on [$symbol$profile]($style) "
})
.output()?;
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ "));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn region_not_set_with_display_region() -> io::Result<()> {
let output = common::render_module("aws")
.use_config(toml::toml! {
[aws]
format = "on [$symbol$region]($style) "
})
.output()?;
let expected = "";
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}

View File

@ -1,151 +0,0 @@
use ansi_term::Color;
use std::io;
use crate::common::{self, TestCommand};
#[test]
fn success_status() -> io::Result<()> {
let expected = format!("{} ", Color::Green.bold().paint(""));
// Status code 0
let output = common::render_module("character")
.arg("--status=0")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
// No status code
let output = common::render_module("character").output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn failure_status() -> io::Result<()> {
let expected = format!("{} ", Color::Red.bold().paint(""));
let exit_values = ["1", "54321", "-5000"];
for status in exit_values.iter() {
let arg = format!("--status={}", status);
let output = common::render_module("character").arg(arg).output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
}
Ok(())
}
#[test]
fn custom_symbol() -> io::Result<()> {
let expected_fail = format!("{} ", Color::Red.bold().paint(""));
let expected_success = format!("{} ", Color::Green.bold().paint(""));
let exit_values = ["1", "54321", "-5000"];
// Test failure values
for status in exit_values.iter() {
let arg = format!("--status={}", status);
let output = common::render_module("character")
.use_config(toml::toml! {
[character]
success_symbol = "[➜](bold green)"
error_symbol = "[✖](bold red)"
})
.arg(arg)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected_fail, actual);
}
// Test success
let output = common::render_module("character")
.use_config(toml::toml! {
[character]
success_symbol = "[➜](bold green)"
error_symbol = "[✖](bold red)"
})
.arg("--status=0")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected_success, actual);
Ok(())
}
#[test]
fn zsh_keymap() -> io::Result<()> {
let expected_vicmd = format!("{} ", Color::Green.bold().paint(""));
let expected_specified = format!("{} ", Color::Green.bold().paint("V"));
let expected_other = format!("{} ", Color::Green.bold().paint(""));
// zle keymap is vicmd
let output = common::render_module("character")
.env("STARSHIP_SHELL", "zsh")
.arg("--keymap=vicmd")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected_vicmd, actual);
// specified vicmd character
let output = common::render_module("character")
.use_config(toml::toml! {
[character]
vicmd_symbol = "[V](bold green)"
})
.env("STARSHIP_SHELL", "zsh")
.arg("--keymap=vicmd")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected_specified, actual);
// zle keymap is other
let output = common::render_module("character")
.env("STARSHIP_SHELL", "zsh")
.arg("--keymap=visual")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected_other, actual);
Ok(())
}
#[test]
fn fish_keymap() -> io::Result<()> {
let expected_vicmd = format!("{} ", Color::Green.bold().paint(""));
let expected_specified = format!("{} ", Color::Green.bold().paint("V"));
let expected_other = format!("{} ", Color::Green.bold().paint(""));
// fish keymap is default
let output = common::render_module("character")
.env("STARSHIP_SHELL", "fish")
.arg("--keymap=default")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected_vicmd, actual);
// specified vicmd character
let output = common::render_module("character")
.use_config(toml::toml! {
[character]
vicmd_symbol = "[V](bold green)"
})
.env("STARSHIP_SHELL", "fish")
.arg("--keymap=default")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected_specified, actual);
// fish keymap is other
let output = common::render_module("character")
.env("STARSHIP_SHELL", "fish")
.arg("--keymap=visual")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected_other, actual);
Ok(())
}

View File

@ -1,92 +0,0 @@
use ansi_term::Color;
use std::io;
use crate::common::{self, TestCommand};
#[test]
fn config_blank_duration_1s() -> io::Result<()> {
let output = common::render_module("cmd_duration")
.arg("--cmd-duration=1000")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = "";
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_blank_duration_5s() -> io::Result<()> {
let output = common::render_module("cmd_duration")
.arg("--cmd-duration=5000")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("took {} ", Color::Yellow.bold().paint("5s"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_5s_duration_3s() -> io::Result<()> {
let output = common::render_module("cmd_duration")
.use_config(toml::toml! {
[cmd_duration]
min_time = 5000
})
.arg("--cmd-duration=3000")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = "";
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_5s_duration_10s() -> io::Result<()> {
let output = common::render_module("cmd_duration")
.use_config(toml::toml! {
[cmd_duration]
min_time = 5000
})
.arg("--cmd-duration=10000")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("took {} ", Color::Yellow.bold().paint("10s"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_1s_duration_prefix_underwent() -> io::Result<()> {
let output = common::render_module("cmd_duration")
.use_config(toml::toml! {
[cmd_duration]
format = "underwent [$duration]($style) "
})
.arg("--cmd-duration=1000")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = "";
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_5s_duration_prefix_underwent() -> io::Result<()> {
let output = common::render_module("cmd_duration")
.use_config(toml::toml! {
[cmd_duration]
format = "underwent [$duration]($style) "
})
.arg("--cmd-duration=5000")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("underwent {} ", Color::Yellow.bold().paint("5s"));
assert_eq!(expected, actual);
Ok(())
}

View File

@ -1,105 +0,0 @@
use once_cell::sync::Lazy;
use remove_dir_all::remove_dir_all;
use std::io::prelude::*;
use std::io::{Error, ErrorKind};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, fs, io, process};
static MANIFEST_DIR: Lazy<&'static Path> = Lazy::new(|| Path::new(env!("CARGO_MANIFEST_DIR")));
static EMPTY_CONFIG: Lazy<PathBuf> = Lazy::new(|| MANIFEST_DIR.join("empty_config.toml"));
#[cfg(windows)]
const EXE_PATH: &str = "./target/debug/starship.exe";
#[cfg(not(windows))]
const EXE_PATH: &str = "./target/debug/starship";
/// Render the full starship prompt
pub fn _render_prompt() -> process::Command {
let mut command = process::Command::new(EXE_PATH);
command
.arg("prompt")
.env_clear()
.env("PATH", env!("PATH")) // Provide the $PATH variable so that external programs are runnable
.env("STARSHIP_CONFIG", EMPTY_CONFIG.as_os_str());
command
}
/// Render a specific starship module by name
pub fn render_module(module_name: &str) -> process::Command {
let binary = fs::canonicalize(EXE_PATH).unwrap();
let mut command = process::Command::new(binary);
command
.arg("module")
.arg(module_name)
.env_clear()
.env("PATH", env!("PATH")) // Provide the $PATH variable so that external programs are runnable
.env("STARSHIP_CONFIG", EMPTY_CONFIG.as_os_str());
command
}
/// Create a repo from the fixture to be used in git module tests
/// Please delete the returned directory manually after usage with `remove_dir_all::remove_dir_all`
pub fn create_fixture_repo() -> io::Result<PathBuf> {
let fixture_repo_path = tempfile::tempdir()?.into_path();
let repo_path = tempfile::tempdir()?.into_path();
let fixture_path = env::current_dir()?.join("tests/fixtures/rocket.bundle");
let fixture_repo_dir = path_str(&fixture_repo_path)?;
let repo_dir = path_str(&repo_path)?;
Command::new("git")
.args(&["clone", "-b", "master"])
.args(&[&fixture_path, &repo_path])
.output()?;
git2::Repository::clone(&fixture_repo_dir, &repo_dir).ok();
remove_dir_all(fixture_repo_path)?;
Command::new("git")
.args(&["config", "--local", "user.email", "starship@example.com"])
.current_dir(&repo_path)
.output()?;
Command::new("git")
.args(&["config", "--local", "user.name", "starship"])
.current_dir(&repo_path)
.output()?;
Command::new("git")
.args(&["reset", "--hard", "HEAD"])
.current_dir(&repo_path)
.output()?;
Ok(repo_path)
}
fn path_str(repo_dir: &PathBuf) -> io::Result<String> {
repo_dir
.to_str()
.ok_or_else(|| Error::from(ErrorKind::Other))
.map(|i| i.replace("\\", "/"))
}
/// Extends `std::process::Command` with methods for testing
pub trait TestCommand {
fn use_config(&mut self, toml: toml::value::Value) -> &mut process::Command;
}
impl TestCommand for process::Command {
/// Create a configuration file with the provided TOML and use it
fn use_config(&mut self, toml: toml::value::Value) -> &mut process::Command {
// Create a persistent config file in a tempdir
let (mut config_file, config_path) =
tempfile::NamedTempFile::new().unwrap().keep().unwrap();
write!(config_file, "{}", toml.to_string()).unwrap();
// Set that newly-created file as the config for the prompt instance
self.env("STARSHIP_CONFIG", config_path)
}
}

View File

@ -1,58 +0,0 @@
use ansi_term::Color;
use std::io;
use crate::common::{self, TestCommand};
#[test]
fn not_in_env() -> io::Result<()> {
let output = common::render_module("conda").output()?;
let expected = "";
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn ignore_base() -> io::Result<()> {
let output = common::render_module("conda")
.env("CONDA_DEFAULT_ENV", "base")
.use_config(toml::toml! {
[conda]
ignore_base = true
})
.output()?;
let expected = "";
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn env_set() -> io::Result<()> {
let output = common::render_module("conda")
.env("CONDA_DEFAULT_ENV", "astronauts")
.output()?;
let expected = format!("via {} ", Color::Green.bold().paint("🅒 astronauts"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn truncate() -> io::Result<()> {
let output = common::render_module("conda")
.env("CONDA_DEFAULT_ENV", "/some/really/long/and/really/annoying/path/that/shouldnt/be/displayed/fully/conda/my_env")
.output()?;
let expected = format!("via {} ", Color::Green.bold().paint("🅒 my_env"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}

View File

@ -1,20 +0,0 @@
use ansi_term::Color;
use std::io;
use crate::common::{self, TestCommand};
#[test]
fn char_symbol_configuration() -> io::Result<()> {
let expected = format!("{} ", Color::Green.bold().paint(""));
let output = common::render_module("character")
.use_config(toml::toml! {
[character]
symbol = ""
})
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}

View File

@ -1,781 +0,0 @@
use ansi_term::Color;
use dirs_next::home_dir;
use git2::Repository;
use std::fs;
use std::io;
#[cfg(not(target_os = "windows"))]
use std::os::unix::fs::symlink;
#[cfg(target_os = "windows")]
use std::os::windows::fs::symlink_dir as symlink;
use std::path::Path;
use tempfile::TempDir;
use crate::common::{self, TestCommand};
#[test]
fn home_directory() -> io::Result<()> {
let output = common::render_module("directory")
.arg("--path=~")
.use_config(toml::toml! { // Necessary if homedir is a git repo
[directory]
truncate_to_repo = false
})
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("~"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn substituted_truncated_path() -> io::Result<()> {
let output = common::render_module("directory")
.arg("--path=/some/long/network/path/workspace/a/b/c/dev")
.use_config(toml::toml! {
[directory]
truncation_length = 4
[directory.substitutions]
"/some/long/network/path" = "/some/net"
"a/b/c" = "d"
})
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("net/workspace/d/dev"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn strange_substitution() -> io::Result<()> {
let strange_sub = "/\\/;,!";
let output = common::render_module("directory")
.arg("--path=/foo/bar/regular/path")
.use_config(toml::toml! {
[directory]
truncation_length = 0
fish_style_pwd_dir_length = 2 // Overridden by substitutions
[directory.substitutions]
"regular" = strange_sub
})
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
Color::Cyan
.bold()
.paint(format!("/foo/bar/{}/path", strange_sub))
);
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn directory_in_home() -> io::Result<()> {
let dir = home_dir().unwrap().join("starship/engine");
fs::create_dir_all(&dir)?;
let output = common::render_module("directory")
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("~/starship/engine"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn truncated_directory_in_home() -> io::Result<()> {
let dir = home_dir().unwrap().join("starship/engine/schematics");
fs::create_dir_all(&dir)?;
let output = common::render_module("directory")
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
Color::Cyan.bold().paint("starship/engine/schematics")
);
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn fish_directory_in_home() -> io::Result<()> {
let dir = home_dir().unwrap().join("starship/engine/schematics");
fs::create_dir_all(&dir)?;
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
truncation_length = 1
fish_style_pwd_dir_length = 2
})
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("~/st/en/schematics"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn root_directory() -> io::Result<()> {
let output = common::render_module("directory")
.arg("--path=/")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
#[cfg(not(target_os = "windows"))]
let expected = format!(
"{}{} ",
Color::Cyan.bold().paint("/"),
Color::Red.normal().paint("🔒")
);
#[cfg(target_os = "windows")]
let expected = format!("{} ", Color::Cyan.bold().paint("/"),);
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[cfg(not(target_os = "windows"))]
fn directory_in_root() -> io::Result<()> {
let output = common::render_module("directory")
.arg("--path=/etc")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{}{} ",
Color::Cyan.bold().paint("/etc"),
Color::Red.normal().paint("🔒")
);
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[cfg(target_os = "windows")]
fn directory_in_root() -> io::Result<()> {
let output = common::render_module("directory")
.arg("--path=C:\\")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("C:"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn truncated_directory_in_root() -> io::Result<()> {
let dir = Path::new("/tmp/starship/thrusters/rocket");
fs::create_dir_all(&dir)?;
let output = common::render_module("directory")
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("starship/thrusters/rocket"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn truncated_directory_config_large() -> io::Result<()> {
let dir = Path::new("/tmp/starship/thrusters/rocket");
fs::create_dir_all(&dir)?;
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
truncation_length = 100
})
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
Color::Cyan.bold().paint("/tmp/starship/thrusters/rocket")
);
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn fish_style_directory_config_large() -> io::Result<()> {
let dir = Path::new("/tmp/starship/thrusters/rocket");
fs::create_dir_all(&dir)?;
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
truncation_length = 1
fish_style_pwd_dir_length = 100
})
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
Color::Cyan.bold().paint("/tmp/starship/thrusters/rocket")
);
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn truncated_directory_config_small() -> io::Result<()> {
let dir = Path::new("/tmp/starship/thrusters/rocket");
fs::create_dir_all(&dir)?;
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
truncation_length = 2
})
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("thrusters/rocket"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn fish_directory_config_small() -> io::Result<()> {
let dir = Path::new("/tmp/starship/thrusters/rocket");
fs::create_dir_all(&dir)?;
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
truncation_length = 2
fish_style_pwd_dir_length = 1
})
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("/t/s/thrusters/rocket"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn git_repo_root() -> io::Result<()> {
// TODO: Investigate why git repo related tests fail when the tempdir is within /tmp/...
// Temporarily making the tempdir within $HOME
// #[ignore] can be removed after this TODO is addressed
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("rocket-controls");
fs::create_dir(&repo_dir)?;
Repository::init(&repo_dir).unwrap();
let output = common::render_module("directory")
.arg("--path")
.arg(repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("rocket-controls"));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn directory_in_git_repo() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("rocket-controls");
let dir = repo_dir.join("src");
fs::create_dir_all(&dir)?;
Repository::init(&repo_dir).unwrap();
let output = common::render_module("directory")
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("rocket-controls/src"));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn truncated_directory_in_git_repo() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("rocket-controls");
let dir = repo_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&dir)?;
Repository::init(&repo_dir).unwrap();
let output = common::render_module("directory")
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("src/meters/fuel-gauge"));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn directory_in_git_repo_truncate_to_repo_false() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let dir = repo_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&dir)?;
Repository::init(&repo_dir).unwrap();
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
// Don't truncate the path at all.
truncation_length = 5
truncate_to_repo = false
})
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
Color::Cyan
.bold()
.paint("above-repo/rocket-controls/src/meters/fuel-gauge")
);
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn fish_path_directory_in_git_repo_truncate_to_repo_false() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let dir = repo_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&dir)?;
Repository::init(&repo_dir).unwrap();
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
// Don't truncate the path at all.
truncation_length = 5
truncate_to_repo = false
fish_style_pwd_dir_length = 1
})
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
Color::Cyan
.bold()
.paint("~/.t/above-repo/rocket-controls/src/meters/fuel-gauge")
);
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn fish_path_directory_in_git_repo_truncate_to_repo_true() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let dir = repo_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&dir)?;
Repository::init(&repo_dir).unwrap();
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
// `truncate_to_repo = true` should display the truncated path
truncation_length = 5
truncate_to_repo = true
fish_style_pwd_dir_length = 1
})
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
Color::Cyan
.bold()
.paint("~/.t/a/rocket-controls/src/meters/fuel-gauge")
);
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn directory_in_git_repo_truncate_to_repo_true() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let dir = repo_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&dir)?;
Repository::init(&repo_dir).unwrap();
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
// `truncate_to_repo = true` should display the truncated path
truncation_length = 5
truncate_to_repo = true
})
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
Color::Cyan
.bold()
.paint("rocket-controls/src/meters/fuel-gauge")
);
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
#[cfg(not(target_os = "windows"))]
fn git_repo_in_home_directory_truncate_to_repo_true() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let dir = tmp_dir.path().join("src/meters/fuel-gauge");
fs::create_dir_all(&dir)?;
Repository::init(&tmp_dir).unwrap();
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
// `truncate_to_repo = true` should attmpt to display the truncated path
truncate_to_repo = true
truncation_length = 5
})
// Set home directory to the temp repository
.env("HOME", tmp_dir.path())
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("~/src/meters/fuel-gauge"));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn symlinked_git_repo_root() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("rocket-controls");
let symlink_dir = tmp_dir.path().join("rocket-controls-symlink");
fs::create_dir(&repo_dir)?;
Repository::init(&repo_dir).unwrap();
symlink(&repo_dir, &symlink_dir)?;
let output = common::render_module("directory")
.arg("--path")
.arg(symlink_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("rocket-controls-symlink"));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn directory_in_symlinked_git_repo() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("rocket-controls");
let src_dir = repo_dir.join("src");
let symlink_dir = tmp_dir.path().join("rocket-controls-symlink");
let symlink_src_dir = symlink_dir.join("src");
fs::create_dir_all(&src_dir)?;
Repository::init(&repo_dir).unwrap();
symlink(&repo_dir, &symlink_dir)?;
let output = common::render_module("directory")
.arg("--path")
.arg(symlink_src_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
Color::Cyan.bold().paint("rocket-controls-symlink/src")
);
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn truncated_directory_in_symlinked_git_repo() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("rocket-controls");
let src_dir = repo_dir.join("src/meters/fuel-gauge");
let symlink_dir = tmp_dir.path().join("rocket-controls-symlink");
let symlink_src_dir = symlink_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&src_dir)?;
Repository::init(&repo_dir).unwrap();
symlink(&repo_dir, &symlink_dir)?;
let output = common::render_module("directory")
.arg("--path")
.arg(symlink_src_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("src/meters/fuel-gauge"));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn directory_in_symlinked_git_repo_truncate_to_repo_false() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let src_dir = repo_dir.join("src/meters/fuel-gauge");
let symlink_dir = tmp_dir
.path()
.join("above-repo")
.join("rocket-controls-symlink");
let symlink_src_dir = symlink_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&src_dir)?;
Repository::init(&repo_dir).unwrap();
symlink(&repo_dir, &symlink_dir)?;
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
// Don't truncate the path at all.
truncation_length = 5
truncate_to_repo = false
})
.arg("--path")
.arg(symlink_src_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
Color::Cyan
.bold()
.paint("above-repo/rocket-controls-symlink/src/meters/fuel-gauge")
);
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn fish_path_directory_in_symlinked_git_repo_truncate_to_repo_false() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let src_dir = repo_dir.join("src/meters/fuel-gauge");
let symlink_dir = tmp_dir
.path()
.join("above-repo")
.join("rocket-controls-symlink");
let symlink_src_dir = symlink_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&src_dir)?;
Repository::init(&repo_dir).unwrap();
symlink(&repo_dir, &symlink_dir)?;
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
// Don't truncate the path at all.
truncation_length = 5
truncate_to_repo = false
fish_style_pwd_dir_length = 1
})
.arg("--path")
.arg(symlink_src_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
Color::Cyan
.bold()
.paint("~/.t/above-repo/rocket-controls-symlink/src/meters/fuel-gauge")
);
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn fish_path_directory_in_symlinked_git_repo_truncate_to_repo_true() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let src_dir = repo_dir.join("src/meters/fuel-gauge");
let symlink_dir = tmp_dir
.path()
.join("above-repo")
.join("rocket-controls-symlink");
let symlink_src_dir = symlink_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&src_dir)?;
Repository::init(&repo_dir).unwrap();
symlink(&repo_dir, &symlink_dir)?;
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
// `truncate_to_repo = true` should display the truncated path
truncation_length = 5
truncate_to_repo = true
fish_style_pwd_dir_length = 1
})
.arg("--path")
.arg(symlink_src_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
Color::Cyan
.bold()
.paint("~/.t/a/rocket-controls-symlink/src/meters/fuel-gauge")
);
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn directory_in_symlinked_git_repo_truncate_to_repo_true() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let src_dir = repo_dir.join("src/meters/fuel-gauge");
let symlink_dir = tmp_dir
.path()
.join("above-repo")
.join("rocket-controls-symlink");
let symlink_src_dir = symlink_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&src_dir)?;
Repository::init(&repo_dir).unwrap();
symlink(&repo_dir, &symlink_dir)?;
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
// `truncate_to_repo = true` should display the truncated path
truncation_length = 5
truncate_to_repo = true
})
.arg("--path")
.arg(symlink_src_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
Color::Cyan
.bold()
.paint("rocket-controls-symlink/src/meters/fuel-gauge")
);
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
fn symlinked_directory_in_git_repo() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("rocket-controls");
let dir = repo_dir.join("src");
fs::create_dir_all(&dir)?;
Repository::init(&repo_dir).unwrap();
symlink(&dir, repo_dir.join("src/loop"))?;
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
// `truncate_to_repo = true` should display the truncated path
truncation_length = 5
truncate_to_repo = true
})
.arg("--path")
.arg(repo_dir.join("src/loop/loop"))
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
Color::Cyan.bold().paint("rocket-controls/src/loop/loop")
);
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
#[ignore]
#[cfg(not(target_os = "windows"))]
fn symlinked_subdirectory_git_repo_out_of_tree() -> io::Result<()> {
let tmp_dir = TempDir::new_in(home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let src_dir = repo_dir.join("src/meters/fuel-gauge");
let symlink_dir = tmp_dir.path().join("fuel-gauge");
fs::create_dir_all(&src_dir)?;
Repository::init(&repo_dir).unwrap();
symlink(&src_dir, &symlink_dir)?;
let output = common::render_module("directory")
// Set home directory to the temp repository
.env("HOME", tmp_dir.path())
.arg("--path")
.arg(symlink_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Cyan.bold().paint("~/fuel-gauge"));
assert_eq!(expected, actual);
tmp_dir.close()
}

View File

@ -1,244 +0,0 @@
use super::common;
use regex::Regex;
use std::fs::{DirBuilder, OpenOptions};
use std::io::{self, Error, ErrorKind, Write};
use std::process::{Command, Stdio};
use tempfile::{self, TempDir};
const DOTNET_OUTPUT_PATTERN: &str = "•NET v\\d+?\\.\\d+?\\.\\d?";
const DOTNET_PINNED_VERSION: &str = "1.2.3";
const DOTNET_PINNED_VERSION_OUTPUT_PATTERN: &str = "•NET v1\\.2\\.3";
const DOTNET_TFM_PATTERN: &str = r"🎯 .+";
const DOTNET_TFM_PINNED_VERSION: &str = r"netstandard2.0";
#[test]
#[ignore]
fn shows_nothing_in_directory_with_zero_relevant_files() -> io::Result<()> {
let workspace = create_workspace(false)?;
expect_output(&workspace, ".", None)?;
workspace.close()
}
#[test]
#[ignore]
fn shows_latest_in_directory_with_directory_build_props_file() -> io::Result<()> {
let workspace = create_workspace(false)?;
touch_path(&workspace, "Directory.Build.props", None)?;
expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?;
workspace.close()
}
#[test]
#[ignore]
fn shows_latest_in_directory_with_directory_build_targets_file() -> io::Result<()> {
let workspace = create_workspace(false)?;
touch_path(&workspace, "Directory.Build.targets", None)?;
expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?;
workspace.close()
}
#[test]
#[ignore]
fn shows_latest_in_directory_with_packages_props_file() -> io::Result<()> {
let workspace = create_workspace(false)?;
touch_path(&workspace, "Packages.props", None)?;
expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?;
workspace.close()
}
#[test]
#[ignore]
fn shows_latest_in_directory_with_solution() -> io::Result<()> {
let workspace = create_workspace(false)?;
touch_path(&workspace, "solution.sln", None)?;
expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?;
workspace.close()
}
#[test]
#[ignore]
fn shows_latest_in_directory_with_csproj() -> io::Result<()> {
let workspace = create_workspace(false)?;
let csproj = make_csproj_with_tfm("TargetFramework", "netstandard2.0");
touch_path(&workspace, "project.csproj", Some(&csproj))?;
expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?;
expect_output(&workspace, ".", Some(DOTNET_TFM_PATTERN))?;
workspace.close()
}
#[test]
#[ignore]
fn shows_latest_in_directory_with_fsproj() -> io::Result<()> {
let workspace = create_workspace(false)?;
touch_path(&workspace, "project.fsproj", None)?;
expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?;
workspace.close()
}
#[test]
#[ignore]
fn shows_latest_in_directory_with_xproj() -> io::Result<()> {
let workspace = create_workspace(false)?;
touch_path(&workspace, "project.xproj", None)?;
expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?;
workspace.close()
}
#[test]
#[ignore]
fn shows_latest_in_directory_with_project_json() -> io::Result<()> {
let workspace = create_workspace(false)?;
touch_path(&workspace, "project.json", None)?;
expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?;
workspace.close()
}
#[test]
#[ignore]
fn shows_pinned_in_directory_with_global_json() -> io::Result<()> {
let workspace = create_workspace(false)?;
let global_json = make_pinned_sdk_json(DOTNET_PINNED_VERSION);
touch_path(&workspace, "global.json", Some(&global_json))?;
expect_output(&workspace, ".", Some(DOTNET_PINNED_VERSION_OUTPUT_PATTERN))?;
workspace.close()
}
#[test]
#[ignore]
fn shows_pinned_in_project_below_root_with_global_json() -> io::Result<()> {
let workspace = create_workspace(false)?;
let global_json = make_pinned_sdk_json(DOTNET_PINNED_VERSION);
let csproj = make_csproj_with_tfm("TargetFramework", DOTNET_TFM_PINNED_VERSION);
touch_path(&workspace, "global.json", Some(&global_json))?;
touch_path(&workspace, "project/project.csproj", Some(&csproj))?;
expect_output(
&workspace,
"project",
Some(DOTNET_PINNED_VERSION_OUTPUT_PATTERN),
)?;
workspace.close()
}
#[test]
#[ignore]
fn shows_pinned_in_deeply_nested_project_within_repository() -> io::Result<()> {
let workspace = create_workspace(true)?;
let global_json = make_pinned_sdk_json("1.2.3");
let csproj = make_csproj_with_tfm("TargetFramework", DOTNET_TFM_PINNED_VERSION);
touch_path(&workspace, "global.json", Some(&global_json))?;
touch_path(
&workspace,
"deep/path/to/project/project.csproj",
Some(&csproj),
)?;
expect_output(
&workspace,
"deep/path/to/project",
Some(DOTNET_PINNED_VERSION_OUTPUT_PATTERN),
)?;
workspace.close()
}
#[test]
#[ignore]
fn shows_single_tfm() -> io::Result<()> {
let workspace = create_workspace(false)?;
let csproj = make_csproj_with_tfm("TargetFramework", "netstandard2.0");
touch_path(&workspace, "project.csproj", Some(&csproj))?;
expect_output(&workspace, ".", Some("•NET v2.2.402"))?;
expect_output(&workspace, ".", Some("🎯 netstandard2.0"))?;
workspace.close()
}
#[test]
#[ignore]
fn shows_multiple_tfms() -> io::Result<()> {
let workspace = create_workspace(false)?;
let csproj = make_csproj_with_tfm("TargetFrameworks", "netstandard2.0;net461");
touch_path(&workspace, "project.csproj", Some(&csproj))?;
expect_output(&workspace, ".", Some("•NET v2.2.402"))?;
expect_output(&workspace, ".", Some("🎯 netstandard2.0;net461"))?;
workspace.close()
}
fn create_workspace(is_repo: bool) -> io::Result<TempDir> {
let repo_dir = tempfile::tempdir()?;
if is_repo {
let mut command = Command::new("git");
command
.args(&["init", "--quiet"])
.stdout(Stdio::null())
.stderr(Stdio::null())
.stdin(Stdio::null())
.current_dir(repo_dir.path());
if !command.status()?.success() {
return Err(Error::from(ErrorKind::Other));
}
}
Ok(repo_dir)
}
fn touch_path(workspace: &TempDir, relative_path: &str, contents: Option<&str>) -> io::Result<()> {
let path = workspace.path().join(relative_path);
DirBuilder::new().recursive(true).create(
path.parent()
.expect("Expected relative_path to be a file in a directory"),
)?;
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&path)?;
write!(file, "{}", contents.unwrap_or(""))?;
file.sync_data()
}
fn make_pinned_sdk_json(version: &str) -> String {
let json_text = r#"
{
"sdk": {
"version": "INSERT_VERSION"
}
}
"#;
json_text.replace("INSERT_VERSION", version)
}
fn make_csproj_with_tfm(tfm_element: &str, tfm: &str) -> String {
let json_text = r#"
<Project>
<PropertyGroup>
<TFM_ELEMENT>TFM_VALUE</TFM_ELEMENT>
</PropertyGroup>
</Project>
"#;
json_text
.replace("TFM_ELEMENT", tfm_element)
.replace("TFM_VALUE", tfm)
}
fn expect_output(workspace: &TempDir, run_from: &str, pattern: Option<&str>) -> io::Result<()> {
let run_path = workspace.path().join(run_from);
let output = common::render_module("dotnet")
.current_dir(run_path)
.output()?;
let text = String::from_utf8(output.stdout).unwrap();
// This can be helpful for debugging
eprintln!("The dotnet module showed: {}", text);
match pattern {
Some(pattern) => {
let re = Regex::new(pattern).unwrap();
assert!(re.is_match(&text));
}
None => assert!(text.is_empty()),
}
Ok(())
}

View File

@ -1,141 +0,0 @@
use ansi_term::{Color, Style};
use std::io;
use crate::common;
use crate::common::TestCommand;
const TEST_VAR_VALUE: &str = "astronauts";
#[test]
fn empty_config() -> io::Result<()> {
let output = common::render_module("env_var")
.env_clear()
.use_config(toml::toml! {
[env_var]
})
.output()?;
let expected = "";
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn defined_variable() -> io::Result<()> {
let output = common::render_module("env_var")
.env_clear()
.use_config(toml::toml! {
[env_var]
variable = "TEST_VAR"
})
.env("TEST_VAR", TEST_VAR_VALUE)
.output()?;
let expected = format!("with {} ", style().paint(TEST_VAR_VALUE));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn undefined_variable() -> io::Result<()> {
let output = common::render_module("env_var")
.env_clear()
.use_config(toml::toml! {
[env_var]
variable = "TEST_VAR"
})
.output()?;
let expected = "";
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn default_has_no_effect() -> io::Result<()> {
let output = common::render_module("env_var")
.env_clear()
.use_config(toml::toml! {
[env_var]
variable = "TEST_VAR"
default = "N/A"
})
.env("TEST_VAR", TEST_VAR_VALUE)
.output()?;
let expected = format!("with {} ", style().paint(TEST_VAR_VALUE));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn default_takes_effect() -> io::Result<()> {
let output = common::render_module("env_var")
.env_clear()
.use_config(toml::toml! {
[env_var]
variable = "UNDEFINED_TEST_VAR"
default = "N/A"
})
.output()?;
let expected = format!("with {} ", style().paint("N/A"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn symbol() -> io::Result<()> {
let output = common::render_module("env_var")
.env_clear()
.use_config(toml::toml! {
[env_var]
variable = "TEST_VAR"
format = "with [■ $env_value](black bold dimmed) "
})
.env("TEST_VAR", TEST_VAR_VALUE)
.output()?;
let expected = format!("with {} ", style().paint(format!("{}", TEST_VAR_VALUE)));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn prefix() -> io::Result<()> {
let output = common::render_module("env_var")
.env_clear()
.use_config(toml::toml! {
[env_var]
variable = "TEST_VAR"
format = "with [_$env_value](black bold dimmed) "
})
.env("TEST_VAR", TEST_VAR_VALUE)
.output()?;
let expected = format!("with {} ", style().paint(format!("_{}", TEST_VAR_VALUE)));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn suffix() -> io::Result<()> {
let output = common::render_module("env_var")
.env_clear()
.use_config(toml::toml! {
[env_var]
variable = "TEST_VAR"
format = "with [${env_value}_](black bold dimmed) "
})
.env("TEST_VAR", TEST_VAR_VALUE)
.output()?;
let expected = format!("with {} ", style().paint(format!("{}_", TEST_VAR_VALUE)));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
fn style() -> Style {
// default style
Color::Black.bold().dimmed()
}

View File

@ -1,159 +0,0 @@
use std::fs::{create_dir, File};
use std::io::{self, Write};
use ansi_term::Color;
use crate::common::{self, TestCommand};
#[test]
fn account_set() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let active_config_path = dir.path().join("active_config");
let mut active_config_file = File::create(&active_config_path)?;
active_config_file.write_all(b"default")?;
create_dir(dir.path().join("configurations"))?;
let config_default_path = dir.path().join("configurations/config_default");
let mut config_default_file = File::create(&config_default_path)?;
config_default_file.write_all(
b"[core]
account = foo@example.com
",
)?;
let output = common::render_module("gcloud")
.env("CLOUDSDK_CONFIG", dir.path().to_string_lossy().as_ref())
.output()?;
let expected = format!("on {} ", Color::Blue.bold().paint("☁️ foo@example.com"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(actual, expected);
dir.close()
}
#[test]
fn account_and_region_set() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let active_config_path = dir.path().join("active_config");
let mut active_config_file = File::create(&active_config_path)?;
active_config_file.write_all(b"default")?;
create_dir(dir.path().join("configurations"))?;
let config_default_path = dir.path().join("configurations/config_default");
let mut config_default_file = File::create(&config_default_path)?;
config_default_file.write_all(
b"[core]
account = foo@example.com
[compute]
region = us-central1
",
)?;
let output = common::render_module("gcloud")
.env("CLOUDSDK_CONFIG", dir.path().to_string_lossy().as_ref())
.output()?;
let expected = format!(
"on {} ",
Color::Blue.bold().paint("☁️ foo@example.com(us-central1)")
);
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(actual, expected);
dir.close()
}
#[test]
fn account_and_region_set_with_alias() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let active_config_path = dir.path().join("active_config");
let mut active_config_file = File::create(&active_config_path)?;
active_config_file.write_all(b"default")?;
create_dir(dir.path().join("configurations"))?;
let config_default_path = dir.path().join("configurations/config_default");
let mut config_default_file = File::create(&config_default_path)?;
config_default_file.write_all(
b"[core]
account = foo@example.com
[compute]
region = us-central1
",
)?;
let output = common::render_module("gcloud")
.env("CLOUDSDK_CONFIG", dir.path().to_string_lossy().as_ref())
.use_config(toml::toml! {
[gcloud.region_aliases]
us-central1 = "uc1"
})
.output()?;
let expected = format!("on {} ", Color::Blue.bold().paint("☁️ foo@example.com(uc1)"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(actual, expected);
dir.close()
}
#[test]
fn active_set() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let active_config_path = dir.path().join("active_config");
let mut active_config_file = File::create(&active_config_path)?;
active_config_file.write_all(b"default1")?;
let output = common::render_module("gcloud")
.env("CLOUDSDK_CONFIG", dir.path().to_string_lossy().as_ref())
.use_config(toml::toml! {
[gcloud]
format = "on [$symbol$active]($style) "
})
.output()?;
let expected = format!("on {} ", Color::Blue.bold().paint("☁️ default1"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(actual, expected);
dir.close()
}
#[test]
fn project_set() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let active_config_path = dir.path().join("active_config");
let mut active_config_file = File::create(&active_config_path)?;
active_config_file.write_all(b"default")?;
create_dir(dir.path().join("configurations"))?;
let config_default_path = dir.path().join("configurations/config_default");
let mut config_default_file = File::create(&config_default_path)?;
config_default_file.write_all(
b"[core]
project = abc
",
)?;
let output = common::render_module("gcloud")
.env("CLOUDSDK_CONFIG", dir.path().to_string_lossy().as_ref())
.use_config(toml::toml! {
[gcloud]
format = "on [$symbol$project]($style) "
})
.output()?;
let expected = format!("on {} ", Color::Blue.bold().paint("☁️ abc"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(actual, expected);
dir.close()
}
#[test]
fn region_not_set_with_display_region() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let output = common::render_module("gcloud")
.env("CLOUDSDK_CONFIG", dir.path().to_string_lossy().as_ref())
.use_config(toml::toml! {
[gcloud]
format = "on [$symbol$region]($style) "
})
.output()?;
let expected = "";
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
dir.close()
}

View File

@ -1,292 +0,0 @@
use ansi_term::Color;
use remove_dir_all::remove_dir_all;
use std::io;
use std::path::Path;
use std::process::Command;
use crate::common::{self, TestCommand};
#[test]
fn show_nothing_on_empty_dir() -> io::Result<()> {
let repo_dir = tempfile::tempdir()?;
let output = common::render_module("git_branch")
.arg("--path")
.arg(repo_dir.path())
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = "";
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn test_changed_truncation_symbol() -> io::Result<()> {
test_truncate_length_with_config(
"1337_hello_world",
15,
"1337_hello_worl",
"%",
"truncation_symbol = \"%\"",
)
}
#[test]
fn test_no_truncation_symbol() -> io::Result<()> {
test_truncate_length_with_config(
"1337_hello_world",
15,
"1337_hello_worl",
"",
"truncation_symbol = \"\"",
)
}
#[test]
fn test_multi_char_truncation_symbol() -> io::Result<()> {
test_truncate_length_with_config(
"1337_hello_world",
15,
"1337_hello_worl",
"a",
"truncation_symbol = \"apple\"",
)
}
#[test]
fn test_ascii_boundary_below() -> io::Result<()> {
test_truncate_length("1337_hello_world", 15, "1337_hello_worl", "")
}
#[test]
fn test_ascii_boundary_on() -> io::Result<()> {
test_truncate_length("1337_hello_world", 16, "1337_hello_world", "")
}
#[test]
fn test_ascii_boundary_above() -> io::Result<()> {
test_truncate_length("1337_hello_world", 17, "1337_hello_world", "")
}
#[test]
fn test_one() -> io::Result<()> {
test_truncate_length("1337_hello_world", 1, "1", "")
}
#[test]
fn test_zero() -> io::Result<()> {
test_truncate_length("1337_hello_world", 0, "1337_hello_world", "")
}
#[test]
fn test_negative() -> io::Result<()> {
test_truncate_length("1337_hello_world", -1, "1337_hello_world", "")
}
#[test]
fn test_hindi_truncation() -> io::Result<()> {
test_truncate_length("नमस्ते", 3, "नमस्", "")
}
#[test]
fn test_hindi_truncation2() -> io::Result<()> {
test_truncate_length("नमस्त", 3, "नमस्", "")
}
#[test]
fn test_japanese_truncation() -> io::Result<()> {
test_truncate_length("がんばってね", 4, "がんばっ", "")
}
#[test]
fn test_format_no_branch() -> io::Result<()> {
test_format("1337_hello_world", "no_branch", "", "no_branch")
}
#[test]
fn test_format_just_branch_name() -> io::Result<()> {
test_format("1337_hello_world", "$branch", "", "1337_hello_world")
}
#[test]
fn test_format_just_branch_name_color() -> io::Result<()> {
test_format(
"1337_hello_world",
"[$branch](bold blue)",
"",
Color::Blue.bold().paint("1337_hello_world").to_string(),
)
}
#[test]
fn test_format_mixed_colors() -> io::Result<()> {
test_format(
"1337_hello_world",
"branch: [$branch](bold blue) [THE COLORS](red) ",
"",
format!(
"branch: {} {} ",
Color::Blue.bold().paint("1337_hello_world").to_string(),
Color::Red.paint("THE COLORS").to_string()
),
)
}
#[test]
fn test_format_symbol_style() -> io::Result<()> {
test_format(
"1337_hello_world",
"$symbol[$branch]($style)",
r#"
symbol = "git: "
style = "green"
"#,
format!(
"git: {}",
Color::Green.paint("1337_hello_world").to_string(),
),
)
}
#[test]
fn test_works_with_unborn_default_branch() -> io::Result<()> {
let repo_dir = tempfile::tempdir()?.into_path();
Command::new("git")
.args(&["init"])
.current_dir(&repo_dir)
.output()?;
Command::new("git")
.args(&["symbolic-ref", "HEAD", "refs/heads/main"])
.current_dir(&repo_dir)
.output()?;
let output = common::render_module("git_branch")
.arg("--path")
.arg(&repo_dir)
.output()
.unwrap();
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"on {} ",
Color::Purple.bold().paint(format!("\u{e0a0} {}", "main")),
);
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
fn test_git_dir_env_variable() -> io::Result<()> {
let repo_dir = tempfile::tempdir()?.into_path();
Command::new("git")
.args(&["init"])
.current_dir(&repo_dir)
.output()?;
let output = common::render_module("git_branch")
.env("GIT_DIR", Path::new(&repo_dir).join(".git"))
.output()
.unwrap();
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"on {} ",
Color::Purple.bold().paint(format!("\u{e0a0} {}", "master")),
);
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
fn test_truncate_length(
branch_name: &str,
truncate_length: i64,
expected_name: &str,
truncation_symbol: &str,
) -> io::Result<()> {
test_truncate_length_with_config(
branch_name,
truncate_length,
expected_name,
truncation_symbol,
"",
)
}
fn test_truncate_length_with_config(
branch_name: &str,
truncate_length: i64,
expected_name: &str,
truncation_symbol: &str,
config_options: &str,
) -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
Command::new("git")
.args(&["checkout", "-b", branch_name])
.current_dir(repo_dir.as_path())
.output()?;
let output = common::render_module("git_branch")
.use_config(
toml::from_str(&format!(
"
[git_branch]
truncation_length = {}
{}
",
truncate_length, config_options
))
.unwrap(),
)
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"on {} ",
Color::Purple
.bold()
.paint(format!("\u{e0a0} {}{}", expected_name, truncation_symbol)),
);
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
fn test_format<T: AsRef<str>>(
branch_name: &str,
format: &str,
config_options: &str,
expected: T,
) -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
Command::new("git")
.args(&["checkout", "-b", branch_name])
.current_dir(repo_dir.as_path())
.output()?;
let output = common::render_module("git_branch")
.use_config(
toml::from_str(&format!(
r#"
[git_branch]
format = "{}"
{}
"#,
format, config_options
))
.unwrap(),
)
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected.as_ref(), actual);
remove_dir_all(repo_dir)
}

View File

@ -1,135 +0,0 @@
use ansi_term::Color;
use remove_dir_all::remove_dir_all;
use std::process::Command;
use std::{io, str};
use crate::common::{self, TestCommand};
#[test]
fn show_nothing_on_empty_dir() -> io::Result<()> {
let repo_dir = tempfile::tempdir()?;
let output = common::render_module("git_commit")
.arg("--path")
.arg(repo_dir.path())
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = "";
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn test_render_commit_hash() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
let mut git_output = Command::new("git")
.args(&["rev-parse", "HEAD"])
.current_dir(repo_dir.as_path())
.output()?
.stdout;
git_output.truncate(7);
let expected_hash = str::from_utf8(&git_output).unwrap();
let output = common::render_module("git_commit")
.use_config(toml::toml! {
[git_commit]
only_detached = false
})
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let mut expected = Color::Green
.bold()
.paint(format!("({})", expected_hash))
.to_string();
expected.push(' ');
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
fn test_render_commit_hash_len_override() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
let mut git_output = Command::new("git")
.args(&["rev-parse", "HEAD"])
.current_dir(repo_dir.as_path())
.output()?
.stdout;
git_output.truncate(14);
let expected_hash = str::from_utf8(&git_output).unwrap();
let output = common::render_module("git_commit")
.use_config(toml::toml! {
[git_commit]
only_detached = false
commit_hash_length = 14
})
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let mut expected = Color::Green
.bold()
.paint(format!("({})", expected_hash))
.to_string();
expected.push(' ');
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
fn test_render_commit_hash_only_detached_on_branch() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
let output = common::render_module("git_commit")
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!("", actual);
remove_dir_all(repo_dir)
}
#[test]
fn test_render_commit_hash_only_detached_on_detached() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
Command::new("git")
.args(&["checkout", "@~1"])
.current_dir(repo_dir.as_path())
.output()?;
let mut git_output = Command::new("git")
.args(&["rev-parse", "HEAD"])
.current_dir(repo_dir.as_path())
.output()?
.stdout;
git_output.truncate(7);
let expected_hash = str::from_utf8(&git_output).unwrap();
let output = common::render_module("git_commit")
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let mut expected = Color::Green
.bold()
.paint(format!("({})", expected_hash))
.to_string();
expected.push(' ');
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}

View File

@ -1,217 +0,0 @@
use super::common;
use ansi_term::Color;
use std::ffi::OsStr;
use std::fs::OpenOptions;
use std::io::{self, Error, ErrorKind, Write};
use std::process::{Command, Stdio};
#[test]
fn show_nothing_on_empty_dir() -> io::Result<()> {
let repo_dir = tempfile::tempdir()?;
let output = common::render_module("git_state")
.arg("--path")
.arg(repo_dir.path())
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = "";
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
fn shows_rebasing() -> io::Result<()> {
let repo_dir = create_repo_with_conflict()?;
let path = path_str(&repo_dir)?;
run_git_cmd(&["rebase", "other-branch"], Some(path), false)?;
let output = common::render_module("git_state")
.arg("--path")
.arg(&path)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let mut expected = Color::Yellow.bold().paint("(REBASING 1/1)").to_string();
expected.push(' ');
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn shows_merging() -> io::Result<()> {
let repo_dir = create_repo_with_conflict()?;
let path = path_str(&repo_dir)?;
run_git_cmd(&["merge", "other-branch"], Some(path), false)?;
let output = common::render_module("git_state")
.arg("--path")
.arg(&path)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let mut expected = Color::Yellow.bold().paint("(MERGING)").to_string();
expected.push(' ');
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn shows_cherry_picking() -> io::Result<()> {
let repo_dir = create_repo_with_conflict()?;
let path = path_str(&repo_dir)?;
run_git_cmd(&["cherry-pick", "other-branch"], Some(path), false)?;
let output = common::render_module("git_state")
.arg("--path")
.arg(&path)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let mut expected = Color::Yellow.bold().paint("(CHERRY-PICKING)").to_string();
expected.push(' ');
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn shows_bisecting() -> io::Result<()> {
let repo_dir = create_repo_with_conflict()?;
let path = path_str(&repo_dir)?;
run_git_cmd(&["bisect", "start"], Some(path), false)?;
let output = common::render_module("git_state")
.arg("--path")
.arg(&path)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let mut expected = Color::Yellow.bold().paint("(BISECTING)").to_string();
expected.push(' ');
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn shows_reverting() -> io::Result<()> {
let repo_dir = create_repo_with_conflict()?;
let path = path_str(&repo_dir)?;
run_git_cmd(&["revert", "--no-commit", "HEAD~1"], Some(path), false)?;
let output = common::render_module("git_state")
.arg("--path")
.arg(&path)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let mut expected = Color::Yellow.bold().paint("(REVERTING)").to_string();
expected.push(' ');
assert_eq!(expected, actual);
Ok(())
}
fn run_git_cmd<A, S>(args: A, dir: Option<&str>, expect_ok: bool) -> io::Result<()>
where
A: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let mut command = Command::new("git");
command
.args(args)
.stdout(Stdio::null())
.stderr(Stdio::null())
.stdin(Stdio::null());
if let Some(dir) = dir {
command.current_dir(dir);
}
let status = command.status()?;
if expect_ok && !status.success() {
Err(Error::from(ErrorKind::Other))
} else {
Ok(())
}
}
fn create_repo_with_conflict() -> io::Result<tempfile::TempDir> {
let repo_dir = tempfile::tempdir()?;
let path = path_str(&repo_dir)?;
let conflicted_file = repo_dir.path().join("the_file");
let write_file = |text: &str| {
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&conflicted_file)?;
write!(file, "{}", text)
};
// Initialize a new git repo
run_git_cmd(&["init", "--quiet", path], None, true)?;
// Set local author info
run_git_cmd(
&["config", "--local", "user.email", "starship@example.com"],
Some(path),
true,
)?;
run_git_cmd(
&["config", "--local", "user.name", "starship"],
Some(path),
true,
)?;
// Write a file on master and commit it
write_file("Version A")?;
run_git_cmd(&["add", "the_file"], Some(path), true)?;
run_git_cmd(&["commit", "--message", "Commit A"], Some(path), true)?;
// Switch to another branch, and commit a change to the file
run_git_cmd(&["checkout", "-b", "other-branch"], Some(path), true)?;
write_file("Version B")?;
run_git_cmd(
&["commit", "--all", "--message", "Commit B"],
Some(path),
true,
)?;
// Switch back to master, and commit a third change to the file
run_git_cmd(&["checkout", "master"], Some(path), true)?;
write_file("Version C")?;
run_git_cmd(
&["commit", "--all", "--message", "Commit C"],
Some(path),
true,
)?;
Ok(repo_dir)
}
fn path_str(repo_dir: &tempfile::TempDir) -> io::Result<&str> {
repo_dir
.path()
.to_str()
.ok_or_else(|| Error::from(ErrorKind::Other))
}

View File

@ -1,680 +0,0 @@
use ansi_term::{ANSIStrings, Color};
use remove_dir_all::remove_dir_all;
use std::fs::{self, File};
use std::io;
use std::path::PathBuf;
use std::process::Command;
use crate::common::{self, TestCommand};
/// Right after the calls to git the filesystem state may not have finished
/// updating yet causing some of the tests to fail. These barriers are placed
/// after each call to git.
/// This barrier is windows-specific though other operating systems may need it
/// in the future.
#[cfg(not(windows))]
fn barrier() {}
#[cfg(windows)]
fn barrier() {
std::thread::sleep(std::time::Duration::from_millis(500));
}
fn format_output(symbols: &str) -> String {
format!("{} ", Color::Red.bold().paint(format!("[{}]", symbols)))
}
#[test]
fn show_nothing_on_empty_dir() -> io::Result<()> {
let repo_dir = tempfile::tempdir()?;
let output = common::render_module("git_status")
.arg("--path")
.arg(repo_dir.path())
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = "";
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
#[ignore]
fn shows_behind() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
behind(&repo_dir)?;
let output = common::render_module("git_status")
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_behind_with_count() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
behind(&repo_dir)?;
let output = common::render_module("git_status")
.use_config(toml::toml! {
[git_status]
behind = "⇣$count"
})
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("⇣1");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_ahead() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
File::create(repo_dir.join("readme.md"))?.sync_all()?;
ahead(&repo_dir)?;
let output = common::render_module("git_status")
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_ahead_with_count() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
File::create(repo_dir.join("readme.md"))?.sync_all()?;
ahead(&repo_dir)?;
let output = common::render_module("git_status")
.use_config(toml::toml! {
[git_status]
ahead="⇡$count"
})
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("⇡1");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_diverged() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
diverge(&repo_dir)?;
let output = common::render_module("git_status")
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_diverged_with_count() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
diverge(&repo_dir)?;
let output = common::render_module("git_status")
.use_config(toml::toml! {
[git_status]
diverged=r"⇕⇡$ahead_count⇣$behind_count"
})
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("⇕⇡1⇣1");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_conflicted() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
create_conflict(&repo_dir)?;
let output = common::render_module("git_status")
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("=");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_conflicted_with_count() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
create_conflict(&repo_dir)?;
let output = common::render_module("git_status")
.use_config(toml::toml! {
[git_status]
conflicted = "=$count"
})
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("=1");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_untracked_file() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
create_untracked(&repo_dir)?;
let output = common::render_module("git_status")
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("?");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_untracked_file_with_count() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
create_untracked(&repo_dir)?;
let output = common::render_module("git_status")
.use_config(toml::toml! {
[git_status]
untracked = "?$count"
})
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("?1");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn doesnt_show_untracked_file_if_disabled() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
create_untracked(&repo_dir)?;
Command::new("git")
.args(&["config", "status.showUntrackedFiles", "no"])
.current_dir(repo_dir.as_path())
.output()?;
barrier();
let output = common::render_module("git_status")
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = "";
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_stashed() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
barrier();
create_stash(&repo_dir)?;
Command::new("git")
.args(&["reset", "--hard", "HEAD"])
.current_dir(repo_dir.as_path())
.output()?;
barrier();
let output = common::render_module("git_status")
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("$");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
fn shows_stashed_with_count() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
barrier();
create_stash(&repo_dir)?;
barrier();
Command::new("git")
.args(&["reset", "--hard", "HEAD"])
.current_dir(repo_dir.as_path())
.output()?;
barrier();
let output = common::render_module("git_status")
.use_config(toml::toml! {
[git_status]
stashed = r"\$$count"
})
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("$1");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_modified() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
create_modified(&repo_dir)?;
let output = common::render_module("git_status")
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("!");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_modified_with_count() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
create_modified(&repo_dir)?;
let output = common::render_module("git_status")
.use_config(toml::toml! {
[git_status]
modified = "!$count"
})
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("!1");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_staged_file() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
create_staged(&repo_dir)?;
let output = common::render_module("git_status")
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("+");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_staged_file_with_count() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
create_staged(&repo_dir)?;
let output = common::render_module("git_status")
.use_config(toml::toml! {
[git_status]
staged = "+[$count](green)"
})
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"{} ",
ANSIStrings(&[
Color::Red.bold().paint("[+"),
Color::Green.paint("1"),
Color::Red.bold().paint("]"),
])
);
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_renamed_file() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
create_renamed(&repo_dir)?;
let output = common::render_module("git_status")
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("»");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_renamed_file_with_count() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
create_renamed(&repo_dir)?;
let output = common::render_module("git_status")
.use_config(toml::toml! {
[git_status]
renamed = "»$count"
})
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("»1");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_deleted_file() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
create_deleted(&repo_dir)?;
let output = common::render_module("git_status")
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
#[test]
#[ignore]
fn shows_deleted_file_with_count() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
create_deleted(&repo_dir)?;
let output = common::render_module("git_status")
.use_config(toml::toml! {
[git_status]
deleted = "✘$count"
})
.arg("--path")
.arg(&repo_dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format_output("✘1");
assert_eq!(expected, actual);
remove_dir_all(repo_dir)
}
// Whenever a file is manually renamed, git itself ('git status') does not treat such file as renamed,
// but as untracked instead. The following test checks if manually deleted and manually renamed
// files are tracked by git_status module in the same way 'git status' does.
#[test]
#[ignore]
fn ignore_manually_renamed() -> io::Result<()> {
let repo_dir = common::create_fixture_repo()?;
File::create(repo_dir.join("a"))?.sync_all()?;
File::create(repo_dir.join("b"))?.sync_all()?;
Command::new("git")
.args(&["add", "--all"])
.current_dir(&repo_dir)
.output()?;
Command::new("git")
.args(&["commit", "-m", "add new files"])
.current_dir(&repo_dir)
.output()?;
fs::remove_file(repo_dir.join("a"))?;
fs::rename(repo_dir.join("b"), repo_dir.join("c"))?;
barrier();
let output = common::render_module("git_status")
.arg("--path")
.arg(&repo_dir)
.env_clear()
.use_config(toml::toml! {
[git_status]
prefix = ""
suffix = ""
style = ""
ahead = "A"
deleted = "D"
untracked = "U"
renamed = "R"
})
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert!(actual.contains('A'));
assert!(actual.contains('D'));
assert!(actual.contains('U'));
assert!(!actual.contains('R'));
remove_dir_all(repo_dir)
}
fn ahead(repo_dir: &PathBuf) -> io::Result<()> {
File::create(repo_dir.join("readme.md"))?.sync_all()?;
Command::new("git")
.args(&["commit", "-am", "Update readme"])
.current_dir(&repo_dir)
.output()?;
barrier();
Ok(())
}
fn behind(repo_dir: &PathBuf) -> io::Result<()> {
Command::new("git")
.args(&["reset", "--hard", "HEAD^"])
.current_dir(repo_dir.as_path())
.output()?;
barrier();
Ok(())
}
fn diverge(repo_dir: &PathBuf) -> io::Result<()> {
Command::new("git")
.args(&["reset", "--hard", "HEAD^"])
.current_dir(repo_dir.as_path())
.output()?;
barrier();
fs::write(repo_dir.join("Cargo.toml"), " ")?;
Command::new("git")
.args(&["commit", "-am", "Update readme"])
.current_dir(repo_dir.as_path())
.output()?;
barrier();
Ok(())
}
fn create_conflict(repo_dir: &PathBuf) -> io::Result<()> {
Command::new("git")
.args(&["reset", "--hard", "HEAD^"])
.current_dir(repo_dir.as_path())
.output()?;
barrier();
fs::write(repo_dir.join("readme.md"), "# goodbye")?;
Command::new("git")
.args(&["add", "."])
.current_dir(repo_dir.as_path())
.output()?;
barrier();
Command::new("git")
.args(&["commit", "-m", "Change readme"])
.current_dir(repo_dir.as_path())
.output()?;
barrier();
Command::new("git")
.args(&["pull", "--rebase"])
.current_dir(repo_dir.as_path())
.output()?;
barrier();
Ok(())
}
fn create_stash(repo_dir: &PathBuf) -> io::Result<()> {
File::create(repo_dir.join("readme.md"))?.sync_all()?;
barrier();
Command::new("git")
.args(&["stash", "--all"])
.current_dir(repo_dir.as_path())
.output()?;
barrier();
Ok(())
}
fn create_untracked(repo_dir: &PathBuf) -> io::Result<()> {
File::create(repo_dir.join("license"))?.sync_all()?;
Ok(())
}
fn create_modified(repo_dir: &PathBuf) -> io::Result<()> {
File::create(repo_dir.join("readme.md"))?.sync_all()?;
Ok(())
}
fn create_staged(repo_dir: &PathBuf) -> io::Result<()> {
File::create(repo_dir.join("license"))?.sync_all()?;
Command::new("git")
.args(&["add", "."])
.current_dir(repo_dir.as_path())
.output()?;
barrier();
Ok(())
}
fn create_renamed(repo_dir: &PathBuf) -> io::Result<()> {
Command::new("git")
.args(&["mv", "readme.md", "readme.md.bak"])
.current_dir(repo_dir.as_path())
.output()?;
barrier();
Command::new("git")
.args(&["add", "-A"])
.current_dir(repo_dir.as_path())
.output()?;
barrier();
Ok(())
}
fn create_deleted(repo_dir: &PathBuf) -> io::Result<()> {
fs::remove_file(repo_dir.join("readme.md"))?;
Ok(())
}

View File

@ -1,239 +0,0 @@
use ansi_term::{Color, Style};
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, io};
use crate::common::{self, TestCommand};
enum Expect<'a> {
BranchName(&'a str),
Empty,
NoTruncation,
Symbol(&'a str),
Style(Style),
TruncationSymbol(&'a str),
}
#[test]
fn show_nothing_on_empty_dir() -> io::Result<()> {
let repo_dir = tempfile::tempdir()?;
let output = common::render_module("hg_branch")
.arg("--path")
.arg(repo_dir.path())
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = "";
assert_eq!(expected, actual);
repo_dir.close()
}
#[test]
#[ignore]
fn test_hg_get_branch_fails() -> io::Result<()> {
let tempdir = tempfile::tempdir()?;
// Create a fake corrupted mercurial repo.
let hgdir = tempdir.path().join(".hg");
fs::create_dir(&hgdir)?;
fs::write(&hgdir.join("requires"), "fake-corrupted-repo")?;
expect_hg_branch_with_config(
tempdir.path(),
"",
&[Expect::BranchName(&"default"), Expect::NoTruncation],
)?;
tempdir.close()
}
#[test]
#[ignore]
fn test_hg_get_branch_autodisabled() -> io::Result<()> {
let tempdir = tempfile::tempdir()?;
expect_hg_branch_with_config(tempdir.path(), "", &[Expect::Empty])?;
tempdir.close()
}
#[test]
#[ignore]
fn test_hg_bookmark() -> io::Result<()> {
let tempdir = tempfile::tempdir()?;
let repo_dir = create_fixture_hgrepo(&tempdir)?;
run_hg(&["bookmark", "bookmark-101"], &repo_dir)?;
expect_hg_branch_with_config(
&repo_dir,
"",
&[Expect::BranchName(&"bookmark-101"), Expect::NoTruncation],
)?;
tempdir.close()
}
#[test]
#[ignore]
fn test_default_truncation_symbol() -> io::Result<()> {
let tempdir = tempfile::tempdir()?;
let repo_dir = create_fixture_hgrepo(&tempdir)?;
run_hg(&["branch", "-f", "branch-name-101"], &repo_dir)?;
run_hg(
&[
"commit",
"-m",
"empty commit 101",
"-u",
"fake user <fake@user>",
],
&repo_dir,
)?;
expect_hg_branch_with_config(
&repo_dir,
"truncation_length = 14",
&[Expect::BranchName(&"branch-name-10")],
)?;
tempdir.close()
}
#[test]
#[ignore]
fn test_configured_symbols() -> io::Result<()> {
let tempdir = tempfile::tempdir()?;
let repo_dir = create_fixture_hgrepo(&tempdir)?;
run_hg(&["branch", "-f", "branch-name-121"], &repo_dir)?;
run_hg(
&[
"commit",
"-m",
"empty commit 121",
"-u",
"fake user <fake@user>",
],
&repo_dir,
)?;
expect_hg_branch_with_config(
&repo_dir,
r#"
symbol = "B "
truncation_length = 14
truncation_symbol = "%"
"#,
&[
Expect::BranchName(&"branch-name-12"),
Expect::Symbol(&"B"),
Expect::TruncationSymbol(&"%"),
],
)?;
tempdir.close()
}
#[test]
#[ignore]
fn test_configured_style() -> io::Result<()> {
let tempdir = tempfile::tempdir()?;
let repo_dir = create_fixture_hgrepo(&tempdir)?;
run_hg(&["branch", "-f", "branch-name-131"], &repo_dir)?;
run_hg(
&[
"commit",
"-m",
"empty commit 131",
"-u",
"fake user <fake@user>",
],
&repo_dir,
)?;
expect_hg_branch_with_config(
&repo_dir,
r#"
style = "underline blue"
"#,
&[
Expect::BranchName(&"branch-name-131"),
Expect::Style(Color::Blue.underline()),
Expect::TruncationSymbol(&""),
],
)?;
tempdir.close()
}
fn expect_hg_branch_with_config(
repo_dir: &Path,
config_options: &str,
expectations: &[Expect],
) -> io::Result<()> {
let output = common::render_module("hg_branch")
.use_config(toml::from_str(&format!(
r#"
[hg_branch]
{}
"#,
config_options
))?)
.arg("--path")
.arg(repo_dir.to_str().unwrap())
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let mut expect_branch_name = "default";
let mut expect_style = Color::Purple.bold();
let mut expect_symbol = "\u{e0a0}";
let mut expect_truncation_symbol = "";
for expect in expectations {
match expect {
Expect::Empty => {
assert_eq!("", actual);
return Ok(());
}
Expect::Symbol(symbol) => {
expect_symbol = symbol;
}
Expect::TruncationSymbol(truncation_symbol) => {
expect_truncation_symbol = truncation_symbol;
}
Expect::NoTruncation => {
expect_truncation_symbol = "";
}
Expect::BranchName(branch_name) => {
expect_branch_name = branch_name;
}
Expect::Style(style) => expect_style = *style,
}
}
let expected = format!(
"on {} ",
expect_style.paint(format!(
"{} {}{}",
expect_symbol, expect_branch_name, expect_truncation_symbol
)),
);
assert_eq!(expected, actual);
Ok(())
}
pub fn create_fixture_hgrepo(tempdir: &tempfile::TempDir) -> io::Result<PathBuf> {
let repo_path = tempdir.path().join("hg-repo");
let fixture_path = env::current_dir()?.join("tests/fixtures/hg-repo.bundle");
run_hg(
&[
"clone",
fixture_path.to_str().unwrap(),
repo_path.to_str().unwrap(),
],
&tempdir.path(),
)?;
Ok(repo_path)
}
fn run_hg(args: &[&str], repo_dir: &Path) -> io::Result<()> {
Command::new("hg")
.args(args)
.current_dir(&repo_dir)
.output()?;
Ok(())
}

View File

@ -1,120 +0,0 @@
use ansi_term::{Color, Style};
use std::io;
use crate::common;
use crate::common::TestCommand;
#[test]
fn ssh_only_false() -> io::Result<()> {
let hostname = match get_hostname() {
Some(h) => h,
None => return hostname_not_tested(),
};
let output = common::render_module("hostname")
.env_clear()
.use_config(toml::toml! {
[hostname]
ssh_only = false
trim_at = ""
})
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("on {} ", style().paint(hostname));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn no_ssh() -> io::Result<()> {
let output = common::render_module("hostname")
.env_clear()
.use_config(toml::toml! {
[hostname]
ssh_only = true
})
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!("", actual);
Ok(())
}
#[test]
fn ssh() -> io::Result<()> {
let hostname = match get_hostname() {
Some(h) => h,
None => return hostname_not_tested(),
};
let output = common::render_module("hostname")
.env_clear()
.use_config(toml::toml! {
[hostname]
ssh_only = true
trim_at = ""
})
.env("SSH_CONNECTION", "something")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("on {} ", style().paint(hostname));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn no_trim_at() -> io::Result<()> {
let hostname = match get_hostname() {
Some(h) => h,
None => return hostname_not_tested(),
};
let output = common::render_module("hostname")
.env_clear()
.use_config(toml::toml! {
[hostname]
ssh_only = false
trim_at = ""
})
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("on {} ", style().paint(hostname));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn trim_at() -> io::Result<()> {
let hostname = match get_hostname() {
Some(h) => h,
None => return hostname_not_tested(),
};
let (remainder, trim_at) = hostname.split_at(1);
let output = common::render_module("hostname")
.env_clear()
.use_config(toml::toml! {
[hostname]
ssh_only = false
trim_at = trim_at
})
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("on {} ", style().paint(remainder));
assert_eq!(expected, actual);
Ok(())
}
fn get_hostname() -> Option<String> {
match gethostname::gethostname().into_string() {
Ok(hostname) => Some(hostname),
Err(_) => None,
}
}
fn style() -> Style {
Color::Green.bold().dimmed()
}
fn hostname_not_tested() -> io::Result<()> {
println!(
"hostname was not tested because gethostname failed! \
This could be caused by your hostname containing invalid UTF."
);
Ok(())
}

View File

@ -1,66 +0,0 @@
use ansi_term::Color;
use std::io;
use crate::common::{self, TestCommand};
#[test]
fn config_blank_job_0() -> io::Result<()> {
let output = common::render_module("jobs").arg("--jobs=0").output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = "";
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_blank_job_1() -> io::Result<()> {
let output = common::render_module("jobs").arg("--jobs=1").output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Blue.bold().paint(""));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_blank_job_2() -> io::Result<()> {
let output = common::render_module("jobs").arg("--jobs=2").output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Blue.bold().paint("✦2"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_2_job_2() -> io::Result<()> {
let output = common::render_module("jobs")
.use_config(toml::toml! {
[jobs]
threshold = 2
})
.arg("--jobs=2")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Blue.bold().paint(""));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_2_job_3() -> io::Result<()> {
let output = common::render_module("jobs")
.use_config(toml::toml! {
[jobs]
threshold = 2
})
.arg("--jobs=3")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("{} ", Color::Blue.bold().paint("✦3"));
assert_eq!(expected, actual);
Ok(())
}

View File

@ -1,25 +0,0 @@
mod aws;
mod character;
mod cmd_duration;
mod common;
mod conda;
mod configuration;
mod directory;
mod dotnet;
mod env_var;
mod gcloud;
mod git_branch;
mod git_commit;
mod git_state;
mod git_status;
mod hg_branch;
mod hostname;
mod jobs;
mod modules;
mod nix_shell;
mod python;
mod shlvl;
mod singularity;
mod terraform;
mod time;
mod username;

View File

@ -1,31 +0,0 @@
use std::io;
use crate::common;
#[test]
fn unknown_module_name() -> io::Result<()> {
let unknown_module_name = "some_random_name";
let output = common::render_module(unknown_module_name).output()?;
let actual_stdout = String::from_utf8(output.stdout).unwrap();
let actual_stderr = String::from_utf8(output.stderr).unwrap();
let expected_stdout = "";
let expected_stderr = format!(
"Error: Unknown module {}. Use starship module --list to list out all supported modules.\n",
unknown_module_name
);
assert_eq!(expected_stdout, actual_stdout);
assert_eq!(expected_stderr, actual_stderr);
Ok(())
}
#[test]
fn known_module_name() -> io::Result<()> {
let output = common::render_module("line_break").output()?;
let actual_stdout = String::from_utf8(output.stdout).unwrap();
let actual_stderr = String::from_utf8(output.stderr).unwrap();
let expected_stdout = "\n";
let expected_stderr = "";
assert_eq!(expected_stdout, actual_stdout);
assert_eq!(expected_stderr, actual_stderr);
Ok(())
}

View File

@ -1,72 +0,0 @@
use ansi_term::Color;
use std::io;
use crate::common;
#[test]
fn no_env_variables() -> io::Result<()> {
let output = common::render_module("nix_shell").output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!("", actual);
Ok(())
}
#[test]
fn invalid_env_variables() -> io::Result<()> {
let output = common::render_module("nix_shell")
.env("IN_NIX_SHELL", "something_wrong")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!("", actual);
Ok(())
}
#[test]
fn pure_shell() -> io::Result<()> {
let output = common::render_module("nix_shell")
.env("IN_NIX_SHELL", "pure")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("via {} ", Color::Blue.bold().paint("❄️ pure"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn impure_shell() -> io::Result<()> {
let output = common::render_module("nix_shell")
.env("IN_NIX_SHELL", "impure")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("via {} ", Color::Blue.bold().paint("❄️ impure"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn pure_shell_name() -> io::Result<()> {
let output = common::render_module("nix_shell")
.env("IN_NIX_SHELL", "pure")
.env("name", "starship")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("via {} ", Color::Blue.bold().paint("❄️ pure (starship)"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn impure_shell_name() -> io::Result<()> {
let output = common::render_module("nix_shell")
.env("IN_NIX_SHELL", "impure")
.env("name", "starship")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("via {} ", Color::Blue.bold().paint("❄️ impure (starship)"));
assert_eq!(expected, actual);
Ok(())
}

View File

@ -1,37 +0,0 @@
use std::fs::File;
use std::io;
use crate::common;
// TODO - These tests should be moved into the python module when we have sorted out mocking of env
// vars.
#[test]
fn with_virtual_env() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.py"))?.sync_all()?;
let output = common::render_module("python")
.env("VIRTUAL_ENV", "/foo/bar/my_venv")
.arg("--path")
.arg(dir.path())
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert!(actual.contains("my_venv"));
dir.close()
}
#[test]
fn with_active_venv() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let output = common::render_module("python")
.env("VIRTUAL_ENV", "/foo/bar/my_venv")
.arg("--path")
.arg(dir.path())
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert!(actual.contains("my_venv"));
dir.close()
}

View File

@ -1,159 +0,0 @@
use ansi_term::{Color, Style};
use std::io;
use crate::common;
use crate::common::TestCommand;
const SHLVL_ENV_VAR: &str = "SHLVL";
fn style() -> Style {
// default style
Color::Yellow.bold()
}
#[test]
fn empty_config() -> io::Result<()> {
let output = common::render_module("shlvl")
.env_clear()
.use_config(toml::toml! {
[shlvl]
})
.env(SHLVL_ENV_VAR, "2")
.output()?;
let expected = "";
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn enabled() -> io::Result<()> {
let output = common::render_module("shlvl")
.env_clear()
.use_config(toml::toml! {
[shlvl]
disabled = false
})
.env(SHLVL_ENV_VAR, "2")
.output()?;
let expected = format!("{} ", style().paint("↕️ 2"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn no_level() -> io::Result<()> {
let output = common::render_module("shlvl")
.env_clear()
.use_config(toml::toml! {
[shlvl]
disabled = false
})
.output()?;
let expected = "";
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn enabled_config_level_1() -> io::Result<()> {
let output = common::render_module("shlvl")
.env_clear()
.use_config(toml::toml! {
[shlvl]
disabled = false
})
.env(SHLVL_ENV_VAR, "1")
.output()?;
let expected = "";
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn lower_threshold() -> io::Result<()> {
let output = common::render_module("shlvl")
.env_clear()
.use_config(toml::toml! {
[shlvl]
threshold = 1
disabled = false
})
.env(SHLVL_ENV_VAR, "1")
.output()?;
let expected = format!("{} ", style().paint("↕️ 1"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn higher_threshold() -> io::Result<()> {
let output = common::render_module("shlvl")
.env_clear()
.use_config(toml::toml! {
[shlvl]
threshold = 3
disabled = false
})
.env(SHLVL_ENV_VAR, "1")
.output()?;
let expected = "";
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn custom_style() -> io::Result<()> {
let output = common::render_module("shlvl")
.env_clear()
.use_config(toml::toml! {
[shlvl]
style = "Red Underline"
disabled = false
})
.env(SHLVL_ENV_VAR, "2")
.output()?;
let expected = format!("{} ", Color::Red.underline().paint("↕️ 2"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn custom_symbol() -> io::Result<()> {
let output = common::render_module("shlvl")
.env_clear()
.use_config(toml::toml! {
[shlvl]
symbol = "shlvl is "
disabled = false
})
.env(SHLVL_ENV_VAR, "2")
.output()?;
let expected = format!("{} ", style().paint("shlvl is 2"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn formatting() -> io::Result<()> {
let output = common::render_module("shlvl")
.env_clear()
.use_config(toml::toml! {
[shlvl]
format = "$symbol going down [$shlvl]($style) GOING UP "
disabled = false
})
.env(SHLVL_ENV_VAR, "2")
.output()?;
let expected = format!("↕️ going down {} GOING UP ", style().paint("2"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}

View File

@ -1,27 +0,0 @@
use ansi_term::Color;
use std::io;
use crate::common;
#[test]
fn no_env_set() -> io::Result<()> {
let output = common::render_module("singularity").output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = "";
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn env_set() -> io::Result<()> {
let output = common::render_module("singularity")
.env_clear()
.env("SINGULARITY_NAME", "centos.img")
.output()?;
let expected = format!("{} ", Color::Blue.bold().dimmed().paint("[centos.img]"));
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!(expected, actual);
Ok(())
}

View File

@ -1,118 +0,0 @@
use ansi_term::Color;
use std::fs::{self, File};
use std::io::{self, Write};
use crate::common;
#[test]
fn folder_without_dotterraform() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let output = common::render_module("terraform")
.arg("--path")
.arg(dir.path())
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = "";
assert_eq!(expected, actual);
dir.close()
}
#[test]
#[ignore]
fn folder_with_tf_file() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.tf"))?;
let output = common::render_module("terraform")
.arg("--path")
.arg(dir.path())
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("via {} ", Color::Fixed(105).bold().paint("💠 default"));
assert_eq!(expected, actual);
dir.close()
}
#[test]
#[ignore]
fn folder_with_workspace_override() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.tf"))?;
let output = common::render_module("terraform")
.arg("--path")
.arg(dir.path())
.env("TF_WORKSPACE", "development")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("via {} ", Color::Fixed(105).bold().paint("💠 development"));
assert_eq!(expected, actual);
dir.close()
}
#[test]
#[ignore]
fn folder_with_datadir_override() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("main.tf"))?;
let datadir = tempfile::tempdir()?;
let mut file = File::create(datadir.path().join("environment"))?;
file.write_all(b"development")?;
file.sync_all()?;
let output = common::render_module("terraform")
.arg("--path")
.arg(dir.path())
.env("TF_DATA_DIR", datadir.path())
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("via {} ", Color::Fixed(105).bold().paint("💠 development"));
assert_eq!(expected, actual);
dir.close()?;
datadir.close()
}
#[test]
#[ignore]
fn folder_with_dotterraform_no_environment() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let tf_dir = dir.path().join(".terraform");
fs::create_dir(&tf_dir)?;
let output = common::render_module("terraform")
.arg("--path")
.arg(dir.path())
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("via {} ", Color::Fixed(105).bold().paint("💠 default"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn folder_with_dotterraform_with_environment() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let tf_dir = dir.path().join(".terraform");
fs::create_dir(&tf_dir)?;
let mut file = File::create(tf_dir.join("environment"))?;
file.write_all(b"development")?;
file.sync_all()?;
let output = common::render_module("terraform")
.arg("--path")
.arg(dir.path())
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("via {} ", Color::Fixed(105).bold().paint("💠 development"));
assert_eq!(expected, actual);
Ok(())
}

View File

@ -1,59 +0,0 @@
use std::io;
use crate::common::{self, TestCommand};
/* Note: tests in this crate cannot rely on the actual time displayed by
the module, since that is dependent on the time inside the test environment,
which we cannot control.
However, we *can* test certain things here, such as the fact that the module
should not display when disabled, should display *something* when enabled,
and should have the correct prefixes and suffixes in a given config */
#[test]
fn config_enabled() -> io::Result<()> {
let output = common::render_module("time")
.use_config(toml::toml! {
[time]
disabled = false
})
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
// We can't test what it actually is...but we can assert it's not blank
assert!(!actual.is_empty());
Ok(())
}
#[test]
fn config_blank() -> io::Result<()> {
let output = common::render_module("time").output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = "";
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn config_check_prefix_and_suffix() -> io::Result<()> {
let output = common::render_module("time")
.use_config(toml::toml! {
[time]
disabled = false
format = "at [\\[$time\\]]($style) "
time_format = "%T"
})
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
// This is the prefix with "at ", the color code, then the prefix char [
let col_prefix = format!("at {}{}[", '\u{1b}', "[1;33m");
// This is the suffix with suffix char ']', then color codes, then a space
let col_suffix = format!("]{}{} ", '\u{1b}', "[0m");
assert!(actual.starts_with(&col_prefix));
assert!(actual.ends_with(&col_suffix));
Ok(())
}

View File

@ -1,79 +0,0 @@
use ansi_term::Color;
use std::io;
use crate::common::{self, TestCommand};
// TODO: Add tests for if root user (UID == 0)
// Requires mocking
#[test]
fn no_env_variables() -> io::Result<()> {
let output = common::render_module("username").output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!("", actual);
Ok(())
}
#[test]
fn logname_equals_user() -> io::Result<()> {
let output = common::render_module("username")
.env("LOGNAME", "astronaut")
.env("USER", "astronaut")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!("", actual);
Ok(())
}
#[test]
fn ssh_wo_username() -> io::Result<()> {
// SSH connection w/o username
let output = common::render_module("username")
.env("SSH_CONNECTION", "192.168.223.17 36673 192.168.223.229 22")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
assert_eq!("", actual);
Ok(())
}
#[test]
fn current_user_not_logname() -> io::Result<()> {
let output = common::render_module("username")
.env("LOGNAME", "astronaut")
.env("USER", "cosmonaut")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("via {} ", Color::Yellow.bold().paint("cosmonaut"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn ssh_connection() -> io::Result<()> {
let output = common::render_module("username")
.env("USER", "astronaut")
.env("SSH_CONNECTION", "192.168.223.17 36673 192.168.223.229 22")
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("via {} ", Color::Yellow.bold().paint("astronaut"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn show_always() -> io::Result<()> {
let output = common::render_module("username")
.env("USER", "astronaut")
.use_config(toml::toml! {
[username]
show_always = true})
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("via {} ", Color::Yellow.bold().paint("astronaut"));
assert_eq!(expected, actual);
Ok(())
}