use super::*;
#[test]
fn plan_owns_padding_outputs_and_grid() {
let plan = plan_ifds_csr_dispatch(
2,
2,
2,
&[(0, 0, 1)],
&[(0, 1, 1, 0)],
&[(0, 0, 1)],
&[(1, 0, 0)],
)
.expect("Fix: valid IFDS CSR dispatch plan should build");
assert_eq!(plan.grid, ifds_csr_dispatch_grid(1, 8));
assert_eq!(plan.killed_words, 8);
assert_eq!(plan.intra_field_words, 1);
assert_eq!(plan.inter_field_words, 1);
assert_eq!(plan.gen_field_words, 1);
assert_eq!(plan.kill_field_words, 1);
assert_eq!(plan.row_ptr_words, 9);
assert_eq!(plan.row_cursor_words, 8);
assert_eq!(plan.col_idx_words, 5);
assert_eq!(plan.col_len_words, 1);
assert_eq!(plan.max_col_count, 5);
assert_eq!(
plan.program_cache_key(),
IfdsCsrProgramCacheKey {
num_procs: 2,
blocks_per_proc: 2,
facts_per_proc: 2,
intra_count: 1,
inter_count: 1,
gen_count: 1,
kill_count: 1,
max_col_count: 5,
}
);
assert!(!plan.layout.empty);
}
#[test]
fn empty_plan_keeps_dispatch_buffers_nonempty_without_fake_rules() {
let plan = plan_ifds_csr_dispatch(0, 0, 0, &[], &[], &[], &[])
.expect("Fix: empty no-rule IFDS dispatch plan should be representable");
assert!(plan.layout.empty);
assert_eq!(plan.intra_field_words, 1);
assert_eq!(plan.inter_field_words, 1);
assert_eq!(plan.gen_field_words, 1);
assert_eq!(plan.kill_field_words, 1);
assert_eq!(plan.row_ptr_words, 1);
assert_eq!(plan.row_cursor_words, 1);
assert_eq!(plan.col_idx_words, 1);
assert_eq!(plan.col_len_words, 1);
assert_eq!(plan.grid, IFDS_CSR_EMPTY_DISPATCH_GRID);
}
#[test]
fn dispatch_grid_scales_with_intra_count_and_total_nodes() {
let plan = plan_ifds_csr_dispatch(
2,
2,
2,
&[(0, 0, 1), (0, 1, 0), (1, 0, 1)],
&[],
&[],
&[],
)
.expect("Fix: multi-edge IFDS dispatch plan should build");
assert_eq!(plan.layout.total_nodes, 8);
assert_eq!(plan.layout.intra_count, 3);
assert!(plan.grid[0] > 1, "grid.x should reflect intra/node work");
assert_eq!(plan.grid, ifds_csr_dispatch_grid(3, 8));
assert_eq!(plan.program().workgroup_size, IFDS_CSR_WORKGROUP_SIZE);
}
#[test]
fn rule_input_fingerprint_distinguishes_same_count_rule_content() {
let base = IfdsCsrRuleInputFingerprint::from_rules(
&[(0, 0, 1)],
&[(0, 1, 1, 0)],
&[(0, 0, 1)],
&[(1, 0, 0)],
);
assert_eq!(
base,
IfdsCsrRuleInputFingerprint::from_rules(
&[(0, 0, 1)],
&[(0, 1, 1, 0)],
&[(0, 0, 1)],
&[(1, 0, 0)],
)
);
assert_ne!(
base,
IfdsCsrRuleInputFingerprint::from_rules(
&[(0, 1, 0)],
&[(0, 1, 1, 0)],
&[(0, 0, 1)],
&[(1, 0, 0)],
)
);
assert_ne!(
base,
IfdsCsrRuleInputFingerprint::from_rules(
&[(0, 0, 1)],
&[(0, 1, 1, 1)],
&[(0, 0, 1)],
&[(1, 0, 0)],
)
);
}
#[test]
fn static_input_key_combines_program_shape_and_rule_content() {
let plan = plan_ifds_csr_dispatch(1, 2, 1, &[(0, 0, 1)], &[], &[], &[])
.expect("Fix: valid IFDS dispatch plan should build");
let first = IfdsCsrRuleInputFingerprint::from_rules(&[(0, 0, 1)], &[], &[], &[]);
let changed = IfdsCsrRuleInputFingerprint::from_rules(&[(0, 1, 0)], &[], &[], &[]);
assert_eq!(plan.static_input_key(first), plan.static_input_key(first));
assert_ne!(plan.static_input_key(first), plan.static_input_key(changed));
assert_eq!(
plan.static_input_key(first).program_key,
plan.program_cache_key()
);
}
#[test]
fn readback_validator_rejects_malformed_csr_outputs() {
let plan = plan_ifds_csr_dispatch(1, 2, 1, &[(0, 0, 1)], &[], &[], &[])
.expect("Fix: valid IFDS dispatch plan should build");
let layout = &plan.layout;
assert_eq!(
validate_ifds_csr_readback(layout, &[0, 1, 1], &[1], 1)
.expect("Fix: canonical readback should validate"),
1
);
assert!(validate_ifds_csr_readback(layout, &[1, 1, 1], &[1], 1)
.expect_err("Fix: row_ptr[0] drift must be rejected")
.contains("row_ptr[0]"));
assert!(validate_ifds_csr_readback(layout, &[0, 1, 0], &[1], 1)
.expect_err("Fix: nonmonotonic row_ptr must be rejected")
.contains("not monotonic"));
assert!(validate_ifds_csr_readback(layout, &[0, 1, 1], &[2], 1)
.expect_err("Fix: out-of-domain column must be rejected")
.contains("outside total_nodes"));
}