use super::{chain_comparator, chain_version, workgroup_sizes, ConformanceSuite};
use crate::proof::comparator::ComparatorKind;
use crate::spec::op_registry;
use crate::spec::types::ChainSpec;
use crate::spec::types::Convention;
use crate::spec::types::{ConstructionTime, ProofToken, ProofTokenError};
fn manual_token() -> Result<ProofToken, ProofTokenError> {
ProofToken::from_specs(&[], ConstructionTime::Manual)
}
#[test]
fn workgroup_sizes_default_contains_1_and_64() -> Result<(), String> {
let sizes = workgroup_sizes(None)?;
assert!(sizes.contains(&1));
assert!(sizes.contains(&64));
Ok(())
}
#[test]
fn workgroup_sizes_with_preferred() -> Result<(), String> {
let sizes = workgroup_sizes(Some(128))?;
assert!(sizes.contains(&1));
assert!(sizes.contains(&64));
assert!(sizes.contains(&128));
Ok(())
}
#[test]
fn workgroup_sizes_preferred_zero_rejected() {
let err = workgroup_sizes(Some(0)).unwrap_err();
assert!(err.contains("cannot be 0"), "got: {err}");
}
#[test]
fn workgroup_sizes_preferred_too_large_rejected() {
let err = workgroup_sizes(Some(2048)).unwrap_err();
assert!(err.contains("exceeds 1024"), "got: {err}");
}
#[test]
fn workgroup_sizes_deduplication() -> Result<(), String> {
let sizes = workgroup_sizes(Some(64))?;
assert_eq!(sizes.iter().filter(|&&s| s == 64).count(), 1);
Ok(())
}
#[test]
fn chain_version_max_of_specs() -> Result<(), ProofTokenError> {
let chain = ChainSpec {
id: "test.chain".to_string(),
ops: vec!["a", "b"],
signature: crate::spec::primitive::binary_u32_sig(),
specs: vec![crate::spec::primitive::xor::spec(), {
let mut s = crate::spec::primitive::and::spec();
s.version = 3;
s
}],
cpu_chain: None,
proof_token: manual_token()?,
};
assert_eq!(chain_version(&chain), 3);
Ok(())
}
#[test]
#[should_panic(expected = "empty chain")]
fn chain_version_empty_chain() {
let chain = ChainSpec {
id: "empty".to_string(),
ops: vec![],
signature: crate::spec::primitive::unary_u32_sig(),
specs: vec![],
cpu_chain: None,
proof_token: manual_token().expect("test token should build"),
};
assert_eq!(chain_version(&chain), 0);
}
#[test]
fn chain_comparator_uses_last_spec() -> Result<(), ProofTokenError> {
let chain = ChainSpec {
id: "test".to_string(),
ops: vec!["a"],
signature: crate::spec::primitive::unary_u32_sig(),
specs: vec![{
let mut s = crate::spec::primitive::xor::spec();
s.comparator = ComparatorKind::UnorderedMatch;
s
}],
cpu_chain: None,
proof_token: manual_token()?,
};
assert_eq!(chain_comparator(&chain), ComparatorKind::UnorderedMatch);
Ok(())
}
#[test]
fn chain_comparator_empty_defaults_to_exact() -> Result<(), ProofTokenError> {
let chain = ChainSpec {
id: "empty".to_string(),
ops: vec![],
signature: crate::spec::primitive::unary_u32_sig(),
specs: vec![],
cpu_chain: None,
proof_token: manual_token()?,
};
assert_eq!(chain_comparator(&chain), ComparatorKind::ExactMatch);
Ok(())
}
#[test]
fn suite_default_has_generators_and_reporters() {
let suite = ConformanceSuite::new();
assert!(!suite.generators.is_empty());
assert!(!suite.reporters.is_empty());
}
#[test]
fn registry_has_required_primitives() {
const MINIMUM_EXPECTED_PRIMITIVES: usize = 35;
let specs = op_registry::all_specs();
let count = specs
.iter()
.filter(|s| s.id.starts_with("primitive."))
.count();
assert!(
count >= MINIMUM_EXPECTED_PRIMITIVES,
"expected at least {MINIMUM_EXPECTED_PRIMITIVES} primitive ops, got {count}"
);
}
#[test]
fn all_ops_have_version_gte_1() {
for op in op_registry::all_specs() {
assert!(op.version >= 1, "op {} has version 0", op.id);
}
}
#[test]
fn all_ops_have_non_empty_wgsl() {
for op in op_registry::all_specs() {
let wgsl = (op.wgsl_fn)();
assert!(
!wgsl.is_empty(),
"op {} produced empty WGSL fragment",
op.id
);
assert!(
wgsl.contains("fn "),
"op {} WGSL does not contain any function definition",
op.id
);
}
}
#[test]
fn all_ops_have_default_convention() {
for op in op_registry::all_specs() {
if op.id.starts_with("primitive.") {
assert_eq!(
op.convention,
Convention::default(),
"primitive op {} should use default convention",
op.id
);
}
}
}
#[test]
fn all_cpu_fns_handle_empty_input_gracefully() {
for op in op_registry::all_specs() {
let result = (op.cpu_fn)(&[]);
if op.signature.min_input_bytes() > 0 {
assert!(
!result.is_empty(),
"op {} returned empty on empty input",
op.id
);
}
}
}
#[test]
fn all_cpu_fns_return_correct_output_size() {
use crate::spec::types::DataType;
for op in op_registry::all_specs() {
if !op.id.starts_with("primitive.") {
continue;
}
let min_bytes = op.signature.min_input_bytes();
let input = vec![0_u8; min_bytes.max(8)];
let result = (op.cpu_fn)(&input);
match op.signature.output {
DataType::Bytes => assert!(
result.len() >= op.signature.output.min_bytes(),
"op {} returned {} bytes, minimum {} for output type {:?}",
op.id,
result.len(),
op.signature.output.min_bytes(),
op.signature.output
),
_ => assert_eq!(
result.len(),
op.signature.output.min_bytes(),
"op {} returned {} bytes, expected {} for output type {:?}",
op.id,
result.len(),
op.signature.output.min_bytes(),
op.signature.output
),
}
}
}