#[cfg(test)]
mod tests {
use super::*;
use crate::generate::composers::CompositionStrategy;
use crate::spec::builder::BuildError;
use crate::spec::types::DataType;
use crate::{OpSignature, OpSpec};
fn dummy_cpu(_input: &[u8]) -> Vec<u8> {
vec![0, 0, 0, 0]
}
fn dummy_wgsl() -> String {
String::new()
}
fn make_spec(
id: &'static str,
inputs: Vec<DataType>,
output: DataType,
) -> Result<OpSpec, BuildError> {
OpSpec::builder(id)
.signature(OpSignature { inputs, output })
.cpu_fn(dummy_cpu)
.wgsl_fn(dummy_wgsl)
.category(crate::Category::A {
composition_of: vec![id],
})
.laws(vec![crate::spec::law::AlgebraicLaw::Bounded {
lo: 0,
hi: u32::MAX,
}])
.strictness(crate::spec::types::Strictness::Strict)
.version(1)
.build()
}
#[test]
fn can_chain_matching_types() -> Result<(), BuildError> {
let a = make_spec("a", vec![DataType::U32], DataType::U32)?;
let b = make_spec("b", vec![DataType::U32], DataType::U32)?;
assert!(can_chain(&a, &b));
Ok(())
}
#[test]
fn cannot_chain_mismatched_types() -> Result<(), BuildError> {
let a = make_spec("a", vec![DataType::U32], DataType::U32)?;
let b = make_spec("b", vec![DataType::Bytes], DataType::Bytes)?;
assert!(!can_chain(&a, &b));
Ok(())
}
#[test]
fn pairwise_generates_self_chains() -> Result<(), BuildError> {
let specs = vec![make_spec(
"xor",
vec![DataType::U32, DataType::U32],
DataType::U32,
)?];
let chains = PairwiseComposer.generate_chains(&specs);
assert_eq!(chains.len(), 1);
assert_eq!(chains[0].id, "xor__then__xor");
Ok(())
}
#[test]
fn pairwise_cross_type_excluded() -> Result<(), BuildError> {
let specs = vec![
make_spec("a", vec![DataType::U32], DataType::U32)?,
make_spec("b", vec![DataType::Bytes], DataType::Bytes)?,
];
let chains = PairwiseComposer.generate_chains(&specs);
assert_eq!(chains.len(), 2);
Ok(())
}
#[test]
fn pairwise_preserves_output_type() -> Result<(), String> {
let a = make_spec("a", vec![DataType::U32, DataType::U32], DataType::U32)
.map_err(|err| format!("Fix: valid pairwise fixture must build: {err:?}"))?;
let b = make_spec("b", vec![DataType::U32], DataType::Bytes)
.map_err(|err| format!("Fix: valid pairwise fixture must build: {err:?}"))?;
let specs = vec![a, b];
let chains = PairwiseComposer.generate_chains(&specs);
let Some(a_then_b) = chains.iter().find(|c| c.id == "a__then__b") else {
return Err(
"Fix: pairwise composer must emit a__then__b for matching signatures".to_string(),
);
};
assert_eq!(a_then_b.signature.output, DataType::Bytes);
Ok(())
}
#[test]
fn pairwise_preserves_input_types() -> Result<(), String> {
let a = make_spec("a", vec![DataType::U32, DataType::U32], DataType::U32)
.map_err(|err| format!("Fix: valid pairwise fixture must build: {err:?}"))?;
let b = make_spec("b", vec![DataType::U32], DataType::U32)
.map_err(|err| format!("Fix: valid pairwise fixture must build: {err:?}"))?;
let specs = vec![a, b];
let chains = PairwiseComposer.generate_chains(&specs);
let Some(a_then_b) = chains.iter().find(|c| c.id == "a__then__b") else {
return Err(
"Fix: pairwise composer must emit a__then__b for matching signatures".to_string(),
);
};
assert_eq!(
a_then_b.signature.inputs,
vec![DataType::U32, DataType::U32]
);
Ok(())
}
#[test]
fn pairwise_empty_specs_produces_nothing() {
let chains = PairwiseComposer.generate_chains(&[]);
assert!(chains.is_empty());
}
}