use proptest::prelude::*;
use rch_common::classify_command;
proptest! {
#[test]
fn p1_determinism_same_input_same_output(
cmd in "\\PC{0,4096}",
) {
let first = classify_command(&cmd);
let second = classify_command(&cmd);
prop_assert_eq!(
&first,
&second,
"classify_command must be deterministic for input: {:?}",
cmd
);
}
}
proptest! {
#[test]
fn p2_trim_invariance(
body in "[a-zA-Z0-9._\\- /=]{0,200}",
prefix_ws in "[ \t\r\n]{0,8}",
suffix_ws in "[ \t\r\n]{0,8}",
) {
let trimmed_input = body.clone();
let padded_input = format!("{prefix_ws}{body}{suffix_ws}");
let trimmed_result = classify_command(&trimmed_input);
let padded_result = classify_command(&padded_input);
prop_assert_eq!(
trimmed_result.is_compilation,
padded_result.is_compilation,
"leading/trailing whitespace must not change is_compilation: trimmed={:?} padded={:?}",
trimmed_input,
padded_input
);
prop_assert_eq!(
trimmed_result.kind,
padded_result.kind,
"leading/trailing whitespace must not change CompilationKind: trimmed={:?} padded={:?}",
trimmed_input,
padded_input
);
}
}
proptest! {
#![proptest_config(ProptestConfig {
cases: std::env::var("PROPTEST_CASES")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(4096),
..ProptestConfig::default()
})]
#[test]
fn p5_no_panic_for_arbitrary_utf8(
cmd in r"\P{Cn}{0,4096}",
) {
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
classify_command(&cmd)
}));
let cmd_len = cmd.len();
prop_assert!(
result.is_ok(),
"classify_command panicked on input ({} bytes): {:?}",
cmd_len,
cmd
);
}
}
const NORMALLY_INTERCEPTED_PREFIXES: &[&str] = &[
"cargo build",
"cargo test",
"cargo check",
"cargo clippy",
"cargo run",
"bun test",
"bun typecheck",
"gcc -o out main.c",
"g++ -o out main.cpp",
"clang -o out main.c",
"make",
];
proptest! {
#[test]
fn p6_top_level_pipe_disables_interception(
prefix_idx in 0usize..NORMALLY_INTERCEPTED_PREFIXES.len(),
middle in "[a-zA-Z0-9._\\-=/ ]{0,40}",
metachar in r"[|>&]",
suffix in "[a-zA-Z0-9._/\\-]{0,40}",
) {
let prefix = NORMALLY_INTERCEPTED_PREFIXES[prefix_idx];
let baseline = format!("{prefix} {middle}");
let baseline_class = classify_command(baseline.trim());
prop_assume!(baseline_class.is_compilation);
let piped = format!("{prefix} {middle} {metachar} {suffix}");
let piped_class = classify_command(&piped);
prop_assert!(
!piped_class.is_compilation,
"unquoted top-level `{metachar}` must disable interception, \
but {piped:?} classified as compilation ({piped_class:?})"
);
}
}
proptest! {
#[test]
fn p6b_trailing_ampersand_disables_interception(
prefix_idx in 0usize..NORMALLY_INTERCEPTED_PREFIXES.len(),
middle in "[a-zA-Z0-9._\\-=/ ]{0,40}",
) {
let prefix = NORMALLY_INTERCEPTED_PREFIXES[prefix_idx];
let baseline = format!("{prefix} {middle}");
let baseline_class = classify_command(baseline.trim());
prop_assume!(baseline_class.is_compilation);
let backgrounded = format!("{prefix} {middle} &");
let bg_class = classify_command(&backgrounded);
prop_assert!(
!bg_class.is_compilation,
"trailing top-level `&` must disable interception, \
but {backgrounded:?} classified as compilation ({bg_class:?})"
);
}
}
#[test]
fn known_inputs_classify_as_documented() {
for cmd in [
"cargo build",
"cargo test",
"cargo build --release",
"cargo check --workspace --all-targets",
"bun test",
] {
let c = classify_command(cmd);
assert!(c.is_compilation, "documented compilation missed: {cmd:?}");
}
for cmd in [
"bun install",
"bun run dev",
"bun test --watch",
"cargo build | tee log",
"cargo test > out.log",
"cargo run &",
"echo hi",
] {
let c = classify_command(cmd);
assert!(
!c.is_compilation,
"documented non-intercept got intercepted: {cmd:?}"
);
}
}