#![allow(dead_code)]
pub use worktrunk::testing::mock_commands;
pub use worktrunk::testing::*;
pub mod list_snapshots;
#[cfg(unix)]
pub mod progressive_output;
#[cfg(feature = "shell-integration-tests")]
pub mod pty;
#[cfg(feature = "shell-integration-tests")]
pub mod shell;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use tempfile::TempDir;
use worktrunk::path::to_posix_path;
#[cfg(unix)]
pub fn ignore_tty_signals() {
use std::cell::Cell;
thread_local! {
static TTY_SIGNALS_BLOCKED: Cell<bool> = const { Cell::new(false) };
}
TTY_SIGNALS_BLOCKED.with(|blocked| {
if blocked.get() {
return;
}
use nix::sys::signal::{SigSet, SigmaskHow, Signal, pthread_sigmask};
let mut mask = SigSet::empty();
mask.add(Signal::SIGTTIN);
mask.add(Signal::SIGTTOU);
pthread_sigmask(SigmaskHow::SIG_BLOCK, Some(&mask), None)
.expect("failed to block SIGTTIN/SIGTTOU signals");
blocked.set(true);
});
}
#[cfg(unix)]
#[rstest::fixture]
pub fn pty_safe() {
ignore_tty_signals();
}
#[rstest::fixture]
pub fn repo() -> TestRepo {
let repo = TestRepo::standard();
let guard =
setup_snapshot_settings_for_paths(repo.root_path(), &repo.worktrees).bind_to_scope();
std::mem::forget(guard);
repo
}
#[rstest::fixture]
pub fn temp_home() -> TempDir {
TempDir::new().unwrap()
}
#[rstest::fixture]
pub fn repo_with_remote(mut repo: TestRepo) -> TestRepo {
repo.setup_remote("main");
repo
}
#[rstest::fixture]
pub fn repo_with_main_worktree(repo: TestRepo) -> TestRepo {
repo
}
#[rstest::fixture]
pub fn repo_with_feature_worktree(mut repo_with_main_worktree: TestRepo) -> TestRepo {
repo_with_main_worktree.add_worktree_with_commit(
"feature",
"feature.txt",
"feature content",
"Add feature file",
);
repo_with_main_worktree
}
#[rstest::fixture]
pub fn repo_with_remote_and_feature(mut repo_with_remote: TestRepo) -> TestRepo {
repo_with_remote.add_worktree_with_commit(
"feature",
"feature.txt",
"feature content",
"Add feature file",
);
repo_with_remote
}
#[rstest::fixture]
pub fn repo_with_alternate_primary(repo: TestRepo) -> TestRepo {
repo.switch_primary_to("develop");
repo.add_main_worktree();
repo
}
#[rstest::fixture]
pub fn repo_with_multi_commit_feature(mut repo_with_main_worktree: TestRepo) -> TestRepo {
let feature_wt = repo_with_main_worktree.add_worktree("feature");
repo_with_main_worktree.commit_in_worktree(
&feature_wt,
"file1.txt",
"content 1",
"feat: add file 1",
);
repo_with_main_worktree.commit_in_worktree(
&feature_wt,
"file2.txt",
"content 2",
"feat: add file 2",
);
repo_with_main_worktree
}
#[rstest::fixture]
pub fn merge_scenario(mut repo: TestRepo) -> (TestRepo, PathBuf) {
let feature_wt = repo.add_worktree("feature");
std::fs::write(feature_wt.join("feature.txt"), "feature content").unwrap();
repo.run_git_in(&feature_wt, &["add", "feature.txt"]);
repo.run_git_in(&feature_wt, &["commit", "-m", "Add feature file"]);
(repo, feature_wt)
}
#[rstest::fixture]
pub fn merge_scenario_multi_commit(mut repo: TestRepo) -> (TestRepo, PathBuf) {
let feature_wt = repo.add_worktree("feature");
repo.commit_in_worktree(&feature_wt, "file1.txt", "content 1", "feat: add file 1");
repo.commit_in_worktree(&feature_wt, "file2.txt", "content 2", "feat: add file 2");
(repo, feature_wt)
}
pub fn native_pty_system() -> Box<dyn portable_pty::PtySystem> {
#[cfg(unix)]
ignore_tty_signals();
portable_pty::native_pty_system()
}
pub fn open_pty() -> portable_pty::PtyPair {
open_pty_with_size(48, 200)
}
pub fn open_pty_with_size(rows: u16, cols: u16) -> portable_pty::PtyPair {
native_pty_system()
.openpty(portable_pty::PtySize {
rows,
cols,
pixel_width: 0,
pixel_height: 0,
})
.unwrap()
}
pub fn configure_pty_command(cmd: &mut portable_pty::CommandBuilder) {
cmd.env_clear();
let home_dir = home::home_dir().unwrap().to_string_lossy().to_string();
cmd.env("HOME", &home_dir);
cmd.env(
"PATH",
std::env::var("PATH").unwrap_or_else(|_| "/usr/bin:/bin".to_string()),
);
#[cfg(windows)]
{
cmd.env("USERPROFILE", &home_dir);
if let Ok(val) = std::env::var("SystemRoot") {
cmd.env("SystemRoot", &val);
cmd.env("windir", &val); }
if let Ok(val) = std::env::var("SystemDrive") {
cmd.env("SystemDrive", val);
}
if let Ok(val) = std::env::var("TEMP") {
cmd.env("TEMP", &val);
cmd.env("TMP", val);
}
if let Ok(val) = std::env::var("COMSPEC") {
cmd.env("COMSPEC", val);
}
if let Ok(val) = std::env::var("PSModulePath") {
cmd.env("PSModulePath", val);
}
}
pass_coverage_env_to_pty_cmd(cmd);
}
pub fn pass_coverage_env_to_pty_cmd(cmd: &mut portable_pty::CommandBuilder) {
for key in [
"LLVM_PROFILE_FILE",
"CARGO_LLVM_COV",
"CARGO_LLVM_COV_TARGET_DIR",
] {
if let Ok(val) = std::env::var(key) {
cmd.env(key, val);
}
}
}
#[cfg(unix)]
pub fn shell_command(
shell: &str,
bin_dir: Option<&std::path::Path>,
) -> portable_pty::CommandBuilder {
let mut cmd = portable_pty::CommandBuilder::new(shell);
cmd.env_clear();
cmd.env(
"HOME",
home::home_dir().unwrap().to_string_lossy().to_string(),
);
let path = match bin_dir {
Some(dir) => format!(
"{}:{}",
dir.display(),
std::env::var("PATH").unwrap_or_else(|_| "/usr/bin:/bin".to_string())
),
None => std::env::var("PATH").unwrap_or_else(|_| "/usr/bin:/bin".to_string()),
};
cmd.env("PATH", path);
match shell {
"zsh" => {
cmd.env("ZDOTDIR", "/dev/null");
cmd.arg("--no-rcs");
cmd.arg("-o");
cmd.arg("NO_GLOBAL_RCS");
cmd.arg("-o");
cmd.arg("NO_RCS");
}
"bash" => {
cmd.arg("--norc");
cmd.arg("--noprofile");
}
"fish" => {
cmd.arg("--no-config");
}
_ => {}
}
pass_coverage_env_to_pty_cmd(&mut cmd);
cmd
}
pub fn add_standard_env_redactions(settings: &mut insta::Settings) {
settings.add_redaction(".env.GIT_CONFIG_GLOBAL", "[TEST_GIT_CONFIG]");
settings.add_redaction(".env.WORKTRUNK_CONFIG_PATH", "[TEST_CONFIG]");
settings.add_redaction(".env.WORKTRUNK_SYSTEM_CONFIG_PATH", "[TEST_SYSTEM_CONFIG]");
settings.add_redaction(
".env.WORKTRUNK_PROJECT_CONFIG_PATH",
"[TEST_PROJECT_CONFIG]",
);
settings.add_redaction(".env.WORKTRUNK_APPROVALS_PATH", "[TEST_APPROVALS]");
settings.add_redaction(".env.WORKTRUNK_DIRECTIVE_CD_FILE", "[DIRECTIVE_CD_FILE]");
settings.add_redaction(
".env.WORKTRUNK_DIRECTIVE_EXEC_FILE",
"[DIRECTIVE_EXEC_FILE]",
);
settings.add_redaction(".env.WORKTRUNK_DIRECTIVE_FILE", "[DIRECTIVE_FILE]");
settings.add_redaction(".env.HOME", "[TEST_HOME]");
settings.add_redaction(".env.USERPROFILE", "[TEST_HOME]");
settings.add_redaction(".env.XDG_CONFIG_HOME", "[TEST_CONFIG_HOME]");
settings.add_redaction(".env.APPDATA", "[TEST_CONFIG_HOME]");
settings.add_redaction(".env.PATH", "[PATH]");
settings.add_redaction(".env.PWD", "[PWD]");
settings.add_redaction(".env.MOCK_CONFIG_DIR", "[MOCK_CONFIG_DIR]");
settings.add_redaction(".env.OPENCODE_CONFIG_DIR", "[TEST_OPENCODE_CONFIG]");
}
fn canonical_home_dir() -> Option<PathBuf> {
home::home_dir().and_then(|path| canonicalize(&path).ok())
}
fn add_snapshot_path_prelude_filters(settings: &mut insta::Settings) {
let project_root = std::env::var("CARGO_MANIFEST_DIR")
.ok()
.and_then(|path| canonicalize(std::path::Path::new(&path)).ok());
if let Some(root) = project_root {
let root_str = root.to_str().unwrap();
settings.add_filter(®ex::escape(root_str), "[PROJECT_ROOT]");
let root_str_normalized = root_str.replace('\\', "/");
if root_str_normalized != root_str {
settings.add_filter(®ex::escape(&root_str_normalized), "[PROJECT_ROOT]");
}
}
settings.add_filter(r"/target/llvm-cov-target/", "/target/");
}
fn add_repo_and_worktree_path_filters(
settings: &mut insta::Settings,
root: &Path,
worktrees: &HashMap<String, PathBuf>,
) {
let root_canonical = canonicalize(root).unwrap_or_else(|_| root.to_path_buf());
let root_str = root_canonical.to_str().unwrap();
let root_str_normalized = root_str.replace('\\', "/");
settings.add_filter(®ex::escape(root_str), "_REPO_");
settings.add_filter(®ex::escape(&root_str_normalized), "_REPO_");
settings.add_filter(®ex::escape(&to_posix_path(root_str)), "_REPO_");
settings.add_filter(r"~/repo(\.[a-zA-Z0-9_-]+)?", "_REPO_$1");
let home_dir = canonical_home_dir();
if let Some(home) = home_dir.as_ref()
&& let Ok(relative) = root_canonical.strip_prefix(home)
{
let tilde_path = format!("~/{}", relative.display()).replace('\\', "/");
settings.add_filter(®ex::escape(&tilde_path), "_REPO_");
let tilde_worktree_pattern = format!(r"{}(\.[a-zA-Z0-9_-]+)", regex::escape(&tilde_path));
settings.add_filter(&tilde_worktree_pattern, "_REPO_$1");
}
for (name, path) in worktrees {
let canonical = canonicalize(path).unwrap_or_else(|_| path.clone());
let path_str = canonical.to_str().unwrap();
let replacement = format!("_WORKTREE_{}_", name.to_uppercase().replace('-', "_"));
let path_str_normalized = path_str.replace('\\', "/");
settings.add_filter(®ex::escape(path_str), &replacement);
settings.add_filter(®ex::escape(&path_str_normalized), &replacement);
settings.add_filter(®ex::escape(&to_posix_path(path_str)), &replacement);
if let Some(home) = home_dir.as_ref()
&& let Ok(relative) = canonical.strip_prefix(home)
{
let tilde_path = format!("~/{}", relative.display()).replace('\\', "/");
settings.add_filter(®ex::escape(&tilde_path), &replacement);
}
}
settings.add_filter(r"~/AppData/Local/Temp/\.tmp[^/]+/repo", "_REPO_");
settings.add_filter(
r"/[a-z]/Users/[^/]+/AppData/Local/Temp/\.tmp[^/]+/repo(\.[a-zA-Z0-9_/-]+)?",
"_REPO_$1",
);
}
fn add_placeholder_cleanup_filters(settings: &mut insta::Settings) {
settings.add_filter(
r"'(?:\x1b\[[0-9;]*m)*(_(?:REPO|WORKTREE_[A-Z0-9_]+)_(?:\.[a-zA-Z0-9_.-]+)?(?:/[^']*)?)(?:\x1b\[[0-9;]*m)*'",
"$1",
);
settings.add_filter(
r"'(?:\x1b\[[0-9;]*m)*(\[[A-Z_]+\])(?:\x1b\[[0-9;]*m)*'",
"$1",
);
settings.add_filter(
r"'(_(?:REPO|WORKTREE_[A-Z0-9_]+)_(?:\.[a-zA-Z0-9_-]+)?/[^']+)'",
"$1",
);
settings.add_filter(r"(diff --git )a/(_(?:REPO|WORKTREE_[A-Z0-9_]+)_)", "$1a$2");
settings.add_filter(r" b/(_(?:REPO|WORKTREE_[A-Z0-9_]+)_)", " b$1");
settings.add_filter(r"(--- )a/(_(?:REPO|WORKTREE_[A-Z0-9_]+)_)", "$1a$2");
settings.add_filter(r"(\+\+\+ )b/(_(?:REPO|WORKTREE_[A-Z0-9_]+)_)", "$1b$2");
settings.add_filter(
r"(\x1b\[1m)(_(?:REPO|WORKTREE_[A-Z0-9_]+)_/[^\s]+) b(_(?:REPO|WORKTREE_[A-Z0-9_]+)_/[^\s]+)",
"$1diff --git a$2 b$3",
);
settings.add_filter(
r"(\x1b\[0m) +--git a(_(?:REPO|WORKTREE_[A-Z0-9_]+)_/)",
"$1 \x1b[1mdiff --git a$2",
);
settings.add_filter(r"(--- )(_(?:REPO|WORKTREE_[A-Z0-9_]+)_/)", "$1a$2");
settings.add_filter(r"(\+\+\+ )(_(?:REPO|WORKTREE_[A-Z0-9_]+)_/)", "$1b$2");
settings.add_filter(
r"(\x1b\[1m)(_(?:REPO|WORKTREE_[A-Z0-9_]+)_/[^\x1b]+\.toml)(\x1b\[m)",
"$1--- a$2$3",
);
settings.add_filter(
r"\x1b\[2m \x1b\[0m\x1b\[2m(?:\x1b\[32m)?(_(?:REPO|WORKTREE_[A-Z0-9_]+)_(?:\.[a-zA-Z0-9_-]+)?)(?:\x1b\[0m)?\x1b\[2m \x1b\[0m",
"\x1b[2m $1 \x1b[0m",
);
settings.add_filter(
r"(?:\x1b\[\d+m)*\x1b\[32m(_(?:REPO|WORKTREE_[A-Z0-9_]+)_(?:/[^\x1b\s]+)?)(?:\x1b\[\d+m)*",
"$1",
);
}
fn add_temp_path_placeholder_filters(settings: &mut insta::Settings) {
settings.add_filter(
r"'?(?:[A-Z]:)?[/\\][^\s']+[/\\]\.tmp[^/\\']+[/\\]test-config\.toml\.new'?",
"[TEST_CONFIG_NEW]",
);
settings.add_filter(
r"'?(?:[A-Z]:)?[/\\][^\s']+[/\\]\.tmp[^/\\']+[/\\]test-config\.toml'?",
"[TEST_CONFIG]",
);
settings.add_filter(
r"'?(?:[A-Z]:)?[/\\][^\s']+[/\\]\.tmp[^/\\']+[/\\]test-approvals\.toml'?",
"[TEST_APPROVALS]",
);
settings.add_filter(
r"(?:\x1b\[\d+m)+(\[TEST_(?:CONFIG(?:_NEW)?|APPROVALS)\])(?:\x1b\[\d+m)+",
"$1",
);
settings.add_filter(
r"(?:[A-Z]:)?/[^\s]+/\.tmp[^/]+/test-gitconfig",
"[TEST_GIT_CONFIG]",
);
}
fn add_temp_home_filters(settings: &mut insta::Settings, temp_home: &Path) {
let temp_home_original = temp_home.to_string_lossy().replace('\\', "/");
let temp_home_canonical = canonicalize(temp_home).unwrap_or_else(|_| temp_home.to_path_buf());
let temp_home_str = temp_home_canonical.to_string_lossy().replace('\\', "/");
if temp_home_str.contains(':') {
settings.add_filter(
&format!("'{}", regex::escape(&temp_home_str)),
"'[TEMP_HOME]",
);
if temp_home_original != temp_home_str {
settings.add_filter(
&format!("'{}", regex::escape(&temp_home_original)),
"'[TEMP_HOME]",
);
}
}
settings.add_filter(®ex::escape(&temp_home_str), "[TEMP_HOME]");
if temp_home_original != temp_home_str {
settings.add_filter(®ex::escape(&temp_home_original), "[TEMP_HOME]");
}
if temp_home_str.starts_with("/private/") {
let without_private = &temp_home_str["/private".len()..];
settings.add_filter(®ex::escape(without_private), "[TEMP_HOME]");
}
settings.add_filter(r"( )(?:\x1b\[[0-9;]*m)+('?)(\[TEMP_HOME\]/)", "$1$2$3");
settings.add_filter(r"(\[TEMP_HOME\]/[^\x1b\s]+)(?:\x1b\[[0-9;]*m)+", "$1");
settings.add_filter(r"'\[TEMP_HOME\](/[^']+)'", "[TEMP_HOME]$1");
settings.add_filter(r"(diff --git )a/(\[TEMP_HOME\])", "$1a$2");
settings.add_filter(r" b/(\[TEMP_HOME\])", " b$1");
settings.add_filter(r"(--- )a/(\[TEMP_HOME\])", "$1a$2");
settings.add_filter(r"(\+\+\+ )b/(\[TEMP_HOME\])", "$1b$2");
settings.add_filter(
r"(diff --git )(\[TEMP_HOME\]/[^\s]+) (\[TEMP_HOME\]/)",
"$1a$2 b$3",
);
settings.add_filter(
r"(\x1b\[1m)(\[TEMP_HOME\]/[^\s]+) b(\[TEMP_HOME\]/[^\s]+)",
"$1diff --git a$2 b$3",
);
settings.add_filter(
r"(\x1b\[0m) +--git a(\[TEMP_HOME\]/)",
"$1 \x1b[1mdiff --git a$2",
);
settings.add_filter(r"(--- )a/(\[TEMP_HOME\]/)", "$1a$2");
settings.add_filter(r"(--- )(\[TEMP_HOME\]/)", "$1a$2");
settings.add_filter(r"(\+\+\+ )b/(\[TEMP_HOME\]/)", "$1b$2");
settings.add_filter(r"(\+\+\+ )(\[TEMP_HOME\]/)", "$1b$2");
settings.add_filter(
r"(\x1b\[1m)(\[TEMP_HOME\]/[^\s\x1b]+\.toml)(\x1b\[m|\n|$)",
"$1--- a$2$3",
);
}
fn add_os_temp_dir_filter(settings: &mut insta::Settings) {
let temp_dir = std::env::temp_dir();
let temp_dir_str = temp_dir.to_string_lossy().replace('\\', "/");
let temp_dir_str = temp_dir_str.trim_end_matches('/');
let canonical = canonicalize(&temp_dir).unwrap_or_else(|_| temp_dir.clone());
let canonical_str = canonical.to_string_lossy().replace('\\', "/");
let canonical_str = canonical_str.trim_end_matches('/');
settings.add_filter(
&format!(
r"'?{}/\.tmp[^/']+/[^)'\s\x1b]+'?",
regex::escape(canonical_str)
),
"[PROJECT_ID]",
);
if canonical_str != temp_dir_str {
settings.add_filter(
&format!(
r"'?{}/\.tmp[^/']+/[^)'\s\x1b]+'?",
regex::escape(temp_dir_str)
),
"[PROJECT_ID]",
);
}
}
fn add_project_id_filters(settings: &mut insta::Settings) {
settings.add_filter(
r"/private/var/folders/[^/]+/[^/]+/T/\.[^/]+/[^)'\s\x1b]+",
"[PROJECT_ID]",
);
settings.add_filter(
r"/var/folders/[^/]+/[^/]+/T/\.[^/]+/[^)'\s\x1b]+",
"[PROJECT_ID]",
);
settings.add_filter(
r"/private/tmp/(?:[^/]+/)*\.tmp[^/]+/[^)'\s\x1b]+",
"[PROJECT_ID]",
);
settings.add_filter(r"/tmp/(?:[^/]+/)*\.tmp[^/]+/[^)'\s\x1b]+", "[PROJECT_ID]");
settings.add_filter(
r"[A-Z]:/Users/[^/]+/AppData/Local/Temp/\.tmp[^/]+/[^)'\s\x1b]+",
"[PROJECT_ID]",
);
settings.add_filter(
r"'[A-Z]:/Users/[^/]+/AppData/Local/Temp/\.tmp[^/]+/[^']+'",
"[PROJECT_ID]",
);
settings.add_filter(r"~/([a-zA-Z0-9_-]+)", "_PARENT_/$1");
settings.add_filter(r"'\[PROJECT_ID\]'", "[PROJECT_ID]");
settings.add_filter(r"HOME: .*/\.tmp[^/\s]+", "HOME: [TEST_HOME]");
}
pub fn setup_snapshot_settings(repo: &TestRepo) -> insta::Settings {
setup_snapshot_settings_impl(repo.root_path(), None)
}
fn setup_snapshot_settings_impl(root: &Path, temp_home: Option<&Path>) -> insta::Settings {
let worktrees = HashMap::new(); setup_snapshot_settings_for_paths_with_home(root, &worktrees, temp_home)
}
pub fn setup_snapshot_settings_for_paths(
root: &Path,
worktrees: &HashMap<String, PathBuf>,
) -> insta::Settings {
setup_snapshot_settings_for_paths_with_home(root, worktrees, None)
}
fn setup_snapshot_settings_for_paths_with_home(
root: &Path,
worktrees: &HashMap<String, PathBuf>,
temp_home: Option<&Path>,
) -> insta::Settings {
let mut settings = if temp_home.is_some() {
insta::Settings::new()
} else {
insta::Settings::clone_current()
};
settings.set_snapshot_path("../snapshots");
add_snapshot_path_prelude_filters(&mut settings);
add_repo_and_worktree_path_filters(&mut settings, root, worktrees);
add_placeholder_cleanup_filters(&mut settings);
add_temp_path_placeholder_filters(&mut settings);
if let Some(temp_home) = temp_home {
add_temp_home_filters(&mut settings, temp_home);
}
add_os_temp_dir_filter(&mut settings);
add_project_id_filters(&mut settings);
add_standard_env_redactions(&mut settings);
settings.add_filter(
r"post-start-[^-]+-[0-9a-f]{7,40}-\d{6}\.log",
"post-start-[NAME]-[TIMESTAMP].log",
);
settings.add_filter(r"(?m)^\x1b\[107m \x1b\[0m {1,2}hint:.*\n", "");
settings.add_filter(
r"(Could not apply [0-9a-f]{7,40}\.\.\.) ([A-Za-z])",
"$1 # $2",
);
settings.add_filter(r"Broken pipe \(os error 32\)", "Error: connection refused");
settings.add_filter(
r"(?:/usr/bin/bash: line \d+|sh(?:: line \d+)?|bash)(?:: \d+)?: ([^:]+): (?:command )?not found",
"sh: $1: command not found",
);
settings.add_filter(r"(?m)^.*[Pp]owershell(?:\x1b\[[0-9;]*m)*:.*\n", ""); settings.add_filter(r"(?m)^.*No .*powershell.* shell extension.*\n", ""); settings.add_filter(r"(?m)^.*shell init powershell.*\n", ""); settings.add_filter(r"(?m)^.*for powershell .*\n", "");
settings.add_filter(r"wt\.exe", "wt");
settings.add_filter(
r"(wt: \x1b\[1m)(?:v[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9]+-g[0-9a-f]+)?(?:-dirty)?|[0-9a-f]{7,40}(?:-dirty)?)",
"${1}[VERSION]",
);
settings.add_filter(
r"(git: \x1b\[1m)[0-9]+\.[0-9]+\.[0-9]+[^\x1b]*",
"${1}[VERSION]",
);
settings.add_filter(
r"(current: |Up to date \(\x1b\[1m)(?:v?[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9]+-g[0-9a-f]+)?(?:-dirty)?|[0-9a-f]{7,40}(?:-dirty)?)",
"${1}[VERSION]",
);
settings.add_filter(
r"(Binary invoked as: \x1b\[1m)[^\x1b]+/target/(debug|release)/wt(\x1b\[22m)",
"${1}[PROJECT_ROOT]/target/$2/wt$3",
);
settings.add_filter(
r"(is binary at \x1b\[1m)[^\x1b]+(/wt|/wt\.exe)(\x1b\[22m)",
"${1}[BINARY_PATH]$2$3",
);
settings.add_filter(r"\x1b\[0m$", "");
settings.add_filter(r"\x1b\[0m\n", "\n");
settings.add_filter(
r"\x1b\[2m \x1b\[0m\x1b\[2m\x1b\[32m(_REPO_[^\x1b]*)\x1b\[0m\x1b\[2m \x1b\[0m\x1b\[2m",
"\x1b[2m $1 \x1b[0m\x1b[2m",
);
settings.add_filter(
r"(Squashed|Committed) @ (?:\x1b\[2m)?[a-f0-9]{7}(?:\x1b\[22m)?",
"$1 @ [HASH]",
);
settings.add_filter(r"@ \x1b\[2m[a-f0-9]{7}\x1b\[22m", "@ \x1b[2m[HASH]\x1b[22m");
settings.add_filter(r"\* \x1b\[33m[a-f0-9]{7}\x1b\[m", "* \x1b[33m[HASH]\x1b[m");
settings.add_filter(r#" CARGO_LLVM_COV: "1"\n"#, "");
settings.add_filter(r#" CARGO_LLVM_COV_TARGET_DIR: "[^"]+"\n"#, "");
settings.add_filter(r#" LLVM_PROFILE_FILE: "[^"]+"\n"#, "");
settings
}
pub fn setup_snapshot_settings_with_home(repo: &TestRepo, temp_home: &TempDir) -> insta::Settings {
setup_snapshot_settings_impl(repo.root_path(), Some(temp_home.path()))
}
pub fn setup_home_snapshot_settings(temp_home: &TempDir) -> insta::Settings {
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../snapshots");
let canonical_home =
canonicalize(temp_home.path()).unwrap_or_else(|_| temp_home.path().to_path_buf());
settings.add_filter(
®ex::escape(&canonical_home.to_string_lossy()),
"[TEMP_HOME]",
);
settings.add_filter(r"\\", "/");
settings.add_filter(r"(?m)^.*[Pp]owershell(?:\x1b\[[0-9;]*m)*:.*\n", "");
settings.add_filter(r"(?m)^.*No .*powershell.* shell extension.*\n", "");
settings.add_filter(r"(?m)^.*shell init powershell.*\n", "");
settings.add_filter(r"(?m)^.*for powershell .*\n", "");
settings.add_filter(r"wt\.exe", "wt");
settings.add_filter(
r"fatal: not a git repository \(or any[^\n]*(?:\n[^\n]*filesystem boundary[^\n]*)?",
"fatal: not a git repository [GIT_DISCOVERY_MSG]",
);
settings.add_filter(r"thread '([^']+)' \(\d+\)", "thread '$1'");
add_standard_env_redactions(&mut settings);
settings
}
pub fn setup_temp_snapshot_settings(temp_path: &std::path::Path) -> insta::Settings {
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../snapshots");
if let Ok(canonical) = dunce::canonicalize(temp_path) {
let canonical_str = canonical.to_str().unwrap();
let temp_str = temp_path.to_str().unwrap();
if canonical_str != temp_str {
settings.add_filter(®ex::escape(canonical_str), "[TEMP]");
}
}
settings.add_filter(®ex::escape(temp_path.to_str().unwrap()), "[TEMP]");
if let Some(dir_name) = temp_path.file_name().and_then(|n| n.to_str()) {
let pattern = format!(r"'?[^\s]*{}", regex::escape(dir_name));
settings.add_filter(&pattern, "[TEMP]");
}
settings.add_filter(r"\\", "/");
settings.add_filter(r"(\[TEMP\]/[^\s]*)'", "$1");
settings.add_filter(r"wt\.exe", "wt");
add_standard_env_redactions(&mut settings);
settings
}
pub fn add_pty_filters(settings: &mut insta::Settings) {
settings.add_filter(r"\^D\x08+", "");
settings.add_filter(r"(?m)^\x1b\[0m", "");
}
pub fn add_pty_binary_path_filters(settings: &mut insta::Settings) {
settings.add_filter(
r"[^\s]+/target/(?:llvm-cov-target/)?(?:debug|release)/wt",
"[BIN]",
);
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
#[rstest]
fn test_commit_with_age(repo: TestRepo) {
repo.commit_with_age("One hour ago", HOUR);
repo.commit_with_age("One day ago", DAY);
repo.commit_with_age("One week ago", WEEK);
repo.commit_with_age("Ten minutes ago", 10 * MINUTE);
let output = repo.git_command().args(["log", "--oneline"]).run().unwrap();
let log = String::from_utf8_lossy(&output.stdout);
assert_eq!(log.lines().count(), 5);
}
}