use common::binary;
use std::fs;
use std::process::Command;
use std::sync::atomic::{AtomicU64, Ordering};
mod common;
static TEST_COUNTER: AtomicU64 = AtomicU64::new(0);
#[test]
fn all_excludes_reorder_rule() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "fn callee() {}\nfn caller() { callee(); }\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(
&cfg,
"exclude:\n - paths: [\"lib.rs\"]\n rules: [\"reorder\"]\n",
)
.unwrap();
let output = Command::new(binary())
.args(["--config", cfg.to_str().unwrap()])
.arg(&tmp)
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
output.status.success(),
"pipeline should succeed: {}",
String::from_utf8_lossy(&output.stderr)
);
let actual = fs::read_to_string(&tmp).unwrap();
assert!(
actual.find("fn callee()").unwrap() < actual.find("fn caller()").unwrap(),
"reorder disabled: non-canonical callee-before-caller must be preserved"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn auto_discovery_walks_up_to_git_root() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
fs::write(dir.join(".git"), "gitdir: placeholder\n").unwrap();
fs::create_dir_all(dir.join("src")).unwrap();
fs::write(dir.join("src").join("lib.rs"), "pub fn example() {}\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(&cfg, "exclude_files:\n - \"src/lib.rs\"\n").unwrap();
let sub = dir.join("src");
let output = Command::new(binary())
.current_dir(&sub)
.arg("--validate")
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
output.status.success(),
"--validate should discover the config by walking up to .git: {}",
String::from_utf8_lossy(&output.stderr)
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn check_excludes_doc001_rule() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn load() -> Result<(), String> { Ok(()) }\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(
&cfg,
"exclude:\n - paths: [\"lib.rs\"]\n rules: [\"DOC001\"]\n",
)
.unwrap();
let output = Command::new(binary())
.args(["--config", cfg.to_str().unwrap()])
.arg(&tmp)
.output()
.expect("failed to spawn rust-llm-tidy");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains("DOC001"),
"DOC001 must be suppressed by exclude: {stderr:?}"
);
assert!(
stderr.contains("DOC002"),
"non-disabled DOC002 must still appear: {stderr:?}"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn config_and_no_config_are_mutually_exclusive() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(&cfg, "exclude_files: []\n").unwrap();
let output = Command::new(binary())
.args([
"--config",
cfg.to_str().unwrap(),
"--no-config",
"--validate",
])
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
!output.status.success(),
"--config and --no-config must be mutually exclusive"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn exclude_flag_additive_skips_lints() {
let dir = temp_dir();
fs::create_dir(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn undocumented() {}\n").unwrap();
let output = Command::new(binary())
.args(["--no-config", "--exclude", "lints"])
.arg(&tmp)
.output()
.expect("failed to spawn");
assert!(
output.status.success(),
"--exclude lints must skip the lint pass: {}",
String::from_utf8_lossy(&output.stderr)
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn exclude_lints_op_suppresses_all_lint_failure() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn load() -> Result<(), String> { Ok(()) }\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(&cfg, "exclude:\n - rules: [\"lints\"]\n").unwrap();
let output = Command::new(binary())
.args(["--config", cfg.to_str().unwrap()])
.arg(&tmp)
.output()
.expect("failed to spawn");
assert!(
output.status.success(),
"lints op disabled -> run must succeed despite doc gaps: {}",
String::from_utf8_lossy(&output.stderr)
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn exclude_lints_overrides_included_lint_code() {
let dir = temp_dir();
fs::create_dir(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn undocumented() {}\n").unwrap();
let output = Command::new(binary())
.args(["--no-config", "--include", "DOC001", "--exclude", "lints"])
.arg(&tmp)
.output()
.expect("failed to spawn");
assert!(
output.status.success(),
"--exclude lints must suppress included DOC001: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(!String::from_utf8_lossy(&output.stderr).contains("DOC001"));
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn excluded_file_not_post_processed() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn example() {}\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(
&cfg,
format!(
"exclude_files:\n - \"lib.rs\"\npost_process:\n - {}\n extensions: [\"rs\"]\n",
post_process_command(1)
),
)
.unwrap();
let output = Command::new(binary())
.args(["--config", cfg.to_str().unwrap(), "--include", "tables"])
.arg(&tmp)
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
output.status.success(),
"excluded file must not be post-processed: {}",
String::from_utf8_lossy(&output.stderr)
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn fix_exclude_skips_file() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let tmp = dir.join("in.md");
let original = "| a | b |\n|---|---|\n| 1 | 2 |\n";
fs::write(&tmp, original).unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(&cfg, "exclude_files:\n - \"in.md\"\n").unwrap();
let output = Command::new(binary())
.args(["--config", cfg.to_str().unwrap()])
.arg(&tmp)
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
output.status.success(),
"default pipeline should succeed even when the file is excluded: {}",
String::from_utf8_lossy(&output.stderr)
);
let actual = fs::read_to_string(&tmp).unwrap();
assert_eq!(actual, original, "excluded file must be unchanged");
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn fix_excludes_links_rule() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let tmp = dir.join("in.md");
fs::write(
&tmp,
"| Name | Value |\n| --- | --- |\n| a | 1 |\n| longname | 200 |\n\nsee [A](http://x) and [A](http://x)\n",
)
.unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(
&cfg,
"exclude:\n - paths: [\"in.md\"]\n rules: [\"links\"]\n",
)
.unwrap();
let output = Command::new(binary())
.args(["--config", cfg.to_str().unwrap(), "--dry-run"])
.arg(&tmp)
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
output.status.success(),
"default pipeline should succeed: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(
output.stdout.is_empty(),
"dry-run must not print reconstructed source to stdout"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains("hoist link"),
"links must NOT be hoisted when `links` is disabled: {stderr:?}"
);
assert!(
stderr.contains("tables were aligned"),
"tables must still be applied when only `links` is disabled: {stderr:?}"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn flags_reject_unknown_op() {
let dir = temp_dir();
fs::create_dir(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn example() {}\n").unwrap();
let output = Command::new(binary())
.args(["--no-config", "--include", "BOGUS"])
.arg(&tmp)
.output()
.expect("failed to spawn");
assert!(!output.status.success(), "--include BOGUS must error");
let output = Command::new(binary())
.args(["--no-config", "--exclude", "BOGUS"])
.arg(&tmp)
.output()
.expect("failed to spawn");
assert!(!output.status.success(), "--exclude BOGUS must error");
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn include_and_exclude_cli_combine_in_whitelist_mode() {
let dir = temp_dir();
fs::create_dir(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub(crate) mod m {\n pub fn f() {}\n}\n").unwrap();
let output = Command::new(binary())
.args([
"--no-config",
"--include",
"vis",
"--include",
"lints",
"--exclude",
"lints",
])
.arg(&tmp)
.output()
.expect("failed to spawn");
assert!(
output.status.success(),
"whitelist {{vis,lints}} - {{lints}} = {{vis}}: vis runs, lints skipped (no DOC001): {}",
String::from_utf8_lossy(&output.stderr)
);
let actual = fs::read_to_string(&tmp).unwrap();
assert!(
actual.contains("pub(crate) fn f"),
"vis must still narrow despite --exclude lints: {actual}"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn include_and_exclude_xor_errors() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
fs::write(dir.join("lib.rs"), "pub fn example() {}\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(
&cfg,
"include:\n - rules: [tables]\nexclude:\n - rules: [reorder]\n",
)
.unwrap();
let output = Command::new(binary())
.args(["--config", cfg.to_str().unwrap(), "--validate"])
.output()
.expect("failed to spawn");
assert!(
!output.status.success(),
"include + exclude must hard-fail at config load: {}",
String::from_utf8_lossy(&output.stderr)
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn include_flag_overrides_config_mode() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub(crate) mod m {\n pub fn f() {}\n}\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(&cfg, "exclude:\n - rules: [vis]\n").unwrap();
let output = Command::new(binary())
.args(["--config", cfg.to_str().unwrap(), "--include", "vis"])
.arg(&tmp)
.output()
.expect("failed to spawn");
assert!(
output.status.success(),
"--include vis must override config and skip lints: {}",
String::from_utf8_lossy(&output.stderr)
);
let actual = fs::read_to_string(&tmp).unwrap();
assert!(
actual.contains("pub(crate) fn f"),
"vis must narrow: {actual}"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn include_lints_exclude_lint_code() {
let dir = temp_dir();
fs::create_dir(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn undocumented() {}\n").unwrap();
let output = Command::new(binary())
.args(["--no-config", "--include", "lints", "--exclude", "DOC001"])
.arg(&tmp)
.output()
.expect("failed to spawn");
assert!(
output.status.success(),
"--exclude DOC001 must suppress DOC001 with --include lints: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(!String::from_utf8_lossy(&output.stderr).contains("DOC001"));
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn include_single_lint_code_runs_only_that_code() {
let dir = temp_dir();
fs::create_dir(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn load() -> Result<(), String> { Ok(()) }\n").unwrap();
let output = Command::new(binary())
.args(["--no-config", "--include", "DOC001"])
.arg(&tmp)
.output()
.expect("failed to spawn");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!output.status.success(),
"--include DOC001 must surface DOC001 diagnostics: {}",
stderr
);
assert!(
stderr.contains("DOC001"),
"--include DOC001 must report DOC001: {stderr:?}"
);
assert!(
!stderr.contains("DOC002"),
"--include DOC001 must not report DOC002: {stderr:?}"
);
let actual = fs::read_to_string(&tmp).unwrap();
assert_eq!(
actual, "pub fn load() -> Result<(), String> { Ok(()) }\n",
"file must be unmutated by the DOC001-only lint pass"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn include_whitelist_runs_nothing_for_unmatched_file() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
fs::create_dir_all(dir.join("other")).unwrap();
fs::write(dir.join("other").join("lib.rs"), "fn matched() {}\n").unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn undocumented() {}\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(
&cfg,
"include:\n - paths: [\"other/**/*.rs\"]\n rules: [vis, lints]\n",
)
.unwrap();
let output = Command::new(binary())
.args(["--config", cfg.to_str().unwrap()])
.arg(&tmp)
.output()
.expect("failed to spawn");
assert!(
output.status.success(),
"unmatched file in whitelist mode must run nothing (no lint failure): {}",
String::from_utf8_lossy(&output.stderr)
);
let actual = fs::read_to_string(&tmp).unwrap();
assert_eq!(
actual, "pub fn undocumented() {}\n",
"file must be unchanged"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn patterns_resolved_relative_to_config_dir() {
let dir = temp_dir();
let sub = dir.join("cfg-dir");
fs::create_dir_all(&sub).unwrap();
let src = dir.join("src");
fs::create_dir_all(&src).unwrap();
fs::write(src.join("lib.rs"), "pub fn example() {}\n").unwrap();
let cfg = sub.join(".rust-llm-tidy.yml");
fs::write(&cfg, "exclude_files:\n - \"../src/lib.rs\"\n").unwrap();
let validate = Command::new(binary())
.args(["--config", cfg.to_str().unwrap(), "--validate"])
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
validate.status.success(),
"--validate should succeed when the pattern matches relative to the config dir: {}",
String::from_utf8_lossy(&validate.stderr)
);
let _ = fs::remove_dir_all(&dir);
}
fn post_process_command(exit_code: u8) -> String {
if cfg!(windows) {
format!("command: \"cmd.exe\"\n args: [\"/C\", \"exit\", \"{exit_code}\"]")
} else {
let command = if exit_code == 0 { "true" } else { "false" };
format!("command: \"{command}\"")
}
}
#[test]
fn post_process_failure_exits_nonzero() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn example() {}\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(
&cfg,
format!(
"post_process:\n - {}\n extensions: [\"rs\"]\n",
post_process_command(1)
),
)
.unwrap();
let output = Command::new(binary())
.args(["--config", cfg.to_str().unwrap(), "--include", "tables"])
.arg(&tmp)
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
!output.status.success(),
"a failing post_process command must exit non-zero"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn post_process_not_run_on_check() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn example() {}\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(
&cfg,
format!(
"post_process:\n - {}\n extensions: [\"rs\"]\n",
post_process_command(1)
),
)
.unwrap();
let output = Command::new(binary())
.args(["--config", cfg.to_str().unwrap(), "--include", "lints"])
.arg(&tmp)
.output()
.expect("failed to spawn rust-llm-tidy");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains("post_process"),
"lints must not invoke post_process: {stderr:?}"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn post_process_runs_on_matching_extension() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn example() {}\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(
&cfg,
format!(
"post_process:\n - {}\n extensions: [\"rs\"]\n",
post_process_command(0)
),
)
.unwrap();
let output = Command::new(binary())
.args(["--config", cfg.to_str().unwrap(), "--include", "tables"])
.arg(&tmp)
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
output.status.success(),
"successful post_process should succeed: {}",
String::from_utf8_lossy(&output.stderr)
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn post_process_skipped_under_dry_run() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn example() {}\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(
&cfg,
format!(
"post_process:\n - {}\n extensions: [\"rs\"]\n",
post_process_command(1)
),
)
.unwrap();
let output = Command::new(binary())
.args([
"--config",
cfg.to_str().unwrap(),
"--include",
"tables",
"--dry-run",
])
.arg(&tmp)
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
output.status.success(),
"--dry-run must skip post_process so `false` never runs"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn post_process_skips_non_matching_extension() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn example() {}\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(
&cfg,
format!(
"post_process:\n - {}\n extensions: [\"md\"]\n",
post_process_command(1)
),
)
.unwrap();
let output = Command::new(binary())
.args(["--config", cfg.to_str().unwrap(), "--include", "tables"])
.arg(&tmp)
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
output.status.success(),
"post_process must NOT run on a .rs file when extensions=[md]: {}",
String::from_utf8_lossy(&output.stderr)
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn regular_command_hard_fails_on_non_matching_path() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let tmp = dir.join("lib.rs");
fs::write(&tmp, "pub fn example() {}\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(&cfg, "exclude_files:\n - \"missing/**\"\n").unwrap();
let output = Command::new(binary())
.args(["--config", cfg.to_str().unwrap()])
.arg(&tmp)
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
!output.status.success(),
"non-matching-path config must hard-fail, not warn"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn validate_fails_on_links_min_occurrences_zero() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(&cfg, "links:\n min_occurrences: 0\n").unwrap();
let output = Command::new(binary())
.arg("--config")
.arg(&cfg)
.arg("--validate")
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
!output.status.success(),
"--validate should fail on min_occurrences: 0"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn validate_fails_on_malformed_yaml() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(&cfg, "exclude_files: [unclosed\n").unwrap();
let output = Command::new(binary())
.arg("--config")
.arg(&cfg)
.arg("--validate")
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
!output.status.success(),
"--validate should fail on bad YAML"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn validate_fails_on_non_matching_path() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(&cfg, "exclude_files:\n - \"does/not/exist/**\"\n").unwrap();
let output = Command::new(binary())
.arg("--config")
.arg(&cfg)
.arg("--validate")
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
!output.status.success(),
"--validate should fail on a non-matching path"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn validate_fails_on_unknown_rule() {
let dir = temp_dir();
fs::create_dir_all(dir.join("src")).unwrap();
fs::write(dir.join("src").join("lib.rs"), "pub fn example() {}\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(
&cfg,
"exclude:\n - paths: [\"src/**\"]\n rules: [\"NOPE\"]\n",
)
.unwrap();
let output = Command::new(binary())
.arg("--config")
.arg(&cfg)
.arg("--validate")
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
!output.status.success(),
"--validate should fail on an unknown rule"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn validate_fails_when_no_config_found() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
let output = Command::new(binary())
.current_dir(&dir)
.arg("--validate")
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
!output.status.success(),
"--validate should fail when no config is found"
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn validate_fails_with_no_config_flag() {
let output = Command::new(binary())
.args(["--no-config", "--validate"])
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
!output.status.success(),
"--validate --no-config must exit non-zero"
);
}
#[test]
fn validate_ok_on_valid_config() {
let dir = temp_dir();
fs::create_dir_all(&dir).unwrap();
fs::create_dir_all(dir.join("src")).unwrap();
fs::write(dir.join("src").join("lib.rs"), "pub fn example() {}\n").unwrap();
let cfg = dir.join(".rust-llm-tidy.yml");
fs::write(&cfg, "exclude_files:\n - \"src/lib.rs\"\n").unwrap();
let output = Command::new(binary())
.arg("--config")
.arg(&cfg)
.arg("--validate")
.output()
.expect("failed to spawn rust-llm-tidy");
assert!(
output.status.success(),
"--validate should exit 0 on a valid config: {}",
String::from_utf8_lossy(&output.stderr)
);
let _ = fs::remove_dir_all(&dir);
}
fn temp_dir() -> std::path::PathBuf {
let seq = TEST_COUNTER.fetch_add(1, Ordering::Relaxed);
let pid = std::process::id();
std::env::temp_dir().join(format!("rust-llm-tidy-cfg-dir-{}-{}", pid, seq))
}