Skip to content

Commit 83a4f61

Browse files
doublegateclaude
andcommitted
test(libretro): a dual cabinet's own state round-trips through the C ABI
Copilot's #562 overview noted the libretro serialize change (NL-09, retro_serialize now going through VsDualSystem::snapshot_into) lacked direct ABI coverage. The harness already sent a dual state through retro_serialize, but only to show a REJECTED state being refused. Nothing loaded the frontend's own dual state back through retro_unserialize. New: a_dual_cabinet_state_round_trips_through_the_abi. Save at frame 10, run 50 more, load the save through retro_unserialize (must return true), and a fresh save must equal it byte for byte. Mutation: writing each block's length prefix one too long in snapshot_into makes the load fail, so the test fails; restored, it passes (39 libretro tests, 1 selected). Its limit, stated in the doc comment: both consoles run the same program in this cabinet, so it proves the container's framing and lengths, not the order of the two blocks. The core's snapshot_into_round_trips_into_a_fresh_cabinet owns the order and catches a swap. fmt and clippy -p rustynes-libretro --all-targets -D warnings pass. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
1 parent 1d3b177 commit 83a4f61

1 file changed

Lines changed: 34 additions & 0 deletions

File tree

‎crates/rustynes-libretro/src/abi_tests.rs‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,40 @@ fn a_rejected_unserialize_leaves_the_machine_as_it_was() {
931931
assert!(unchanged, "a rejected dual unserialize changed the cabinet");
932932
}
933933

934+
/// v2.9.1 (NL-09): a Vs. `DualSystem` state now leaves `retro_serialize`
935+
/// through `VsDualSystem::snapshot_into`. The test above only shows a state
936+
/// being REFUSED; this one shows the frontend's own state coming back. Save
937+
/// at frame 10, run 50 more, load the save, and a fresh save must equal it
938+
/// byte for byte. Both consoles run the same program here, so this proves the
939+
/// container's framing and lengths, not the order of the two blocks -- the
940+
/// core's `snapshot_into_round_trips_into_a_fresh_cabinet` owns that.
941+
#[test]
942+
fn a_dual_cabinet_state_round_trips_through_the_abi() {
943+
let _frontend = frontend();
944+
let mut dual = NESTEST.to_vec();
945+
dual[7] = 0x08 | 0x01;
946+
dual[13] = 0x50;
947+
let dual: &'static [u8] = Box::leak(dual.into_boxed_slice());
948+
assert!(load(dual, true));
949+
for _ in 0..10 {
950+
run_frame();
951+
}
952+
let size = serialize_size();
953+
let mut saved = vec![0_u8; size];
954+
assert!(serialize(&mut saved));
955+
for _ in 0..50 {
956+
run_frame();
957+
}
958+
assert!(unserialize(&saved), "the cabinet's own state must load");
959+
let mut again = vec![0_u8; size];
960+
assert!(serialize(&mut again));
961+
unload();
962+
assert!(
963+
saved == again,
964+
"a loaded dual state must serialize back to itself"
965+
);
966+
}
967+
934968
/// libretro audit §1.1 (L-1.1). A panic inside a frame used to cross the
935969
/// `extern "C"` boundary and abort the frontend: in this test process it would
936970
/// abort the test binary, which is what the mutation that removes

0 commit comments

Comments
 (0)