weirflow 0.1.0

GPU-first dataflow analysis primitives for Vyre and Santh compiler pipelines.
Documentation
use super::*;

// ------------------------------------------------------------------
// 16. Boundary / edge / empty problem
// ------------------------------------------------------------------

#[test]
fn ifds_vyre_resident_empty_problem_zero_resources_dispatch() {
    let b = backend();
    let a = adapter(&b);
    a.dispatch_resident(&program(), &[], None).unwrap();
    assert_eq!(b.dispatch_count(), 1);
    assert_eq!(b.take_dispatches()[0].resource_handles.len(), 0);
}

#[test]
fn ifds_vyre_resident_empty_problem_zero_uploads() {
    let b = backend();
    let a = adapter(&b);
    a.upload_resident_many(&[]).unwrap();
    a.upload_resident_at_many(&[]).unwrap();
    assert_eq!(b.upload_count(), 0);
}

#[test]
fn ifds_vyre_resident_empty_problem_zero_downloads() {
    let b = backend();
    let a = adapter(&b);
    let mut outputs: Vec<&mut Vec<u8>> = vec![];
    a.download_resident_ranges_into(&[], &mut outputs).unwrap();
}

#[test]
fn ifds_vyre_resident_empty_problem_zero_alloc_free() {
    let b = backend();
    // No allocations, nothing to free
    assert_eq!(b.alloc_count(), 0);
    assert!(b.alive_resources().is_empty());
}

#[test]
fn ifds_vyre_resident_oversized_dimension_alloc_large() {
    let b = backend();
    let a = adapter(&b);
    let r = a.allocate_resident(16_777_216).unwrap();
    assert_eq!(b.alloc_count(), 1);
    a.free_resident(r).unwrap();
}

#[test]
fn ifds_vyre_resident_boundary_upload_at_exact_end() {
    let b = backend();
    let a = adapter(&b);
    let r = a.allocate_resident(64).unwrap();
    a.upload_resident_at_many(&[(&r, 63, &[0u8; 1])]).unwrap();
}

#[test]
fn ifds_vyre_resident_boundary_download_range_exact_end() {
    let b = backend();
    let a = adapter(&b);
    let r = a.allocate_resident(64).unwrap();
    let data = a.download_resident_range(&r, 56, 8).unwrap();
    assert_eq!(data.len(), 8);
}

#[test]
fn ifds_vyre_resident_boundary_download_range_zero_at_end() {
    let b = backend();
    let a = adapter(&b);
    let r = a.allocate_resident(64).unwrap();
    let data = a.download_resident_range(&r, 64, 0).unwrap();
    assert!(data.is_empty());
}

#[test]
fn ifds_vyre_resident_mixed_simple_and_complex_paths() {
    let b = backend();
    let a = adapter(&b);
    // Simple path: single resource, direct dispatch
    let r1 = a.allocate_resident(64).unwrap();
    a.upload_resident(&r1, &[1u8; 32]).unwrap();
    a.dispatch_resident(&program(), &[r1.clone()], None)
        .unwrap();

    // Complex path: multiple resources, sequence, ranges
    let r2 = a.allocate_resident(64).unwrap();
    let r3 = a.allocate_resident(64).unwrap();
    a.upload_resident_many(&[(&r2, &[2u8; 8]), (&r3, &[3u8; 8])])
        .unwrap();
    let mut out = Vec::new();
    a.dispatch_resident_sequence_read_ranges_into(
        &[(&program(), &[r2.clone(), r3.clone()], Some([4, 1, 1]))],
        &[(&r2, 0, 8)],
        &mut [&mut out],
    )
    .unwrap();

    a.free_resident(r1).unwrap();
    a.free_resident(r2).unwrap();
    a.free_resident(r3).unwrap();
    assert!(b.alive_resources().is_empty());
}

// ------------------------------------------------------------------
// 17. Resource leak detection
// ------------------------------------------------------------------

#[test]
fn ifds_vyre_resident_no_leak_single() {
    let b = backend();
    let a = adapter(&b);
    let r = a.allocate_resident(64).unwrap();
    a.free_resident(r).unwrap();
    assert!(b.alive_resources().is_empty());
}

#[test]
fn ifds_vyre_resident_no_leak_ten_resources() {
    let b = backend();
    let a = adapter(&b);
    let mut resources = Vec::new();
    for _ in 0..10 {
        resources.push(a.allocate_resident(64).unwrap());
    }
    for r in resources {
        a.free_resident(r).unwrap();
    }
    assert!(b.alive_resources().is_empty());
}

