test(mt1959): UT10 run_init returns Ok once load_firmware confirms (RED)

stop-design-v5 §5.2 UT10 / G3 (D10). Red on dev: after a confirmed
load_firmware, run_init settles 10 s and loops back into do_unlock, and a
confirmation on the last attempt is discarded as Err(last_err).
This commit is contained in:
Matthew Jackson
2026-09-27 23:32:30 -07:00
parent 6d91b7ce02
commit 226be3a9f0
+57
View File
@@ -1211,6 +1211,63 @@ mod tests {
);
}
// UT10 (stop-design-v5 §5.2, D10): "`run_init` must `return Ok(())` as soon as
// `load_firmware` confirms the unlock. The final upload is kept." GUARD G3 too;
// per design D10, do not change without a design citation proving otherwise.
#[test]
fn run_init_returns_ok_once_load_firmware_confirms() {
use crate::scsi::mock::{MockTransport, Reply};
let sig = [0x11, 0x22, 0x33, 0x44];
let unlock_ok = build_response(sig, FIRMWARE_ACTIVE_SIG, FIRMWARE_MODE_SIG);
let short = || Reply::short(vec![0u8; 64], 10); // a generic do_unlock failure
let upload_ok = || {
vec![
Reply::good(vec![]), // WRITE_BUFFER
Reply::good(vec![0u8; VALIDATE_RESPONSE_SIZE as usize]), // 0x45 verify
]
};
// Case 1: attempt 0 reloads and load_firmware's own do_unlock confirms.
// Case 2: attempts 0-1 reload but fail; attempt 2 (the LAST) confirms,
// which the retry loop used to discard as `Err(last_err)`.
for failed_reloads in [0usize, 2] {
let mut script = Vec::new();
for _ in 0..failed_reloads {
script.push(short()); // run_init's do_unlock
script.extend(upload_ok());
script.push(short()); // load_firmware's do_unlock fails
}
script.push(short()); // run_init's do_unlock
script.extend(upload_ok());
script.push(Reply::good(unlock_ok.clone())); // load_firmware do_unlock #1
script.push(Reply::good(unlock_ok.clone())); // do_unlock #2 (best-effort)
let expected_calls = script.len();
// Anything past the script is a further retry, which must not happen.
let mut t = MockTransport::scripted(script, Reply::TransportFault);
let mut profile = fixture_profile(sig);
profile.firmware = vec![0u8; 64];
let mut mt = Mt1959::new(profile, false);
let t0 = std::time::Instant::now();
mt.init(&mut t)
.unwrap_or_else(|e| panic!("{failed_reloads} failed reloads: {e:?}"));
assert_eq!(
t.calls(),
expected_calls,
"no retry after a confirmed unlock"
);
assert!(mt.is_ready() && mt.is_unlocked(), "the unlock is kept");
let uploads = t.cdbs.iter().filter(|c| c[0] == SCSI_WRITE_BUFFER).count();
assert_eq!(
uploads,
failed_reloads + 1,
"the final upload is still issued"
);
assert!(
t0.elapsed() < std::time::Duration::from_secs(5),
"no 10 s settle"
);
}
}
// ── run_probe entry / disc-type detection ───────────────────────────────
/// `run_probe` called before `init_complete` runs `do_unlock` itself