extern crate std;
use display_types::cea861::hdmi_forum::HdmiForumFrl;
use crate::types::{FfeLevels, LtpReq};
use super::sim::{MockPhy, RATE, SimScdc, cfg, flt_ready, frl_started, ltp, not_ready};
use super::*;
#[test]
fn training_config_default_ffe_levels() {
assert_eq!(TrainingConfig::default().ffe_levels, FfeLevels::Ffe0);
}
#[test]
fn training_config_default_dsc_frl_max() {
assert!(!TrainingConfig::default().dsc_frl_max);
}
#[test]
fn training_config_default_timeouts() {
let c = TrainingConfig::default();
assert_eq!(c.flt_ready_timeout, 1000);
assert_eq!(c.frl_start_timeout, 1000);
assert_eq!(c.ltp_timeout, 1000);
}
#[test]
fn training_outcome_success_carries_rate() {
let o = TrainingOutcome::Success {
achieved_rate: HdmiForumFrl::Rate6Gbps4Lanes,
};
assert_eq!(
o,
TrainingOutcome::Success {
achieved_rate: HdmiForumFrl::Rate6Gbps4Lanes
}
);
}
#[test]
fn training_outcome_fallback_required() {
assert_eq!(
TrainingOutcome::FallbackRequired,
TrainingOutcome::FallbackRequired
);
}
#[test]
fn training_outcome_variants_are_distinct() {
assert_ne!(
TrainingOutcome::Success {
achieved_rate: HdmiForumFrl::Rate6Gbps4Lanes
},
TrainingOutcome::FallbackRequired,
);
}
#[test]
fn training_error_scdc_variant() {
let e: TrainingError<u8, u8> = TrainingError::Scdc(42);
assert_eq!(e, TrainingError::Scdc(42));
}
#[test]
fn training_error_phy_variant() {
let e: TrainingError<u8, u8> = TrainingError::Phy(7);
assert_eq!(e, TrainingError::Phy(7));
}
#[test]
fn training_error_variants_are_distinct() {
let s: TrainingError<u8, u8> = TrainingError::Scdc(1);
let p: TrainingError<u8, u8> = TrainingError::Phy(1);
assert_ne!(s, p);
}
#[test]
fn successful_training() {
let mut scdc = SimScdc::new();
scdc.push(not_ready());
scdc.push(not_ready());
scdc.push(flt_ready());
scdc.push(flt_ready());
scdc.push(frl_started());
scdc.push(ltp(LtpReq::Lfsr0));
scdc.push(ltp(LtpReq::Lfsr2));
scdc.push(frl_started());
let outcome = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap();
assert_eq!(
outcome,
TrainingOutcome::Success {
achieved_rate: RATE
}
);
}
#[test]
fn flt_ready_timeout() {
let mut scdc = SimScdc::new();
scdc.push(not_ready());
scdc.push(not_ready());
scdc.push(not_ready());
let outcome = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate(RATE, &cfg(3, 10, 10))
.unwrap();
assert_eq!(outcome, TrainingOutcome::FallbackRequired);
}
#[test]
fn flt_ready_immediate() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready()); scdc.push(frl_started());
scdc.push(frl_started());
let outcome = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap();
assert_eq!(
outcome,
TrainingOutcome::Success {
achieved_rate: RATE
}
);
}
#[test]
fn frl_start_timeout() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready()); scdc.push(flt_ready());
scdc.push(flt_ready());
scdc.push(flt_ready());
let outcome = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate(RATE, &cfg(10, 3, 10))
.unwrap();
assert_eq!(outcome, TrainingOutcome::FallbackRequired);
}
#[test]
fn frl_start_immediate() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready()); scdc.push(frl_started()); scdc.push(frl_started());
let outcome = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap();
assert_eq!(
outcome,
TrainingOutcome::Success {
achieved_rate: RATE
}
);
}
#[test]
fn ltp_loop_timeout() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready());
scdc.push(frl_started());
scdc.push(ltp(LtpReq::Lfsr1));
scdc.push(ltp(LtpReq::Lfsr1));
scdc.push(ltp(LtpReq::Lfsr1));
let outcome = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate(RATE, &cfg(10, 10, 3))
.unwrap();
assert_eq!(outcome, TrainingOutcome::FallbackRequired);
}
#[test]
fn ltp_success_on_first_read() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready());
scdc.push(frl_started());
scdc.push(frl_started());
let outcome = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap();
assert_eq!(
outcome,
TrainingOutcome::Success {
achieved_rate: RATE
}
);
}
#[test]
fn ltp_all_lfsr_variants() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready());
scdc.push(frl_started());
scdc.push(ltp(LtpReq::Lfsr0));
scdc.push(ltp(LtpReq::Lfsr1));
scdc.push(ltp(LtpReq::Lfsr2));
scdc.push(ltp(LtpReq::Lfsr3));
scdc.push(frl_started());
let outcome = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap();
assert_eq!(
outcome,
TrainingOutcome::Success {
achieved_rate: RATE
}
);
}
#[test]
fn error_write_frl_config() {
let mut scdc = SimScdc::new();
scdc.fail_write_frl_config = true;
let err = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap_err();
assert_eq!(err, TrainingError::Scdc(()));
}
#[test]
fn error_set_frl_rate() {
let mut phy = MockPhy::new();
phy.fail_set_frl_rate = true;
let err = FrlTrainer::new(SimScdc::new(), phy)
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap_err();
assert_eq!(err, TrainingError::Phy(()));
}
#[test]
fn error_read_training_status_phase2() {
let mut scdc = SimScdc::new();
scdc.push_err();
let err = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap_err();
assert_eq!(err, TrainingError::Scdc(()));
}
#[test]
fn error_read_training_status_phase3() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready());
scdc.push_err();
let err = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap_err();
assert_eq!(err, TrainingError::Scdc(()));
}
#[test]
fn error_read_training_status_phase4() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready());
scdc.push(frl_started());
scdc.push_err();
let err = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap_err();
assert_eq!(err, TrainingError::Scdc(()));
}
#[test]
fn error_read_ced() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready());
scdc.push(frl_started());
scdc.push(ltp(LtpReq::Lfsr0)); scdc.fail_read_ced = true;
let err = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap_err();
assert_eq!(err, TrainingError::Scdc(()));
}
#[test]
fn error_send_ltp() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready());
scdc.push(frl_started());
scdc.push(ltp(LtpReq::Lfsr0)); let mut phy = MockPhy::new();
phy.fail_send_ltp = true;
let err = FrlTrainer::new(scdc, phy)
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap_err();
assert_eq!(err, TrainingError::Phy(()));
}
#[test]
fn send_ltp_correct_pattern_per_ltp_req() {
let cases: &[(LtpReq, u8)] = &[
(LtpReq::Lfsr0, 1),
(LtpReq::Lfsr1, 2),
(LtpReq::Lfsr2, 3),
(LtpReq::Lfsr3, 4),
];
for &(req, expected_raw) in cases {
let mut scdc = SimScdc::new();
scdc.push(flt_ready());
scdc.push(frl_started());
scdc.push(ltp(req)); scdc.push(frl_started());
let mut trainer = FrlTrainer::new(scdc, MockPhy::new());
trainer
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap();
let (_, phy) = trainer.into_parts();
assert_eq!(
phy.last_ltp.unwrap().value(),
expected_raw,
"LtpReq variant produced wrong LtpPattern raw value"
);
}
}
#[test]
fn into_parts_recovers_scdc_and_phy() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready());
scdc.push(frl_started());
scdc.push(frl_started());
let mut trainer = FrlTrainer::new(scdc, MockPhy::new());
trainer
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap();
let (scdc, phy) = trainer.into_parts();
assert_eq!(scdc.written_config.unwrap().rate, RATE);
assert_eq!(phy.frl_rate, Some(RATE));
}
#[test]
fn sim_scdc_empty_queue_returns_err() {
use crate::scdc::ScdcClient;
let mut scdc = SimScdc::new();
assert_eq!(scdc.read_training_status(), Err(()));
}
#[test]
fn sim_scdc_statuses_consumed_fifo() {
use crate::scdc::ScdcClient;
let mut scdc = SimScdc::new();
scdc.push(not_ready());
scdc.push(flt_ready());
assert_eq!(scdc.read_training_status(), Ok(not_ready()));
assert_eq!(scdc.read_training_status(), Ok(flt_ready()));
assert_eq!(scdc.read_training_status(), Err(())); }
#[test]
fn sim_scdc_ced_calls_counted() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready());
scdc.push(frl_started());
scdc.push(ltp(LtpReq::Lfsr0));
scdc.push(ltp(LtpReq::Lfsr1));
scdc.push(frl_started());
let mut trainer = FrlTrainer::new(scdc, MockPhy::new());
trainer
.train_at_rate(RATE, &TrainingConfig::default())
.unwrap();
let (scdc, _) = trainer.into_parts();
assert_eq!(scdc.ced_calls, 2);
}
#[test]
fn sim_scdc_read_ced_returns_all_none() {
use crate::scdc::ScdcClient;
let mut scdc = SimScdc::new();
let ced = scdc.read_ced().unwrap();
assert!(ced.lane0.is_none());
assert!(ced.lane1.is_none());
assert!(ced.lane2.is_none());
assert!(ced.lane3.is_none());
}
#[test]
fn mock_phy_adjust_equalization_always_succeeds() {
use hdmi_hal::phy::{EqParams, HdmiPhy};
let mut phy = MockPhy::new();
assert!(phy.adjust_equalization(EqParams::default()).is_ok());
}
#[test]
fn mock_phy_set_scrambling_always_succeeds() {
use hdmi_hal::phy::HdmiPhy;
let mut phy = MockPhy::new();
assert!(phy.set_scrambling(true).is_ok());
assert!(phy.set_scrambling(false).is_ok());
}
#[cfg(feature = "alloc")]
mod trace_tests {
extern crate std;
use std::vec::Vec;
use crate::trace::TrainingEvent;
use crate::types::{FfeLevels, LtpReq};
use super::*;
#[test]
fn trace_successful_run() {
let mut scdc = SimScdc::new();
scdc.push(not_ready());
scdc.push(not_ready());
scdc.push(flt_ready());
scdc.push(flt_ready());
scdc.push(frl_started());
scdc.push(ltp(LtpReq::Lfsr0));
scdc.push(ltp(LtpReq::Lfsr2));
scdc.push(frl_started());
let (outcome, trace) = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate_traced(RATE, &TrainingConfig::default())
.unwrap();
assert_eq!(
outcome,
TrainingOutcome::Success {
achieved_rate: RATE
}
);
assert_eq!(trace.rate, RATE);
assert_eq!(
trace.events,
[
TrainingEvent::RateConfigured {
rate: RATE,
ffe_levels: FfeLevels::Ffe0
},
TrainingEvent::FltReadyReceived {
after_iterations: 2
},
TrainingEvent::FrlStartReceived {
after_iterations: 1
},
TrainingEvent::LtpPatternRequested {
pattern: LtpReq::Lfsr0
},
TrainingEvent::LtpPatternRequested {
pattern: LtpReq::Lfsr2
},
TrainingEvent::AllLanesSatisfied {
after_iterations: 2
},
]
);
}
#[test]
fn trace_phase2_timeout() {
let mut scdc = SimScdc::new();
scdc.push(not_ready());
scdc.push(not_ready());
scdc.push(not_ready());
let (_outcome, trace) = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate_traced(RATE, &cfg(3, 10, 10))
.unwrap();
assert_eq!(
trace.events,
[
TrainingEvent::RateConfigured {
rate: RATE,
ffe_levels: FfeLevels::Ffe0
},
TrainingEvent::FltReadyTimeout {
iterations_elapsed: 3
},
]
);
}
#[test]
fn trace_phase3_timeout() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready()); scdc.push(flt_ready()); scdc.push(flt_ready());
scdc.push(flt_ready());
let (_outcome, trace) = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate_traced(RATE, &cfg(10, 3, 10))
.unwrap();
assert_eq!(
trace.events,
[
TrainingEvent::RateConfigured {
rate: RATE,
ffe_levels: FfeLevels::Ffe0
},
TrainingEvent::FltReadyReceived {
after_iterations: 0
},
TrainingEvent::FrlStartTimeout {
iterations_elapsed: 3
},
]
);
}
#[test]
fn trace_phase4_timeout() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready());
scdc.push(frl_started());
scdc.push(ltp(LtpReq::Lfsr1)); scdc.push(ltp(LtpReq::Lfsr3)); scdc.push(ltp(LtpReq::Lfsr3));
let (_outcome, trace) = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate_traced(RATE, &cfg(10, 10, 3))
.unwrap();
assert_eq!(
trace.events,
[
TrainingEvent::RateConfigured {
rate: RATE,
ffe_levels: FfeLevels::Ffe0
},
TrainingEvent::FltReadyReceived {
after_iterations: 0
},
TrainingEvent::FrlStartReceived {
after_iterations: 0
},
TrainingEvent::LtpPatternRequested {
pattern: LtpReq::Lfsr1
},
TrainingEvent::LtpPatternRequested {
pattern: LtpReq::Lfsr3
},
TrainingEvent::LtpLoopTimeout {
iterations_elapsed: 3
},
]
);
}
#[test]
fn trace_ltp_pattern_on_transition_only() {
let mut scdc = SimScdc::new();
scdc.push(flt_ready());
scdc.push(frl_started());
scdc.push(ltp(LtpReq::Lfsr1));
scdc.push(ltp(LtpReq::Lfsr1));
scdc.push(ltp(LtpReq::Lfsr1));
scdc.push(ltp(LtpReq::Lfsr2));
scdc.push(frl_started());
let (_outcome, trace) = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate_traced(RATE, &TrainingConfig::default())
.unwrap();
let ltp_events: Vec<_> = trace
.events
.iter()
.filter(|e| matches!(e, TrainingEvent::LtpPatternRequested { .. }))
.collect();
assert_eq!(ltp_events.len(), 2); assert_eq!(
ltp_events[0],
&TrainingEvent::LtpPatternRequested {
pattern: LtpReq::Lfsr1
}
);
assert_eq!(
ltp_events[1],
&TrainingEvent::LtpPatternRequested {
pattern: LtpReq::Lfsr2
}
);
assert!(trace.events.contains(&TrainingEvent::AllLanesSatisfied {
after_iterations: 4
}));
}
#[test]
fn trace_config_recorded_and_timeout_interpretable() {
let config = cfg(5, 7, 9);
let mut scdc = SimScdc::new();
for _ in 0..5 {
scdc.push(not_ready());
}
let (_outcome, trace) = FrlTrainer::new(scdc, MockPhy::new())
.train_at_rate_traced(RATE, &config)
.unwrap();
assert_eq!(trace.config, config);
assert!(trace.events.contains(&TrainingEvent::FltReadyTimeout {
iterations_elapsed: 5
}));
assert_eq!(trace.config.flt_ready_timeout, 5);
}
}