#[test]
fn ifds_vyre_resident_no_leak_after_sequence_complex() {
    let b = backend();
    let a = adapter(&b);
    let r1 = a.allocate_resident(64).unwrap();
    let r2 = a.allocate_resident(64).unwrap();
    let p = program();
    let mut out1 = Vec::new();
    let mut out2 = Vec::new();
    a.dispatch_resident_sequence_read_ranges_into(
        &[(&p, &[r1.clone()], None), (&p, &[r2.clone()], None)],
        &[(&r1, 0, 8), (&r2, 0, 8)],
        &mut [&mut out1, &mut out2],
    )
    .unwrap();
    a.free_resident(r1).unwrap();
    a.free_resident(r2).unwrap();
    assert!(b.alive_resources().is_empty());
}

#[test]
fn ifds_vyre_resident_no_leak_after_repeated_complex() {
    let b = backend();
    let a = adapter(&b);
    let r = a.allocate_resident(64).unwrap();
    let p = program();
    let mut out = Vec::new();
    a.dispatch_resident_repeated_sequence_read_ranges_into(
        &[(&p, &[r.clone()], None)],
        &[(&p, &[r.clone()], None)],
        3,
        &[(&r, 0, 8)],
        &mut [&mut out],
    )
    .unwrap();
    a.free_resident(r).unwrap();
    assert!(b.alive_resources().is_empty());
}

#[test]
fn ifds_vyre_resident_alive_after_partial_free() {
    let b = backend();
    let a = adapter(&b);
    let r1 = a.allocate_resident(64).unwrap();
    let r2 = a.allocate_resident(64).unwrap();
    let r3 = a.allocate_resident(64).unwrap();
    a.free_resident(r1).unwrap();
    assert_eq!(b.alive_resources().len(), 2);
    a.free_resident(r2).unwrap();
    a.free_resident(r3).unwrap();
    assert!(b.alive_resources().is_empty());
}

#[test]
fn ifds_vyre_resident_no_leak_many_allocs_frees() {
    let b = backend();
    let a = adapter(&b);
    for _ in 0..50 {
        let r = a.allocate_resident(64).unwrap();
        a.upload_resident(&r, &[0u8; 32]).unwrap();
        a.free_resident(r).unwrap();
    }
    assert!(b.alive_resources().is_empty());
    assert_eq!(b.alloc_count(), 50);
    assert_eq!(b.free_count(), 50);
}

// ------------------------------------------------------------------
// 18. Upload/download at_many boundary combinations
// ------------------------------------------------------------------

macro_rules! at_many_boundary_test {
    ($name:ident, $alloc:expr, $offset:expr, $len:expr, $should_ok:expr) => {
        #[test]
        fn $name() {
            let b = backend();
            let a = adapter(&b);
            let r = a.allocate_resident($alloc).unwrap();
            let result = a.upload_resident_at_many(&[(&r, $offset, &vec![0u8; $len])]);
            if $should_ok {
                result.unwrap();
            } else {
                assert!(result.is_err());
            }
        }
    };
}

at_many_boundary_test!(ifds_vyre_resident_upload_at_boundary_0, 64, 0, 64, true);
at_many_boundary_test!(ifds_vyre_resident_upload_at_boundary_1, 64, 1, 63, true);
at_many_boundary_test!(ifds_vyre_resident_upload_at_boundary_mid, 64, 32, 32, true);
at_many_boundary_test!(
    ifds_vyre_resident_upload_at_boundary_overflow_1,
    64,
    64,
    1,
    false
);
at_many_boundary_test!(
    ifds_vyre_resident_upload_at_boundary_overflow_mid,
    64,
    60,
    8,
    false
);

macro_rules! download_range_boundary_test {
    ($name:ident, $alloc:expr, $offset:expr, $len:expr, $should_ok:expr) => {
        #[test]
        fn $name() {
            let b = backend();
            let a = adapter(&b);
            let r = a.allocate_resident($alloc).unwrap();
            let result = a.download_resident_range(&r, $offset, $len);
            if $should_ok {
                result.unwrap();
            } else {
                assert!(result.is_err());
            }
        }
    };
}

download_range_boundary_test!(
    ifds_vyre_resident_download_range_boundary_0,
    64,
    0,
    64,
    true
);
download_range_boundary_test!(
    ifds_vyre_resident_download_range_boundary_1,
    64,
    1,
    63,
    true
);
download_range_boundary_test!(
    ifds_vyre_resident_download_range_boundary_mid,
    64,
    32,
    32,
    true
);
download_range_boundary_test!(
    ifds_vyre_resident_download_range_boundary_overflow_1,
    64,
    64,
    1,
    false
);
download_range_boundary_test!(
    ifds_vyre_resident_download_range_boundary_overflow_mid,
    64,
    60,
    8,
    false
);