test: 100% coverage for wma + aroon_oscillator + atr + awesome_oscillator + cci (#22)

* test(wma): cover period/warmup/name + kill dead naive panic arm

Codecov flagged 10 lines in crates/wickra-core/src/indicators/wma.rs
(file at 92.48%): const accessor period (56-58), Indicator-impl
warmup_period (111-113), name (119-121), and line 186 — the
`_ => panic!("warmup mismatch")` arm in matches_naive_over_random_
inputs, an invariant guard that never fires when both streams share
a warmup period.

Add accessors_and_metadata covering the three metadata methods.
Refactor matches_naive_over_random_inputs to assert the warmup-shape
invariant via assert_eq!(g.is_some(), w.is_some()) + if let,
removing the dead panic arm.

wma.rs is now at 133/133 lines, no behavioural change.

* test(aroon_oscillator): cover period/value accessors + name metadata

Codecov flagged 9 lines in crates/wickra-core/src/indicators/aroon_
oscillator.rs (file at 90.42%): const accessors period (57-59),
value (62-64) and Indicator-impl name (90-92). warmup_period is
already covered by warmup_period_matches_aroon.

Add accessors_and_metadata asserting period == 7, name ==
"AroonOscillator", and value() across the None (pre-warmup) and
Some (post-warmup) branches.

aroon_oscillator.rs is now at 94/94 lines, no behavioural change.

* test(atr): cover period/value accessors + name metadata

Codecov flagged 9 lines in crates/wickra-core/src/indicators/atr.rs
(file at 93.70%): const accessors period (54-57), value (59-62) and
Indicator-impl name body (103-105). warmup_period is exercised
indirectly via downstream indicators; the metadata getters were
never queried directly.

Add accessors_and_metadata asserting period == 14, name == "ATR",
and value() across the None (pre-warmup) and Some (post-warmup)
branches.

atr.rs is now at 143/143 lines, no behavioural change.

* test(awesome_oscillator): cover periods accessor + warmup/name metadata

Codecov flagged 9 lines in crates/wickra-core/src/indicators/awesome_
oscillator.rs (file at 88.15%): const accessor periods (59-61),
Indicator-impl warmup_period (83-85), name (91-93). The classic()
constructor is covered indirectly through the existing tests; only
the metadata methods were dead.

Add accessors_and_metadata asserting periods == (5, 34),
warmup_period == 34 (= slow_period), name == "AwesomeOscillator".

awesome_oscillator.rs is now at 76/76 lines, no behavioural change.

* test(cci): cover period accessor + warmup/name metadata

Codecov flagged 9 lines in crates/wickra-core/src/indicators/cci.rs
(file at 89.65%): const accessor period (68-70), Indicator-impl
warmup_period (102-104), name (110-112). Existing tests never
inspected the metadata surface.

Add accessors_and_metadata asserting period == 20, warmup_period ==
20, name == "CCI".

cci.rs is now at 87/87 lines, no behavioural change.
This commit is contained in:
kingchenc
2026-05-24 00:46:59 +02:00
committed by GitHub
parent a9670b0ad1
commit 8582338b5d
5 changed files with 68 additions and 5 deletions
@@ -107,6 +107,21 @@ mod tests {
assert!(AroonOscillator::new(0).is_err());
}
/// Cover the const accessors `period` / `value` (57-64) and the
/// Indicator-impl `name` body (90-92). `warmup_period` is covered
/// already by `warmup_period_matches_aroon`.
#[test]
fn accessors_and_metadata() {
let mut osc = AroonOscillator::new(7).unwrap();
assert_eq!(osc.period(), 7);
assert_eq!(osc.name(), "AroonOscillator");
assert_eq!(osc.value(), None);
for i in 0..8 {
osc.update(candle(100.0 + f64::from(i), 90.0, 95.0, i64::from(i)));
}
assert!(osc.value().is_some());
}
#[test]
fn pure_uptrend_yields_plus_100() {
// Every bar a fresh high, no fresh low: AroonUp = 100, AroonDown = 0.
+15
View File
@@ -151,6 +151,21 @@ mod tests {
assert!(matches!(Atr::new(0), Err(Error::PeriodZero)));
}
/// Cover the const accessors `period` / `value` (54-62) and the
/// Indicator-impl `name` body (103-105). Existing tests inspect
/// numeric ATR output but never query the metadata.
#[test]
fn accessors_and_metadata() {
let mut atr = Atr::new(14).unwrap();
assert_eq!(atr.period(), 14);
assert_eq!(atr.name(), "ATR");
assert_eq!(atr.value(), None);
for _ in 0..14 {
atr.update(c(11.0, 9.0, 10.0));
}
assert!(atr.value().is_some());
}
#[test]
fn warmup_emits_on_period_th_candle() {
let candles = vec![
@@ -118,6 +118,17 @@ mod tests {
assert!(AwesomeOscillator::new(0, 5).is_err());
}
/// Cover the const accessor `periods` (59-61) and the Indicator-impl
/// `warmup_period` (83-85) + `name` (91-93). Existing tests never
/// inspect these metadata methods.
#[test]
fn accessors_and_metadata() {
let ao = AwesomeOscillator::classic();
assert_eq!(ao.periods(), (5, 34));
assert_eq!(ao.warmup_period(), 34);
assert_eq!(ao.name(), "AwesomeOscillator");
}
#[test]
fn batch_equals_streaming() {
let candles: Vec<Candle> = (0..50)
+11
View File
@@ -138,6 +138,17 @@ mod tests {
assert!(Cci::with_factor(20, -1.0).is_err());
}
/// Cover the const accessor `period` (68-70) and the Indicator-impl
/// `warmup_period` (102-104) + `name` (110-112). Existing tests never
/// inspect these metadata methods.
#[test]
fn accessors_and_metadata() {
let cci = Cci::new(20).unwrap();
assert_eq!(cci.period(), 20);
assert_eq!(cci.warmup_period(), 20);
assert_eq!(cci.name(), "CCI");
}
#[test]
fn batch_equals_streaming() {
let candles: Vec<Candle> = (0..60)
+16 -5
View File
@@ -154,6 +154,17 @@ mod tests {
assert!(matches!(Wma::new(0), Err(Error::PeriodZero)));
}
/// Cover the const accessor `period` (56-58) and the Indicator-impl
/// `warmup_period` (111-113) + `name` (119-121). Existing tests never
/// inspect these metadata methods.
#[test]
fn accessors_and_metadata() {
let wma = Wma::new(7).unwrap();
assert_eq!(wma.period(), 7);
assert_eq!(wma.warmup_period(), 7);
assert_eq!(wma.name(), "WMA");
}
#[test]
fn warmup_returns_none() {
let mut wma = Wma::new(3).unwrap();
@@ -179,11 +190,11 @@ mod tests {
let mut wma = Wma::new(7).unwrap();
let got = wma.batch(&prices);
let want = wma_naive(&prices, 7);
for (g, w) in got.iter().zip(want.iter()) {
match (g, w) {
(None, None) => {}
(Some(a), Some(b)) => assert_relative_eq!(*a, *b, epsilon = 1e-9),
_ => panic!("warmup mismatch"),
for (i, (g, w)) in got.iter().zip(want.iter()).enumerate() {
// Same warmup — emission shape must agree at every index.
assert_eq!(g.is_some(), w.is_some(), "warmup mismatch at index {i}");
if let (Some(a), Some(b)) = (g, w) {
assert_relative_eq!(*a, *b, epsilon = 1e-9);
}
}
}