use super::*;
fn level(gamma: f32) -> NoiseLevel {
NoiseLevel::from_signal_variance(gamma).unwrap()
}
#[test]
fn interval_rejects_out_of_range_and_degenerate_bounds() {
for (lo, hi) in [
(-0.1, 0.5),
(0.2, 1.1),
(f32::NAN, 0.5),
(0.2, f32::INFINITY),
] {
assert!(matches!(
GuidanceInterval::new(lo, hi),
Err(Error::Validation(_))
));
}
for (lo, hi) in [(0.6, 0.6), (0.8, 0.2)] {
assert!(matches!(
GuidanceInterval::new(lo, hi),
Err(Error::Validation(_))
));
}
let band = GuidanceInterval::new(0.2, 0.8).unwrap();
assert_eq!((band.lo(), band.hi()), (0.2, 0.8));
}
#[test]
fn default_is_constant() {
assert_eq!(GuidanceSchedule::default(), GuidanceSchedule::Constant);
}
#[test]
fn constant_returns_the_base_scale_unchanged() {
for gamma in [0.0, 0.3, 0.5, 1.0] {
for w in [1.0, 2.5, 0.4] {
assert_eq!(GuidanceSchedule::Constant.guidance_at(level(gamma), w), w);
}
}
}
#[test]
fn interval_keeps_full_guidance_inside_and_passes_through_outside() {
let schedule = GuidanceSchedule::Interval(GuidanceInterval::new(0.25, 0.75).unwrap());
let w = 3.0;
for gamma in [0.25, 0.5, 0.75] {
assert_eq!(schedule.guidance_at(level(gamma), w), w);
}
for gamma in [0.0, 0.1, 0.9, 1.0] {
assert_eq!(schedule.guidance_at(level(gamma), w), 1.0);
}
}
#[test]
fn monotone_ramps_from_neutral_at_high_noise_to_base_at_low_noise() {
let schedule = GuidanceSchedule::Monotone;
let w = 5.0;
assert_eq!(schedule.guidance_at(level(0.0), w), 1.0);
assert_eq!(schedule.guidance_at(level(1.0), w), w);
assert!((schedule.guidance_at(level(0.5), w) - (1.0 + (w - 1.0) * 0.5)).abs() < 1e-6);
assert!(schedule.guidance_at(level(0.2), w) <= schedule.guidance_at(level(0.6), w));
}
#[test]
fn every_schedule_is_inert_at_neutral_base_guidance() {
let band = GuidanceSchedule::Interval(GuidanceInterval::new(0.1, 0.9).unwrap());
for gamma in [0.0, 0.4, 1.0] {
assert_eq!(
GuidanceSchedule::Constant.guidance_at(level(gamma), 1.0),
1.0
);
assert_eq!(
GuidanceSchedule::Monotone.guidance_at(level(gamma), 1.0),
1.0
);
assert_eq!(band.guidance_at(level(gamma), 1.0), 1.0);
}
}
#[test]
fn serde_round_trips_each_variant() {
for schedule in [
GuidanceSchedule::Constant,
GuidanceSchedule::Monotone,
GuidanceSchedule::Interval(GuidanceInterval::new(0.2, 0.8).unwrap()),
] {
let json = serde_json::to_string(&schedule).unwrap();
assert_eq!(
serde_json::from_str::<GuidanceSchedule>(&json).unwrap(),
schedule
);
}
assert_eq!(
serde_json::to_string(&GuidanceSchedule::Constant).unwrap(),
r#"{"type":"constant"}"#
);
let parsed: GuidanceSchedule =
serde_json::from_str(r#"{"type":"interval","band":{"lo":0.2,"hi":0.8}}"#).unwrap();
assert_eq!(
parsed,
GuidanceSchedule::Interval(GuidanceInterval::new(0.2, 0.8).unwrap())
);
}
#[test]
fn deserialization_validates_band_and_fails_closed() {
assert!(
serde_json::from_str::<GuidanceSchedule>(
r#"{"type":"interval","band":{"lo":0.9,"hi":0.2}}"#
)
.is_err()
);
assert!(
serde_json::from_str::<GuidanceSchedule>(
r#"{"type":"interval","band":{"lo":0.2,"hi":0.8,"mid":0.5}}"#
)
.is_err()
);
assert!(serde_json::from_str::<GuidanceSchedule>(r#"{"type":"constant","mid":0.5}"#).is_err());
assert!(serde_json::from_str::<GuidanceSchedule>(r#"{"type":"cosine"}"#).is_err());
}