use std::fs;
use std::path::Path;
#[test]
fn dispatch_decode_is_split_by_gpu_abi_responsibility() {
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
let legacy = root.join("src/dispatch_decode.rs");
assert!(
!legacy.exists(),
"Fix: dispatch_decode must stay a module directory, not a single mixed src/dispatch_decode.rs file."
);
for required in [
"src/dispatch_decode/mod.rs",
"src/dispatch_decode/schema.rs",
"src/dispatch_decode/pack.rs",
"src/dispatch_decode/validate.rs",
"src/dispatch_decode/unpack.rs",
] {
assert!(
root.join(required).is_file(),
"Fix: missing dispatch decode module file {required}."
);
}
}
#[test]
fn dispatch_decode_mod_file_preserves_stable_crate_internal_surface() {
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
let source = fs::read_to_string(root.join("src/dispatch_decode/mod.rs"))
.expect("dispatch_decode/mod.rs must be readable");
for module in ["mod pack;", "mod schema;", "mod validate;", "mod unpack;"] {
assert!(
source.contains(module),
"Fix: dispatch_decode/mod.rs must declare {module}."
);
}
for required_api in [
"pack_u32",
"pack_exact_u32_slots_into",
"DispatchU32Schema",
"U32_BITS_PER_WORD",
"require_bitset_words",
"require_csr_shape",
"require_output_count",
"unpack_only_exact_u32_into",
"unpack_exact_u32_scalar",
] {
assert!(
source.contains(required_api),
"Fix: dispatch_decode/mod.rs must re-export crate-internal API {required_api}."
);
}
}
#[test]
fn dispatch_decode_submodules_do_one_kind_of_work() {
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
let pack_source = fs::read_to_string(root.join("src/dispatch_decode/pack.rs"))
.expect("dispatch_decode/pack.rs must be readable");
let schema_source = fs::read_to_string(root.join("src/dispatch_decode/schema.rs"))
.expect("dispatch_decode/schema.rs must be readable");
let validate_source = fs::read_to_string(root.join("src/dispatch_decode/validate.rs"))
.expect("dispatch_decode/validate.rs must be readable");
let unpack_source = fs::read_to_string(root.join("src/dispatch_decode/unpack.rs"))
.expect("dispatch_decode/unpack.rs must be readable");
let pack = production_source(&pack_source);
let schema = production_source(&schema_source);
let validate = production_source(&validate_source);
let unpack = production_source(&unpack_source);
assert!(
pack.contains("fn try_pack_u32_into")
&& pack.contains("fn pack_repeated_u32_into")
&& !pack.contains("fn unpack_exact_u32"),
"Fix: pack.rs must own byte packing only, not output decoding."
);
assert!(
schema.contains("struct DispatchU32Schema")
&& schema.contains("fn require_input_words")
&& schema.contains("fn require_output_bytes")
&& !schema.contains("fn try_pack_u32_into")
&& !schema.contains("fn unpack_exact_u32_into")
&& !schema.contains("fn require_csr_shape"),
"Fix: schema.rs must own shared ABI schema and exact diagnostics, not packing, decoding, or graph validation."
);
assert!(
validate.contains("fn require_bitset_words")
&& validate.contains("fn require_csr_offsets_targets")
&& !validate.contains("fn pack_u32")
&& !validate.contains("fn unpack_exact_u32"),
"Fix: validate.rs must own shape/domain validation only."
);
assert!(
unpack.contains("fn require_output_count")
&& unpack.contains("fn unpack_exact_u32_into")
&& !unpack.contains("fn pack_u32")
&& !unpack.contains("fn require_csr_shape"),
"Fix: unpack.rs must own output-count and byte-decoding ABI checks only."
);
}
fn production_source(source: &str) -> &str {
source
.split("#[cfg(test)]")
.next()
.expect("source must have a production prefix")
}