use morok_dtype::DType;
use morok_ir::{AddrSpace, AxisId, AxisType, BufferizeOpts, ConstValue, Op, UOp};
use smallvec::SmallVec;
use crate::rangeify::{KernelContext, bufferize_to_store, run_kernel_split_pipeline};
use crate::test::unit::rangeify::helpers::extract_kernel;
#[test]
fn test_zero_size_range() {
let range_zero = UOp::range_axis(UOp::index_const(0), AxisId::Renumbered(0), AxisType::Loop);
assert!(matches!(range_zero.op(), Op::Range { .. }));
if let Op::Range { end, .. } = range_zero.op() {
if let Op::Const(cv) = end.op() {
assert_eq!(cv.0, ConstValue::Int(0));
} else {
panic!("Expected const end");
}
}
}
#[test]
fn test_empty_bufferize() {
let mut ctx = KernelContext::new();
let compute = UOp::native_const(42.0f32);
let bufferize = UOp::new(
Op::Bufferize {
compute: compute.clone(),
ranges: SmallVec::new(),
opts: BufferizeOpts { device: None, addrspace: AddrSpace::Global, removable: true },
},
DType::Float32,
);
let result = bufferize_to_store(&bufferize, &mut ctx, true);
assert!(result.is_some());
let result = result.unwrap();
let Op::After { passthrough, deps } = result.op() else {
panic!("Expected AFTER operation, got {:?}", result.op());
};
assert!(matches!(passthrough.op(), Op::Buffer { .. }), "Expected BUFFER, got {:?}", passthrough.op());
assert_eq!(deps.len(), 1);
let Op::Store { index, value, .. } = deps[0].op() else {
panic!("Expected STORE in AFTER deps, got {:?}", deps[0].op());
};
let _ = index;
assert!(std::sync::Arc::ptr_eq(value, &compute));
}
#[test]
fn test_zero_size_index() {
let buffer = UOp::param(0, 1, DType::Float32, None);
let index = UOp::index().buffer(buffer.clone()).indices(vec![]).call().expect("INDEX with no indices should work");
if let Op::Index { buffer: idx_buf, indices, .. } = index.op() {
assert!(std::sync::Arc::ptr_eq(idx_buf, &buffer));
assert_eq!(indices.len(), 0);
} else {
panic!("Expected INDEX operation");
}
}
#[test]
fn test_zero_size_end() {
let store = UOp::noop();
let end = store.clone().end(SmallVec::new());
assert!(std::sync::Arc::ptr_eq(&end, &store), "end(empty) should return self");
}
#[test]
fn test_zero_size_pipeline() {
let compute = UOp::native_const(0i32);
let bufferize = UOp::new(
Op::Bufferize {
compute,
ranges: SmallVec::new(),
opts: BufferizeOpts { device: None, addrspace: AddrSpace::Global, removable: true },
},
DType::Int32,
);
let (result, _context) = run_kernel_split_pipeline(bufferize);
let kernel = extract_kernel(&result).expect("Pipeline should create a KERNEL");
assert!(matches!(kernel.op(), Op::Kernel { .. }));
}
#[test]
#[should_panic(expected = "Cannot allocate buffer: range vmax resolved to")]
fn test_bufferize_with_zero_range_inside() {
let mut ctx = KernelContext::new();
let compute = UOp::native_const(1.0f32);
let range_zero = UOp::range_const(0, 0);
let bufferize = UOp::new(
Op::Bufferize {
compute: compute.clone(),
ranges: smallvec::smallvec![range_zero.clone()],
opts: BufferizeOpts { device: None, addrspace: AddrSpace::Global, removable: true },
},
DType::Float32,
);
let _result = bufferize_to_store(&bufferize, &mut ctx, true);
}
#[test]
#[should_panic(expected = "Cannot allocate buffer: range vmax resolved to")]
fn test_multiple_zero_ranges() {
let mut ctx = KernelContext::new();
let compute = UOp::native_const(true);
let range1 = UOp::range_const(0, 0);
let range2 = UOp::range_const(0, 1);
let bufferize = UOp::new(
Op::Bufferize {
compute,
ranges: smallvec::smallvec![range1.clone(), range2.clone()],
opts: BufferizeOpts { device: None, addrspace: AddrSpace::Local, removable: true },
},
DType::Bool,
);
let _result = bufferize_to_store(&bufferize, &mut ctx, true);
}