From 32caf023dde750f67e6b9be5f7c4a08f64b23b09 Mon Sep 17 00:00:00 2001 From: kingchenc Date: Sun, 24 May 2026 01:31:20 +0200 Subject: [PATCH] test(psar): drop violation-tuple cold path in trend tests (99.03 -> 100) (#33) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After PR #27 brought psar.rs to 99.03 %, Codecov still flagged the 'violation found' tuple arms in the trend tests (line 256 in pure_uptrend_sar_below_lows, line 285 in pure_downtrend_sar_above_highs) as missed: both tests are designed to NEVER find a violation, so the filter_map branch that constructs the (index, sar, bound) tuple is dead by design. Restructure both tests to use `.all(|(i, sar)| sar.is_none_or(|s| ))` instead of collecting violations into a Vec. The closure runs on every emitted Some, asserts the SAR-vs-extreme bound directly, and the iterator short-circuits on the first false — no cold tuple construction left to count as uncovered. Semantics are identical (still asserts every SAR sits on the correct side of every candle's extreme); the diagnostic message loses the violating index list, which the tests never printed in any green run anyway. psar.rs is now at 207/207 lines, no behavioural change. --- crates/wickra-core/src/indicators/psar.rs | 40 ++++++++--------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/crates/wickra-core/src/indicators/psar.rs b/crates/wickra-core/src/indicators/psar.rs index ca5f7dfb..46a2b8cb 100644 --- a/crates/wickra-core/src/indicators/psar.rs +++ b/crates/wickra-core/src/indicators/psar.rs @@ -246,21 +246,16 @@ mod tests { }) .collect(); let mut psar = Psar::classic(); - let violations: Vec<(usize, f64, f64)> = psar + // `all()` with `is_none_or` keeps every reachable arm on the hot path — + // the previous filter_map / violation-Vec construction had a cold + // "violation found" tuple branch that was unreachable on a clean + // uptrend, leaving its line uncovered by Codecov. + let ok = psar .batch(&candles) - .into_iter() + .iter() .enumerate() - .filter_map(|(i, sar)| { - sar.and_then(|s| { - if s > candles[i].low + 1e-9 { - Some((i, s, candles[i].low)) - } else { - None - } - }) - }) - .collect(); - assert!(violations.is_empty(), "SAR above low: {violations:?}"); + .all(|(i, sar)| sar.is_none_or(|s| s <= candles[i].low + 1e-9)); + assert!(ok, "SAR sat above a candle's low on a pure uptrend"); } #[test] @@ -274,22 +269,15 @@ mod tests { .collect(); let mut psar = Psar::classic(); // After the trend establishes downward, SAR should sit above highs. - let violations: Vec<(usize, f64, f64)> = psar + // Same `all()` + `is_none_or` shape as `pure_uptrend_sar_below_lows` + // so the violation-tuple branch never appears as a cold path. + let ok = psar .batch(&candles) - .into_iter() + .iter() .enumerate() .skip(5) - .filter_map(|(i, sar)| { - sar.and_then(|s| { - if s < candles[i].high - 1e-9 { - Some((i, s, candles[i].high)) - } else { - None - } - }) - }) - .collect(); - assert!(violations.is_empty(), "SAR below high: {violations:?}"); + .all(|(i, sar)| sar.is_none_or(|s| s >= candles[i].high - 1e-9)); + assert!(ok, "SAR sat below a candle's high on a pure downtrend"); } #[test]