Files
alktype/tests/poc_roundtrip.rs
glm-5.2 5e268e8f47 Rebrand TypeDef to AlkType in code, keyword strings, and docs
Rename all 19 JSON Schema custom keyword strings from "TypeDef:*"
to "AlkType:*" (e.g., "TypeDef:Struct" -> "AlkType:Struct")
across source, tests, and docs. This is a breaking change to the
schema format itself — existing schemas using the old keywords
must be updated.

Rename the Rust identifiers:
- TypedefEngine -> AlkTypeEngine
- TypedefError -> AlkTypeError
- TypeDefKind -> AlkTypeKind
- TYPEDEF_PREFIX -> ALKTYPE_PREFIX
- get_typedef_kind{,_loose,_loose_enum,_enum} ->
  get_alktype_kind{,_loose,_loose_enum,_enum}

Update error message strings ("unknown TypeDef kind" ->
"unknown AlkType kind"), 11 test function names containing
typedef_kind/to_typedef_error, and doc-comment prose ("TypeDef
kind" -> "AlkType kind", "typedef engine" -> "alktype
engine", "typedef schema" -> "alktype schema"). Fix the broken
docs/architecture/crates/typedef/ path references in source doc
comments to point at docs/architecture/ directly. Rebrand the
typedef:annotation test fixture and the "not-a-typedef" test
string to their alktype equivalents.

Update ~20 generic "typedef" prose references in the architecture
docs ("typedef is the binary struct engine", "use typedef",
"typedef limitation", "replaced by typedef", etc.) to alktype.
Rename TypedefEngine in the ADR-007 code example to AlkTypeEngine.

Preserve as provenance per the prior prose-rebrand decision:
typedef.ts references (external TypeBox source file),
docs/research/alknet-typedef/findings.md research citations,
/workspace/alknet-typedef-poc/ POC path, and the
"alknet-typedef:" research section headers in findings.

Build, 295 tests, and clippy all pass clean.
2026-08-02 07:05:53 +00:00

536 lines
17 KiB
Rust

//! POC round-trip tests adapted from `/workspace/alknet-typedef-poc/`.
//!
//! These tests re-validate the byte-identical round-trip behaviour that
//! the POC verified: fixed-size primitives, length-prefixed strings and
//! bytes, nested structs, big-endian, alignment padding, packed-layout
//! `LayoutBuilder` with `data_access` writes, and `SequentialReader`
//! walks. Each test writes values to a buffer at computed offsets and
//! reads them back, asserting both the values and (where applicable)
//! the byte positions.
use alktype::data_access;
use alktype::tunion;
use alktype::*;
use serde_json::json;
use std::collections::HashMap;
fn var_sizes(pairs: &[(&str, usize)]) -> HashMap<String, usize> {
pairs.iter().map(|(k, v)| (k.to_string(), *v)).collect()
}
#[test]
fn fixed_size_round_trip_via_offset_map() -> Result<(), AlkTypeError> {
let schema = json!({
"AlkType:Struct": true,
"properties": {
"id": { "AlkType:Uint32": true },
"score": { "AlkType:Float32": true },
"flag": { "AlkType:Uint8": true },
"count": { "AlkType:Uint16": true }
}
});
let offset_map = OffsetMap::compute(&schema)?;
let mut buffer = vec![0u8; offset_map.total_size()];
let id_range = offset_map.get("id").expect("id range");
data_access::write_u32(&mut buffer, id_range.start, 42, "id", Endian::Little)?;
let score_range = offset_map.get("score").expect("score range");
data_access::write_f32(&mut buffer, score_range.start, 1.5, "score", Endian::Little)?;
let flag_range = offset_map.get("flag").expect("flag range");
data_access::write_u8(&mut buffer, flag_range.start, 1, "flag")?;
let count_range = offset_map.get("count").expect("count range");
data_access::write_u16(
&mut buffer,
count_range.start,
1000,
"count",
Endian::Little,
)?;
assert_eq!(
data_access::read_u32(&buffer, id_range.start, "id", Endian::Little)?,
42
);
let score = data_access::read_f32(&buffer, score_range.start, "score", Endian::Little)?;
assert!((score - 1.5).abs() < 0.001, "score: {score}");
assert_eq!(data_access::read_u8(&buffer, flag_range.start, "flag")?, 1);
assert_eq!(
data_access::read_u16(&buffer, count_range.start, "count", Endian::Little)?,
1000
);
Ok(())
}
#[test]
fn fixed_size_round_trip_via_engine_aligned() -> Result<(), AlkTypeError> {
let mut schema = json!({
"AlkType:Struct": true,
"endian": "little",
"properties": {
"id": { "AlkType:Uint32": true },
"score": { "AlkType:Float32": true },
"flag": { "AlkType:Uint8": true }
}
});
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
let offset_map = engine.offset_map().expect("aligned mode has offset_map");
let mut buffer = vec![0u8; offset_map.total_size()];
engine.write_field(&mut buffer, "id", &FieldValue::U32(42))?;
engine.write_field(&mut buffer, "score", &FieldValue::F32(1.5))?;
engine.write_field(&mut buffer, "flag", &FieldValue::U8(1))?;
assert_eq!(engine.read_field(&buffer, "id")?, FieldValue::U32(42));
let score = match engine.read_field(&buffer, "score")? {
FieldValue::F32(f) => f,
other => panic!("expected F32, got {other:?}"),
};
assert!((score - 1.5).abs() < 0.001);
assert_eq!(engine.read_field(&buffer, "flag")?, FieldValue::U8(1));
Ok(())
}
#[test]
fn string_round_trip_via_data_access() -> Result<(), AlkTypeError> {
let mut buffer = vec![0u8; 32];
let written = data_access::write_string(&mut buffer, 0, "hello", "name", Endian::Little)?;
assert_eq!(written, 4 + 5);
assert_eq!(buffer[0..4], 5u32.to_le_bytes());
assert_eq!(&buffer[4..9], b"hello");
assert_eq!(
data_access::read_string(&buffer, 0, "name", Endian::Little)?,
"hello"
);
Ok(())
}
#[test]
fn string_round_trip_via_engine_aligned() -> Result<(), AlkTypeError> {
let mut schema = json!({
"AlkType:Struct": true,
"properties": {
"name": { "AlkType:String": true }
}
});
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
let offset_map = engine.offset_map().expect("aligned mode has offset_map");
let mut buffer = vec![0u8; offset_map.total_size() + 64];
engine.write_field(&mut buffer, "name", &FieldValue::String("hello"))?;
assert_eq!(
engine.read_field(&buffer, "name")?,
FieldValue::String("hello")
);
Ok(())
}
#[test]
fn bytes_round_trip_via_data_access() -> Result<(), AlkTypeError> {
let payload = [0xAA, 0xBB, 0xCC, 0xDD];
let mut buffer = vec![0u8; 32];
let written = data_access::write_bytes(&mut buffer, 0, &payload, "data", Endian::Little)?;
assert_eq!(written, 4 + 4);
assert_eq!(buffer[0..4], 4u32.to_le_bytes());
assert_eq!(&buffer[4..8], &payload);
assert_eq!(
data_access::read_bytes(&buffer, 0, "data", Endian::Little)?,
&payload[..]
);
Ok(())
}
#[test]
fn nested_struct_round_trip_via_offset_map() -> Result<(), AlkTypeError> {
let schema = json!({
"AlkType:Struct": true,
"properties": {
"header": {
"AlkType:Struct": true,
"properties": {
"version": { "AlkType:Uint32": true },
"magic": { "AlkType:Uint32": true }
}
},
"payload": { "AlkType:Bytes": true }
}
});
let offset_map = OffsetMap::compute(&schema)?;
let header_version = offset_map.get("header.version").expect("header.version");
let header_magic = offset_map.get("header.magic").expect("header.magic");
let payload_prefix = offset_map.get("payload").expect("payload");
assert_eq!(header_version.start, 0);
assert_eq!(header_magic.start, 4);
assert_eq!(payload_prefix.start, 8);
let data = b"body-data".to_vec();
let mut buffer = vec![0u8; offset_map.total_size() + data.len()];
data_access::write_u32(
&mut buffer,
header_version.start,
1,
"header.version",
Endian::Little,
)?;
data_access::write_u32(
&mut buffer,
header_magic.start,
0xCAFEBABE,
"header.magic",
Endian::Little,
)?;
data_access::write_bytes(
&mut buffer,
payload_prefix.start,
&data,
"payload",
Endian::Little,
)?;
assert_eq!(
data_access::read_u32(
&buffer,
header_version.start,
"header.version",
Endian::Little
)?,
1
);
assert_eq!(
data_access::read_u32(&buffer, header_magic.start, "header.magic", Endian::Little)?,
0xCAFEBABE
);
assert_eq!(
data_access::read_bytes(&buffer, payload_prefix.start, "payload", Endian::Little)?,
&data[..]
);
Ok(())
}
#[test]
fn nested_struct_round_trip_via_engine_aligned() -> Result<(), AlkTypeError> {
let mut schema = json!({
"AlkType:Struct": true,
"properties": {
"header": {
"AlkType:Struct": true,
"properties": {
"version": { "AlkType:Uint8": true },
"flags": { "AlkType:Uint8": true }
}
},
"payload_len": { "AlkType:Uint32": true }
}
});
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
let offset_map = engine.offset_map().expect("aligned mode");
assert_eq!(offset_map.get("header.version").unwrap().start, 0);
assert_eq!(offset_map.get("header.flags").unwrap().start, 1);
assert_eq!(offset_map.get("payload_len").unwrap().start, 4);
let mut buffer = vec![0u8; offset_map.total_size()];
engine.write_field(&mut buffer, "header.version", &FieldValue::U8(1))?;
engine.write_field(&mut buffer, "header.flags", &FieldValue::U8(0x0F))?;
engine.write_field(&mut buffer, "payload_len", &FieldValue::U32(1024))?;
assert_eq!(
engine.read_field(&buffer, "header.version")?,
FieldValue::U8(1)
);
assert_eq!(
engine.read_field(&buffer, "header.flags")?,
FieldValue::U8(0x0F)
);
assert_eq!(
engine.read_field(&buffer, "payload_len")?,
FieldValue::U32(1024)
);
Ok(())
}
#[test]
fn big_endian_round_trip_via_offset_map() -> Result<(), AlkTypeError> {
let schema = json!({
"AlkType:Struct": true,
"endian": "big",
"properties": {
"id": { "AlkType:Uint32": true },
"offset": { "AlkType:Float64": true }
}
});
let offset_map = OffsetMap::compute(&schema)?;
let endian = Endian::from_schema(&schema);
assert_eq!(endian, Endian::Big);
let id_range = offset_map.get("id").expect("id");
let offset_range = offset_map.get("offset").expect("offset");
assert_eq!(id_range.start, 0);
assert_eq!(offset_range.start, 8);
let value: f64 = std::f64::consts::PI;
let mut buffer = vec![0u8; offset_map.total_size()];
data_access::write_u32(&mut buffer, id_range.start, 0x01020304, "id", endian)?;
data_access::write_f64(&mut buffer, offset_range.start, value, "offset", endian)?;
assert_eq!(&buffer[0..4], &[0x01, 0x02, 0x03, 0x04]);
assert_eq!(&buffer[4..8], &[0x00, 0x00, 0x00, 0x00]);
assert_eq!(&buffer[8..16], value.to_be_bytes());
assert_eq!(
data_access::read_u32(&buffer, id_range.start, "id", endian)?,
0x01020304
);
let read = data_access::read_f64(&buffer, offset_range.start, "offset", endian)?;
assert!((read - value).abs() < 1e-12);
Ok(())
}
#[test]
fn alignment_padding_round_trip_u8_then_u32() -> Result<(), AlkTypeError> {
let schema = json!({
"AlkType:Struct": true,
"properties": {
"flag": { "AlkType:Uint8": true },
"id": { "AlkType:Uint32": true }
}
});
let offset_map = OffsetMap::compute(&schema)?;
let flag_range = offset_map.get("flag").expect("flag");
let id_range = offset_map.get("id").expect("id");
assert_eq!(flag_range.start, 0);
assert_eq!(flag_range.end, 1);
assert_eq!(id_range.start, 4);
assert_eq!(id_range.end, 8);
assert_eq!(offset_map.total_size(), 8);
let mut buffer = vec![0u8; offset_map.total_size()];
data_access::write_u8(&mut buffer, flag_range.start, 0xAB, "flag")?;
data_access::write_u32(
&mut buffer,
id_range.start,
0x01020304,
"id",
Endian::Little,
)?;
assert_eq!(buffer[0], 0xAB);
assert_eq!(&buffer[1..4], &[0x00, 0x00, 0x00]);
assert_eq!(&buffer[4..8], 0x01020304u32.to_le_bytes());
assert_eq!(
data_access::read_u8(&buffer, flag_range.start, "flag")?,
0xAB
);
assert_eq!(
data_access::read_u32(&buffer, id_range.start, "id", Endian::Little)?,
0x01020304
);
Ok(())
}
#[test]
fn packed_layout_round_trip_via_layout_builder() -> Result<(), AlkTypeError> {
let schema = json!({
"AlkType:Struct": true,
"endian": "little",
"properties": {
"flag": { "AlkType:Uint8": true },
"id": { "AlkType:Uint32": true },
"payload": { "AlkType:String": true }
}
});
let builder = LayoutBuilder::new(&schema)?;
let layout = builder.build(&var_sizes(&[("payload", 10)]))?;
let flag_pos = layout.get("flag").expect("flag");
let id_pos = layout.get("id").expect("id");
let payload_pos = layout.get("payload").expect("payload");
assert_eq!(flag_pos.offset, 0);
assert_eq!(id_pos.offset, 1);
assert_eq!(payload_pos.offset, 5);
assert_eq!(layout.total_size(), 19);
let payload_str = "ten bytes!";
let payload_bytes = payload_str.as_bytes();
assert_eq!(payload_bytes.len(), 10);
let mut buffer = vec![0u8; layout.total_size()];
data_access::write_u8(&mut buffer, flag_pos.offset, 0xAB, "flag")?;
data_access::write_u32(&mut buffer, id_pos.offset, 0x01020304, "id", Endian::Little)?;
data_access::write_string(
&mut buffer,
payload_pos.offset,
payload_str,
"payload",
Endian::Little,
)?;
assert_eq!(buffer[0], 0xAB);
assert_eq!(&buffer[1..5], 0x01020304u32.to_le_bytes());
assert_eq!(&buffer[5..9], 10u32.to_le_bytes());
assert_eq!(&buffer[9..19], payload_bytes);
assert_eq!(
data_access::read_u8(&buffer, flag_pos.offset, "flag")?,
0xAB
);
assert_eq!(
data_access::read_u32(&buffer, id_pos.offset, "id", Endian::Little)?,
0x01020304
);
assert_eq!(
data_access::read_string(&buffer, payload_pos.offset, "payload", Endian::Little)?,
payload_str
);
Ok(())
}
#[test]
fn sequential_reader_round_trip_packed_buffer() -> Result<(), AlkTypeError> {
let schema = json!({
"AlkType:Struct": true,
"endian": "little",
"properties": {
"id": { "AlkType:Uint8": true },
"name": { "AlkType:String": true },
"tail": { "AlkType:Uint8": true }
}
});
let builder = LayoutBuilder::new(&schema)?;
let payload = "hello";
let layout = builder.build(&var_sizes(&[("name", payload.len())]))?;
let mut buffer = vec![0u8; layout.total_size()];
data_access::write_u8(&mut buffer, 0, 7, "id")?;
data_access::write_string(&mut buffer, 1, payload, "name", Endian::Little)?;
let after = 1 + 4 + payload.len();
data_access::write_u8(&mut buffer, after, 99, "tail")?;
let mut reader = SequentialReader::new(&schema)?;
assert_eq!(reader.endian(), Endian::Little);
assert_eq!(reader.position(), 0);
let (name, value) = reader.read_next(&buffer)?.expect("field 0");
assert_eq!(name, "id");
assert_eq!(value, FieldValue::U8(7));
assert_eq!(reader.position(), 1);
let (name, value) = reader.read_next(&buffer)?.expect("field 1");
assert_eq!(name, "name");
assert_eq!(value, FieldValue::String("hello"));
assert_eq!(reader.position(), after);
let (name, value) = reader.read_next(&buffer)?.expect("field 2");
assert_eq!(name, "tail");
assert_eq!(value, FieldValue::U8(99));
assert_eq!(reader.position(), after + 1);
assert!(reader.read_next(&buffer)?.is_none());
Ok(())
}
#[test]
fn sequential_reader_read_field_walks_preceding_fields() -> Result<(), AlkTypeError> {
let schema = json!({
"AlkType:Struct": true,
"endian": "little",
"properties": {
"a": { "AlkType:Uint8": true },
"b": { "AlkType:Uint32": true },
"c": { "AlkType:Uint8": true }
}
});
let mut buffer = vec![0u8; 16];
data_access::write_u8(&mut buffer, 0, 1, "a")?;
data_access::write_u32(&mut buffer, 1, 0xDEADBEEF, "b", Endian::Little)?;
data_access::write_u8(&mut buffer, 5, 9, "c")?;
let mut reader = SequentialReader::new(&schema)?;
let value = reader.read_field(&buffer, "c")?;
assert_eq!(value, FieldValue::U8(9));
assert_eq!(reader.position(), 6);
reader.reset();
let value = reader.read_field(&buffer, "b")?;
assert_eq!(value, FieldValue::U32(0xDEADBEEF));
Ok(())
}
#[test]
fn tunion_byte_offset_discriminator_dispatch() -> Result<(), AlkTypeError> {
let union_schema = json!({
"AlkType:Union": true,
"discriminator": {
"kind": "byte",
"offset": 0,
"type": "AlkType:Uint8"
},
"mapping": {
"5": { "$ref": "#/$defs/Read" },
"6": { "$ref": "#/$defs/Write" }
},
"$defs": {
"Read": {
"AlkType:Struct": true,
"properties": {
"handle": { "AlkType:Uint32": true },
"length": { "AlkType:Uint32": true }
}
},
"Write": {
"AlkType:Struct": true,
"properties": {
"handle": { "AlkType:Uint32": true },
"length": { "AlkType:Uint32": true },
"data": { "AlkType:Uint32": true }
}
}
}
});
let mut buffer = vec![0u8; 32];
buffer[0] = 5;
data_access::write_u32(&mut buffer, 1, 0x01020304, "Read.handle", Endian::Big)?;
data_access::write_u32(&mut buffer, 5, 4096, "Read.length", Endian::Big)?;
let dispatch = tunion::read_byte_discriminator(&buffer, &union_schema, Endian::Big)?;
assert_eq!(dispatch.key, "5");
assert_eq!(dispatch.variant_offset, 1);
assert_eq!(dispatch.discriminator_size, 1);
let variant = tunion::resolve_variant(&union_schema, &dispatch.key)?;
assert_eq!(
variant
.get("AlkType:Struct")
.and_then(serde_json::Value::as_bool),
Some(true)
);
Ok(())
}
#[test]
fn tunion_byte_offset_discriminator_size_lookup() -> Result<(), AlkTypeError> {
let u8_schema = json!({
"AlkType:Union": true,
"discriminator": {"kind": "byte", "type": "AlkType:Uint8"},
"mapping": {}
});
let u16_schema = json!({
"AlkType:Union": true,
"discriminator": {"kind": "byte", "type": "AlkType:Uint16"},
"mapping": {}
});
let u32_schema = json!({
"AlkType:Union": true,
"discriminator": {"kind": "byte", "type": "AlkType:Uint32"},
"mapping": {}
});
assert_eq!(tunion::discriminator_size(&u8_schema)?, 1);
assert_eq!(tunion::discriminator_size(&u16_schema)?, 2);
assert_eq!(tunion::discriminator_size(&u32_schema)?, 4);
Ok(())
}