use super::*;
use crate::driver::memory::{classify_device_memory, DeviceMemoryClass};
struct EnvVarGuard {
key: &'static str,
prior: Option<std::ffi::OsString>,
}
impl EnvVarGuard {
fn set(key: &'static str, value: &str) -> Self {
let prior = std::env::var_os(key);
std::env::set_var(key, value);
Self { key, prior }
}
}
impl Drop for EnvVarGuard {
fn drop(&mut self) {
match self.prior.take() {
Some(v) => std::env::set_var(self.key, v),
None => std::env::remove_var(self.key),
}
}
}
#[test]
fn test_alloc_oversize_100gb() {
let _exclusive = device_memory_exclusive();
let ctx = CudaContext::new(0).expect("Context");
let total_bytes = ctx.total_memory().expect("cuDeviceTotalMem MUST succeed");
let integrated = matches!(
classify_device_memory(&ctx).expect("CU_DEVICE_ATTRIBUTE_INTEGRATED MUST be readable"),
DeviceMemoryClass::UnifiedMemory
);
let oversize_bytes: usize = if integrated {
1usize << 60
} else {
total_bytes
.checked_mul(2)
.expect("2x device memory overflows usize")
};
let oversize = oversize_bytes / std::mem::size_of::<f32>();
let _env = EnvVarGuard::set("MANAGED_MEMORY", "0");
let result = GpuBuffer::<f32>::new(&ctx, oversize);
match result {
Err(GpuError::OutOfMemory { .. }) => {
}
Err(GpuError::MemoryAllocation(_)) => {
}
Err(e) => {
println!("Oversize alloc returned: {:?}", e);
}
Ok(_) => {
panic!(
"CRITICAL: allocating {} bytes (device total {} bytes, integrated={}) SUCCEEDED - \
an allocation larger than the whole device must fail",
oversize_bytes, total_bytes, integrated
);
}
}
}
#[test]
fn test_copy_from_host_too_small() {
let ctx = CudaContext::new(0).expect("Context");
let mut buf = GpuBuffer::<f32>::new(&ctx, 1000).expect("Alloc");
let small_data = vec![1.0f32; 500];
let result = buf.copy_from_host(&small_data);
assert!(
result.is_err(),
"copy_from_host should fail when host buffer is smaller"
);
if let Err(e) = result {
assert!(
format!("{:?}", e).contains("mismatch") || format!("{:?}", e).contains("Transfer"),
"Error should mention size mismatch: {:?}",
e
);
}
}
#[test]
fn test_copy_to_host_too_large() {
let ctx = CudaContext::new(0).expect("Context");
let buf = GpuBuffer::<f32>::new(&ctx, 100).expect("Alloc");
let mut large_data = vec![0.0f32; 500];
let result = buf.copy_to_host(&mut large_data);
assert!(
result.is_err(),
"copy_to_host should fail when host buffer size doesn't match"
);
}
#[test]
fn test_copy_from_host_at_out_of_bounds() {
let ctx = CudaContext::new(0).expect("Context");
let mut buf = GpuBuffer::<f32>::new(&ctx, 100).expect("Alloc");
let data = vec![1.0f32; 50];
let result = buf.copy_from_host_at(&data, 60);
assert!(
result.is_err(),
"copy_from_host_at should fail when offset+len > buffer size"
);
}
#[test]
fn test_copy_to_host_at_out_of_bounds() {
let ctx = CudaContext::new(0).expect("Context");
let data = vec![1.0f32; 100];
let buf = GpuBuffer::from_host(&ctx, &data).expect("Alloc");
let mut result = vec![0.0f32; 50];
let copy_result = buf.copy_to_host_at(&mut result, 60);
assert!(
copy_result.is_err(),
"copy_to_host_at should fail when offset+len > buffer size"
);
}
#[test]
fn test_d2d_copy_size_mismatch() {
let ctx = CudaContext::new(0).expect("Context");
let src = GpuBuffer::<f32>::new(&ctx, 100).expect("Alloc src");
let mut dst = GpuBuffer::<f32>::new(&ctx, 200).expect("Alloc dst");
let result = dst.copy_from_buffer(&src);
assert!(
result.is_err(),
"D2D copy should fail when buffer sizes don't match"
);
}
#[test]
fn test_d2d_copy_at_dst_out_of_bounds() {
let ctx = CudaContext::new(0).expect("Context");
let src = GpuBuffer::<f32>::new(&ctx, 50).expect("Alloc src");
let mut dst = GpuBuffer::<f32>::new(&ctx, 100).expect("Alloc dst");
let result = dst.copy_from_buffer_at(&src, 60, 0, 50);
assert!(
result.is_err(),
"D2D copy_at should fail when dst_offset+count > dst.len"
);
}
#[test]
fn test_d2d_copy_at_src_out_of_bounds() {
let ctx = CudaContext::new(0).expect("Context");
let src = GpuBuffer::<f32>::new(&ctx, 50).expect("Alloc src");
let mut dst = GpuBuffer::<f32>::new(&ctx, 100).expect("Alloc dst");
let result = dst.copy_from_buffer_at(&src, 0, 30, 50);
assert!(
result.is_err(),
"D2D copy_at should fail when src_offset+count > src.len"
);
}
#[test]
fn test_raii_cleanup_single_buffer() {
let ctx = CudaContext::new(0).expect("Context");
let before = device_bytes_outstanding();
let size = 25_000_000; let bytes = (size * std::mem::size_of::<f32>()) as u64;
{
let _buf = GpuBuffer::<f32>::new(&ctx, size).expect("Alloc");
assert_eq!(
device_bytes_outstanding(),
before + bytes,
"allocating {bytes} bytes must be accounted for on this thread"
);
}
assert_eq!(
device_bytes_outstanding(),
before,
"RAII leak: dropping the buffer did not release its {bytes} bytes"
);
}