use criterion::{Criterion, criterion_group, criterion_main};
use std::path::{Path, PathBuf};
use std::process::Command;
use wt_perf::{RepoConfig, isolate_cmd, run_git, run_git_ok, setup_fake_remote};
fn create_bench_repo(base_path: &Path, with_hooks: bool) -> PathBuf {
let config = RepoConfig::typical(2); wt_perf::create_repo_at(&config, base_path);
setup_fake_remote(base_path);
if with_hooks {
let config_dir = base_path.join(".config");
std::fs::create_dir_all(&config_dir).unwrap();
std::fs::write(
config_dir.join("wt.toml"),
"[post-remove]\ndocs = \"echo post-remove-done\"\n",
)
.unwrap();
run_git(base_path, &["add", "."]);
run_git(base_path, &["commit", "-m", "Add project config"]);
}
base_path.to_path_buf()
}
fn recreate_worktree(repo_path: &Path) {
let wt_path = repo_path.parent().unwrap().join(format!(
"{}.feature-wt-1",
repo_path.file_name().unwrap().to_str().unwrap()
));
std::thread::sleep(std::time::Duration::from_millis(1200));
let _ = std::fs::remove_dir_all(&wt_path);
let trash_dir = repo_path.join(".git/wt/trash");
if trash_dir.exists() {
let _ = std::fs::remove_dir_all(&trash_dir);
}
let _ = run_git_ok(repo_path, &["worktree", "prune"]);
let _ = run_git_ok(repo_path, &["branch", "-D", "feature-wt-1"]);
run_git(
repo_path,
&[
"worktree",
"add",
"-b",
"feature-wt-1",
wt_path.to_str().unwrap(),
"HEAD",
],
);
}
fn bench_remove_e2e(c: &mut Criterion) {
let mut group = c.benchmark_group("remove_e2e");
let binary = Path::new(env!("CARGO_BIN_EXE_wt"));
let temp_no_hooks = tempfile::tempdir().unwrap();
let temp_with_hooks = tempfile::tempdir().unwrap();
let repo_no_hooks = create_bench_repo(&temp_no_hooks.path().join("repo"), false);
let repo_with_hooks = create_bench_repo(&temp_with_hooks.path().join("repo"), true);
let user_config_no_hooks = temp_no_hooks.path().join("config.toml");
std::fs::write(&user_config_no_hooks, "").unwrap();
let user_config_with_hooks = temp_with_hooks.path().join("config.toml");
std::fs::write(
&user_config_with_hooks,
"[hooks.post-switch]\nzellij-tab = \"echo post-switch-done\"\n",
)
.unwrap();
let wt_name = |repo: &Path| -> PathBuf {
repo.parent().unwrap().join(format!(
"{}.feature-wt-1",
repo.file_name().unwrap().to_str().unwrap()
))
};
group.bench_function("first_output", |b| {
b.iter(|| {
let mut cmd = Command::new(binary);
cmd.args(["remove", "--yes", "--no-hooks", "--force", "feature-wt-1"]);
cmd.current_dir(&repo_no_hooks);
isolate_cmd(&mut cmd, Some(&user_config_no_hooks));
cmd.env("WORKTRUNK_FIRST_OUTPUT", "1");
let output = cmd.output().unwrap();
assert!(
output.status.success(),
"first_output failed: {}",
String::from_utf8_lossy(&output.stderr)
);
});
});
group.bench_function("no_hooks", |b| {
b.iter_batched(
|| recreate_worktree(&repo_no_hooks),
|()| {
let wt_path = wt_name(&repo_no_hooks);
let mut cmd = Command::new(binary);
cmd.args(["remove", "--yes", "--no-hooks", "--force"]);
cmd.current_dir(&wt_path);
isolate_cmd(&mut cmd, Some(&user_config_no_hooks));
let output = cmd.output().unwrap();
assert!(
output.status.success(),
"no_hooks failed: {}",
String::from_utf8_lossy(&output.stderr)
);
},
criterion::BatchSize::PerIteration,
);
});
group.bench_function("with_hooks", |b| {
b.iter_batched(
|| recreate_worktree(&repo_with_hooks),
|()| {
let wt_path = wt_name(&repo_with_hooks);
let mut cmd = Command::new(binary);
cmd.args(["remove", "--yes", "--force"]);
cmd.current_dir(&wt_path);
isolate_cmd(&mut cmd, Some(&user_config_with_hooks));
let output = cmd.output().unwrap();
assert!(
output.status.success(),
"with_hooks failed: {}",
String::from_utf8_lossy(&output.stderr)
);
},
criterion::BatchSize::PerIteration,
);
});
group.finish();
}
criterion_group! {
name = benches;
config = Criterion::default()
.sample_size(20)
.measurement_time(std::time::Duration::from_secs(20))
.warm_up_time(std::time::Duration::from_secs(3));
targets = bench_remove_e2e
}
criterion_main!(benches);