feat(ci): add make fixtures-client target for GDScript->Rust cross-encoder validation (#475)
Closes the bidirectional protocol compatibility loop (D-030 Layer 1): - GDScript fixture generator (20 fixtures: inputs, boundary ticks, batch) - Rust decoder test verifying all GDScript-encoded fixtures deserialize - Makefile target with generation + verification in one step Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
@@ -0,0 +1 @@
|
||||
うtickヲaction・Pause
|
||||
@@ -0,0 +1 @@
|
||||
うtickフヲaction・Pause
|
||||
@@ -0,0 +1 @@
|
||||
‚¤tickÒÿÿÿ¦action¥Pause
|
||||
@@ -0,0 +1 @@
|
||||
うtickフ�ヲaction・Pause
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
うtickム�ヲaction・Pause
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
‚¤tickÍÿÿ¦action¥Pause
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
うtickdヲaction→Interactげtarget_entity_idタ、verbタ
|
||||
@@ -0,0 +1 @@
|
||||
うtickdヲactionゥMoveNorth
|
||||
@@ -0,0 +1 @@
|
||||
うtickdヲactionュMoveNortheast
|
||||
@@ -0,0 +1 @@
|
||||
うtickdヲactionュMoveNorthwest
|
||||
@@ -0,0 +1 @@
|
||||
うtickdヲactionュMoveSoutheast
|
||||
@@ -0,0 +1 @@
|
||||
うtickdヲactionュMoveSouthwest
|
||||
@@ -0,0 +1 @@
|
||||
うtickdヲaction・Pause
|
||||
@@ -0,0 +1 @@
|
||||
うtickフネヲaction�UsePerceptionModeァthermal
|
||||
@@ -0,0 +1 @@
|
||||
うtickdヲactionョToggleStanceUp
|
||||
@@ -1004,6 +1004,100 @@ fn malformed_input_in_batch_rejects_entire_batch() {
|
||||
);
|
||||
}
|
||||
|
||||
/// GDScript-generated fixtures must deserialize correctly (D-030 Layer 1, #475).
|
||||
/// Validates the reverse direction: GDScript encoder -> Rust decoder.
|
||||
/// Together with all_fixtures_deserialize (Rust -> GDScript), this closes the
|
||||
/// cross-encoder compatibility loop.
|
||||
///
|
||||
/// Fixtures generated by: make fixtures-client
|
||||
/// (runs client/tests/gen_client_fixtures.gd via Godot headless)
|
||||
#[test]
|
||||
fn gdscript_generated_fixtures_deserialize() {
|
||||
let fixture_dir = std::path::Path::new("tests/fixtures/gdscript");
|
||||
|
||||
if !fixture_dir.exists() {
|
||||
eprintln!(
|
||||
"SKIP: GDScript fixtures not found at {}. Run `make fixtures-client` to generate.",
|
||||
fixture_dir.display()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut count = 0;
|
||||
for entry in fs::read_dir(&fixture_dir).expect("read gdscript fixture dir") {
|
||||
let entry = entry.expect("read dir entry");
|
||||
let path = entry.path();
|
||||
if path.extension().and_then(|e| e.to_str()) != Some("msgpack") {
|
||||
continue;
|
||||
}
|
||||
let name = path.file_stem().unwrap().to_str().unwrap().to_string();
|
||||
let bytes = fs::read(&path).unwrap_or_else(|_| panic!("read fixture {}", name));
|
||||
|
||||
if name.starts_with("input_batch") {
|
||||
let inputs: Vec<PlayerInput> = rmp_serde::from_slice(&bytes).unwrap_or_else(|e| {
|
||||
panic!("deserialize GDScript batch fixture {}: {}", name, e)
|
||||
});
|
||||
assert!(
|
||||
!inputs.is_empty(),
|
||||
"batch fixture {} should not be empty",
|
||||
name
|
||||
);
|
||||
} else if name.starts_with("input_") || name.starts_with("boundary_tick_") {
|
||||
let input: PlayerInput = rmp_serde::from_slice(&bytes).unwrap_or_else(|e| {
|
||||
panic!("deserialize GDScript input fixture {}: {}", name, e)
|
||||
});
|
||||
// Verify specific fixtures for extra confidence
|
||||
match name.as_str() {
|
||||
"input_move_north" => {
|
||||
assert_eq!(input.tick, 100);
|
||||
assert!(matches!(input.action, PlayerAction::MoveNorth));
|
||||
}
|
||||
"input_perception_mode" => {
|
||||
assert_eq!(input.tick, 200);
|
||||
assert!(
|
||||
matches!(input.action, PlayerAction::UsePerceptionMode(ref s) if s == "thermal")
|
||||
);
|
||||
}
|
||||
"input_interact" => {
|
||||
assert_eq!(input.tick, 100);
|
||||
assert!(matches!(input.action, PlayerAction::Interact { .. }));
|
||||
}
|
||||
"boundary_tick_256" => {
|
||||
assert_eq!(input.tick, 256, "int_16 asymmetry: tick=256");
|
||||
}
|
||||
"boundary_tick_32767" => {
|
||||
assert_eq!(input.tick, 32767, "int_16 asymmetry: tick=32767");
|
||||
}
|
||||
"boundary_tick_65536" => {
|
||||
assert_eq!(input.tick, 65536, "int_32 asymmetry: tick=65536");
|
||||
}
|
||||
"boundary_tick_2147483647" => {
|
||||
assert_eq!(
|
||||
input.tick, 2147483647,
|
||||
"int_32 asymmetry: tick=2^31-1"
|
||||
);
|
||||
}
|
||||
_ => {} // Other fixtures: deserialization success is sufficient
|
||||
}
|
||||
} else {
|
||||
panic!(
|
||||
"unknown GDScript fixture naming convention: {} — add a deserialization branch",
|
||||
name
|
||||
);
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
eprintln!(
|
||||
"SKIP: no .msgpack files found in {}. Run `make fixtures-client` to generate.",
|
||||
fixture_dir.display()
|
||||
);
|
||||
return;
|
||||
}
|
||||
eprintln!("Verified {} GDScript-generated fixtures", count);
|
||||
}
|
||||
|
||||
/// NearbyInteraction.object_type round-trips through MessagePack (#422).
|
||||
/// Verifies object_type=Some(Container) survives the wire.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user