rusty-pv 0.1.0

Pipe viewer — a Rust port of Andrew Wood's `pv(1)` with progress bar, ETA, rate display, token-bucket rate limiting, IEC/SI unit math, SIGWINCH-aware terminal redraw, SIGUSR1 size refresh, multi-instance cursor coordination, and a typed library API.
Documentation
//! US5 (Numeric mode) integration tests.

mod common;

#[test]
fn numeric_mode_emits_integer_percentages_to_stderr() {
    // Force display (since stderr in tests is not a TTY) and use a known
    // total size so percentages are computable.
    let payload = vec![0u8; 100 * 1024];
    let assert = common::rusty_pv_cmd()
        .arg("-n")
        .arg("-f")
        .arg("-i")
        .arg("0.05")
        .arg("-s")
        .arg("100K")
        .arg("-L")
        .arg("400K")
        .write_stdin(payload.clone())
        .assert()
        .success();

    let stderr = String::from_utf8_lossy(&assert.get_output().stderr).into_owned();
    assert!(!stderr.is_empty(), "numeric mode should emit something");
    // Every line is an integer in [0,100].
    let lines: Vec<&str> = stderr.lines().filter(|l| !l.is_empty()).collect();
    for line in &lines {
        let n: u64 = line
            .parse()
            .unwrap_or_else(|_| panic!("non-integer line: {line:?}"));
        assert!(n <= 100, "percentage > 100: {n}");
    }
    // Sequence is monotonic non-decreasing (FR-008).
    let nums: Vec<u64> = lines.iter().map(|l| l.parse().unwrap()).collect();
    for w in nums.windows(2) {
        assert!(
            w[1] >= w[0],
            "numeric sequence must be monotonic non-decreasing; got {nums:?}"
        );
    }
    // The final value should be 100 (transfer completed).
    assert_eq!(*nums.last().unwrap(), 100, "final tick must be 100");
    // Only ONE 100 line per Clarifications Q9.
    let hundreds = nums.iter().filter(|&&n| n == 100).count();
    assert_eq!(
        hundreds, 1,
        "numeric mode must emit `100` exactly once; got {hundreds} from {nums:?}"
    );
}

#[test]
fn numeric_mode_with_quiet_emits_nothing() {
    // -q wins over -n per Edge Cases.
    let assert = common::rusty_pv_cmd()
        .arg("-n")
        .arg("-q")
        .arg("-f")
        .arg("-s")
        .arg("100")
        .write_stdin("x".repeat(100))
        .assert()
        .success();
    assert!(
        assert.get_output().stderr.is_empty(),
        "numeric + quiet should emit nothing"
    );
}

#[test]
fn numeric_mode_unknown_size_emits_zeros() {
    // FR-008: When total is unknown, emit 0 per tick (upstream convention).
    let assert = common::rusty_pv_cmd()
        .arg("-n")
        .arg("-f")
        .arg("-i")
        .arg("0.1")
        .arg("-L")
        .arg("10K")
        .write_stdin(vec![0u8; 5 * 1024])
        .assert()
        .success();
    let stderr = String::from_utf8_lossy(&assert.get_output().stderr).into_owned();
    let lines: Vec<&str> = stderr.lines().filter(|l| !l.is_empty()).collect();
    // All lines should be `0` since no -s was given.
    for line in &lines {
        assert_eq!(
            line.trim(),
            "0",
            "unknown-size numeric should emit `0`; got: {line:?}"
        );
    }
}