#![cfg(feature = "dynamic")]
use edgefirst_tensor::{
CpuAccess, DType, Error, PixelFormat, Quantization, Region, Tensor, TensorDyn, TensorMapTrait,
TensorMemory, TensorTrait,
};
fn bare_u8(shape: &[usize]) -> Tensor<u8> {
Tensor::<u8>::new(shape, None, None).expect("bare tensor allocation")
}
#[cfg(unix)]
fn platform_resource_absent(err: &Error) -> bool {
match err {
Error::IoError(e) => {
matches!(
e.kind(),
std::io::ErrorKind::NotFound | std::io::ErrorKind::PermissionDenied
) || e.to_string().contains("Permission denied")
|| e.to_string().contains("No such file or directory")
}
Error::NotImplemented(msg) => {
msg.contains("Permission denied") || msg.contains("errno 13") || msg.contains("errno 2")
}
_ => false,
}
}
#[test]
fn family1_set_format_set_row_stride_and_set_plane_offset_take_effect() {
let (w, h) = (64usize, 48usize);
let mut t: TensorDyn = bare_u8(&[h + h / 2, w]).into();
assert_eq!(t.format(), None);
t.set_format(PixelFormat::Nv12)
.expect("set_format must succeed against a real ef_tensor_set_format primitive");
assert_eq!(t.format(), Some(PixelFormat::Nv12));
t.set_row_stride(128)
.expect("set_row_stride must succeed against a real ef_tensor_set_row_stride primitive");
assert_eq!(
t.effective_row_stride(),
Some(128),
"the padded stride must be visible back through the handle"
);
assert_eq!(t.plane_offset(), None);
t.set_plane_offset(4096);
assert_eq!(
t.plane_offset(),
Some(4096),
"set_plane_offset must be visible back through the handle, not a silent no-op"
);
}
#[test]
fn family2_quantization_reads_back_metadata_set_by_another_producer() {
let handle = unsafe {
let b = edgefirst_tensor_ffi::ef_tensor_builder_new();
assert!(!b.is_null());
let dims = [4u64, 4];
assert_eq!(
edgefirst_tensor_ffi::ef_tensor_builder_dtype(b, DType::U8.code()),
0
);
assert_eq!(
edgefirst_tensor_ffi::ef_tensor_builder_shape(b, dims.as_ptr(), 2),
0
);
assert_eq!(edgefirst_tensor_ffi::ef_tensor_builder_storage(b, 0), 0); let scales = [0.1f32, 0.2, 0.3, 0.4];
let zps = [1i32, -2, 3, -4];
assert_eq!(
edgefirst_tensor_ffi::ef_tensor_builder_quantization(
b,
1,
scales.as_ptr(),
zps.as_ptr(),
4
),
0
);
let raw = edgefirst_tensor_ffi::ef_tensor_builder_alloc(b);
edgefirst_tensor_ffi::ef_tensor_builder_free(b);
assert!(!raw.is_null(), "producer-side allocation must succeed");
TensorDyn::from_raw(raw)
};
let typed = handle
.as_typed::<u8>()
.expect("the handle's dtype must be u8");
let q = typed.quantization().expect(
"quantization set by another producer via ef_tensor_builder_quantization must \
be readable back through Tensor::quantization -- the old stub panicked here \
unconditionally",
);
assert_eq!(q.axis(), Some(1));
assert_eq!(q.scale(), &[0.1, 0.2, 0.3, 0.4]);
assert_eq!(q.zero_point(), Some(&[1, -2, 3, -4][..]));
let mut own = bare_u8(&[4, 4]);
assert_eq!(own.quantization(), None);
let per_tensor = Quantization::per_tensor(0.5, -7);
own.set_quantization(per_tensor.clone()).expect(
"set_quantization must succeed against a real ef_tensor_quantization_set primitive",
);
assert_eq!(own.quantization(), Some(&per_tensor));
own.clear_quantization();
assert_eq!(
own.quantization(),
None,
"clear_quantization must be visible back through the handle"
);
}
#[test]
fn family3_configure_image_reconfigures_an_oversized_pool_tensor() {
let mut pool = bare_u8(&[128 + 128 / 2, 128]);
pool.configure_image(64, 64, PixelFormat::Grey)
.expect("configure_image must succeed against a real ef_tensor_configure_image primitive");
assert_eq!(pool.format(), Some(PixelFormat::Grey));
assert_eq!(pool.width(), Some(64));
assert_eq!(pool.height(), Some(64));
}
#[test]
fn family4_image_allocates_a_real_tensor() {
let img = Tensor::<u8>::image(64, 48, PixelFormat::Grey, None, CpuAccess::None)
.expect("image must succeed against a real ef_tensor_image_alloc primitive");
assert_eq!(img.format(), Some(PixelFormat::Grey));
assert_eq!(img.width(), Some(64));
assert_eq!(img.height(), Some(48));
}
#[test]
fn family4_image_desc_allocates_from_a_declarative_request() {
let desc = edgefirst_tensor::ImageDesc::new(64, 48, PixelFormat::Rgb, DType::U8);
let img = Tensor::<u8>::image_desc(&desc)
.expect("image_desc must succeed against a real ef_tensor_image_desc_alloc primitive");
assert_eq!(img.format(), Some(PixelFormat::Rgb));
assert_eq!(img.width(), Some(64));
assert_eq!(img.height(), Some(48));
}
#[test]
fn family4_view_region_is_a_real_zero_copy_window_and_is_bounds_checked() {
let parent = Tensor::<u8>::image(
8,
8,
PixelFormat::Grey,
Some(TensorMemory::Mem),
CpuAccess::ReadWrite,
)
.expect("parent image allocation");
{
let mut m = parent.map_with(CpuAccess::ReadWrite).expect("map parent");
m.as_mut_slice().fill(0);
}
let view = parent
.view(Region::new(0, 0, 4, 4))
.expect("view must succeed against a real ef_tensor_view_region primitive");
{
let mut m = view.map_with(CpuAccess::ReadWrite).expect("map view");
m.as_mut_slice()[0] = 0xAB;
}
{
let m = parent.map_with(CpuAccess::Read).expect("re-map parent");
assert_eq!(
m.as_slice()[0],
0xAB,
"a write through the view must be visible through the parent -- \
ef_tensor_view_region must share the parent's real storage, not a fresh copy"
);
}
let out_of_bounds = parent.view(Region::new(0, 0, 1000, 1000));
assert!(
matches!(out_of_bounds, Err(Error::RegionOutOfBounds { .. })),
"an out-of-bounds region must be rejected, not silently clamped: got {out_of_bounds:?}"
);
}
#[test]
fn family5_from_planes_combines_two_independently_allocated_tensors() {
let (w, h) = (64usize, 48usize);
let luma = bare_u8(&[h, w]);
let chroma = bare_u8(&[h / 2, w]);
let combined = Tensor::<u8>::from_planes(luma, chroma, PixelFormat::Nv12)
.expect("from_planes must succeed against a real ef_tensor_from_planes primitive");
assert_eq!(combined.format(), Some(PixelFormat::Nv12));
}
#[test]
fn family5_from_planes_is_genuinely_multiplane_and_chroma_is_independently_writable() {
let (w, h) = (64usize, 48usize);
let luma = bare_u8(&[h, w]);
let chroma = bare_u8(&[h / 2, w]);
let combined = Tensor::<u8>::from_planes(luma, chroma, PixelFormat::Nv12)
.expect("from_planes must succeed");
assert!(
!combined.is_multiplane(),
"a Mem-backed chroma plane has no fd to shadow -- must degrade to the pre-task-17 \
answer, not panic or silently misreport a `Some`"
);
}
#[test]
fn dma_multiplane_from_planes_makes_is_multiplane_and_chroma_honest() {
if !edgefirst_tensor::is_dma_available() {
eprintln!(
"SKIPPED: dma_multiplane_from_planes_makes_is_multiplane_and_chroma_honest - \
DMA not available"
);
return;
}
let (w, h) = (64usize, 48usize);
let luma =
Tensor::<u8>::new(&[h, w], Some(TensorMemory::DmaBuf), None).expect("dma luma allocation");
let chroma = Tensor::<u8>::new(&[h / 2, w], Some(TensorMemory::DmaBuf), None)
.expect("dma chroma allocation");
let mut combined = Tensor::<u8>::from_planes(luma, chroma, PixelFormat::Nv12)
.expect("from_planes must succeed against a real ef_tensor_from_planes primitive");
assert!(
combined.is_multiplane(),
"a DMA-backed from_planes tensor is genuinely two allocations -- is_multiplane() must \
say so, not silently take the combined-plane path a real caller would misread chroma \
bytes through"
);
{
let chroma_ref = combined
.chroma_mut()
.expect("chroma_mut() must return the shadow this from_planes call built");
chroma_ref.set_row_stride_unchecked(w + 16);
}
assert_eq!(
combined.chroma().and_then(|c| c.effective_row_stride()),
Some(w + 16),
"the stride set through chroma_mut() must read back through chroma() -- this is the \
exact round trip edgefirst-image's import_image relies on"
);
}
#[test]
fn dma_buffer_identity_is_derived_from_the_inode_not_the_handle_address() {
if !edgefirst_tensor::is_dma_available() {
eprintln!(
"SKIPPED: dma_buffer_identity_is_derived_from_the_inode_not_the_handle_address \
- DMA not available"
);
return;
}
let t = Tensor::<u8>::new(&[4096], Some(TensorMemory::DmaBuf), None)
.expect("dma tensor allocation");
assert_eq!(
t.buffer_identity().kind(),
edgefirst_tensor::IdentityKind::DmaBuf,
"a DMA-BUF handle's identity must be inode-derived, not the process-local HostPtr \
fallback -- see TensorDyn::derive_identity's doc comment"
);
}
#[cfg(target_os = "windows")]
#[test]
fn a_texture_handles_identity_names_its_texture_not_its_recyclable_handle_address() {
use std::collections::HashSet;
const ITERATIONS: usize = 200;
for format in [PixelFormat::Rgba, PixelFormat::Rgb] {
match Tensor::<u8>::image(
64,
48,
format,
Some(TensorMemory::DmaBuf),
CpuAccess::ReadWrite,
) {
Ok(t) if t.d3d11_texture().is_some() => drop(t),
other => {
eprintln!(
"SKIPPED: \
a_texture_handles_identity_names_its_texture_not_its_recyclable_handle_address \
- no D3D11 {format:?} texture tensors on this host ({:?})",
other.map(|t| t.memory())
);
return;
}
}
}
let expected_id =
|tex: usize| ((edgefirst_tensor::IdentityKind::D3d11Texture as u64) << 56) ^ (tex as u64);
let mut ids = HashSet::new();
let mut recycled = 0usize;
for i in 0..ITERATIONS {
let mut pair = Vec::new();
for format in [PixelFormat::Rgba, PixelFormat::Rgb] {
let t = Tensor::<u8>::image(
64,
48,
format,
Some(TensorMemory::DmaBuf),
CpuAccess::ReadWrite,
)
.expect("image allocation");
let tex = t.d3d11_texture().expect("a texture-backed handle") as usize;
let identity = t.buffer_identity();
assert_eq!(
identity.kind(),
edgefirst_tensor::IdentityKind::D3d11Texture,
"iteration {i} {format:?}: a texture handle's identity must name its \
ID3D11Texture2D, not the process-local HostPtr fallback over the handle's \
own address -- an EGLImage cache keyed on that address serves the previous \
texture's image and the convert leaves the destination blank"
);
assert_eq!(
identity.id(),
expected_id(tex),
"iteration {i} {format:?}: the identity must be the texture pointer keyed as \
D3d11Texture, the same value the static backend's tex_key derives, so one \
texture has one identity whichever backend names it"
);
if !ids.insert(identity.id()) {
recycled += 1;
}
pair.push(t);
}
assert_ne!(
pair[0].buffer_identity().id(),
pair[1].buffer_identity().id(),
"iteration {i}: two simultaneously live textures must not share an identity"
);
}
eprintln!(
"{ITERATIONS} texture pairs: {} distinct identities over {} tensors, \
{recycled} landed on an address a dropped texture had used",
ids.len(),
ITERATIONS * 2
);
}
#[test]
fn two_distinct_views_of_one_dma_parent_share_one_identity_and_do_not_collide_with_an_unrelated_buffer(
) {
if !edgefirst_tensor::is_dma_available() {
eprintln!("SKIPPED: two_distinct_views_of_one_dma_parent... - DMA not available");
return;
}
let (w, h) = (64usize, 32usize);
let parent = Tensor::<u8>::image(
w,
h,
PixelFormat::Grey,
Some(TensorMemory::DmaBuf),
CpuAccess::ReadWrite,
)
.expect("dma parent image allocation");
let view_a = parent
.view(Region::new(0, 0, w, h / 2))
.expect("view_a must succeed");
let view_b = parent
.view(Region::new(0, h / 2, w, h / 2))
.expect("view_b must succeed");
let parent_id = parent.buffer_identity().id();
let a_id = view_a.buffer_identity().id();
let b_id = view_b.buffer_identity().id();
assert_eq!(parent_id, a_id, "view_a must share its parent's identity");
assert_eq!(parent_id, b_id, "view_b must share its parent's identity");
assert_eq!(
a_id, b_id,
"two sibling views of ONE parent must resolve to the SAME identity as each other, not \
just each individually matching the parent -- a per-view address-derived identity \
would fail exactly this check while still passing a parent-only comparison"
);
assert!(
parent.aliases(&view_a) && parent.aliases(&view_b) && view_a.aliases(&view_b),
"aliases() must agree with buffer_identity() for every pair"
);
let unrelated = Tensor::<u8>::new(&[w * h], Some(TensorMemory::DmaBuf), None)
.expect("unrelated dma allocation");
assert_ne!(
parent_id,
unrelated.buffer_identity().id(),
"a genuinely different DMA-BUF must not collide with the parent's identity"
);
}
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
struct MockPboOps {
storage: Mutex<Vec<u8>>,
maps: AtomicUsize,
unmaps: AtomicUsize,
deletes: AtomicUsize,
}
impl MockPboOps {
fn new(size: usize) -> Arc<Self> {
Arc::new(Self {
storage: Mutex::new(vec![0u8; size]),
maps: AtomicUsize::new(0),
unmaps: AtomicUsize::new(0),
deletes: AtomicUsize::new(0),
})
}
}
unsafe impl edgefirst_tensor::PboOps for MockPboOps {
fn map_buffer(
&self,
_buffer_id: u32,
size: usize,
) -> edgefirst_tensor::Result<edgefirst_tensor::PboMapping> {
self.maps.fetch_add(1, Ordering::AcqRel);
let storage = self.storage.lock().expect("lock");
assert_eq!(storage.len(), size, "mock PBO size mismatch");
Ok(edgefirst_tensor::PboMapping {
ptr: storage.as_ptr() as *mut u8,
size,
})
}
fn unmap_buffer(&self, _buffer_id: u32) -> edgefirst_tensor::Result<()> {
self.unmaps.fetch_add(1, Ordering::AcqRel);
Ok(())
}
fn delete_buffer(&self, _buffer_id: u32) {
self.deletes.fetch_add(1, Ordering::AcqRel);
}
}
#[test]
fn dynamic_pbo_wraps_and_reads_back_through_the_typed_tensor() {
let ops = MockPboOps::new(16);
let pbo = edgefirst_tensor::PboTensor::<u8>::from_pbo(7, 16, &[4, 4, 1], Some("t18_pbo"), ops)
.expect("PboTensor::from_pbo");
let mut tensor = Tensor::<u8>::from_pbo(pbo).expect("Tensor::from_pbo must allocate cleanly");
assert_eq!(
TensorTrait::memory(&tensor),
TensorMemory::Pbo,
"a PBO-backed dynamic tensor must report TensorMemory::Pbo, not the metadata handle's \
own (host) storage kind"
);
tensor
.set_format(PixelFormat::Grey)
.expect("set_format on a PBO-backed tensor (real edgefirst-image call shape)");
{
let pbo_ref = tensor
.as_pbo()
.expect("as_pbo must recover the wrapped PboTensor");
assert_eq!(
pbo_ref.buffer_id(),
7,
"as_pbo must preserve the real GL buffer id"
);
let mut map = pbo_ref.map().expect("PboTensor::map through the mock ops");
map.as_mut_slice().fill(0xEE);
}
let map = tensor
.as_pbo()
.expect("as_pbo must still resolve after the write")
.map()
.expect("second PboTensor::map");
assert!(
map.as_slice().iter().all(|&b| b == 0xEE),
"the byte written through the first PBO map must be visible through a fresh one"
);
let dyn_tensor: TensorDyn = tensor.into();
assert!(
dyn_tensor
.as_u8()
.expect("as_u8 on the erased PBO tensor")
.as_pbo()
.is_some(),
"as_pbo must still resolve after erasing back to TensorDyn and downcasting again"
);
}
struct MockCudaGlOps;
impl edgefirst_tensor::CudaGlOps for MockCudaGlOps {
fn map(&self, _resource: *mut std::ffi::c_void) -> Option<(*mut std::ffi::c_void, usize)> {
None
}
fn unmap(&self, _resource: *mut std::ffi::c_void) {}
fn unregister(&self, _resource: *mut std::ffi::c_void) {}
}
#[test]
fn dynamic_set_cuda_handle_is_read_back_through_cuda() {
let mut tensor = bare_u8(&[4, 4]);
assert!(
tensor.cuda().is_none(),
"a tensor with no attached CUDA handle must report None"
);
let handle = edgefirst_tensor::CudaHandle::new_gl(
std::ptr::null_mut(),
16,
Arc::new(MockCudaGlOps) as Arc<dyn edgefirst_tensor::CudaGlOps>,
);
tensor.set_cuda_handle(handle);
assert!(
tensor.cuda().is_some(),
"set_cuda_handle must make cuda() return Some -- this was unconditionally None before \
this task, and set_cuda_handle did not exist at all"
);
}
#[test]
fn dynamic_typed_downcast_family_matches_dtype_and_rejects_mismatch() {
let t: Tensor<f32> = Tensor::new(&[4], None, None).expect("f32 alloc");
let d: TensorDyn = t.into();
assert_eq!(d.dtype(), DType::F32);
assert!(d.as_f32().is_some(), "f32 lens must open on an f32 tensor");
assert!(d.as_u8().is_none(), "u8 lens must refuse an f32 tensor");
assert!(d.as_i8().is_none(), "i8 lens must refuse an f32 tensor");
assert!(d.as_f16().is_none(), "f16 lens must refuse an f32 tensor");
assert!(d.as_i16().is_none(), "i16 lens must refuse an f32 tensor");
let d = match d.into_u8() {
Ok(_) => panic!("into_u8 on an f32 tensor must not succeed"),
Err(d) => d,
};
let f32_tensor = d
.into_f32()
.expect("into_f32 must succeed on the f32 tensor it started as");
assert_eq!(f32_tensor.shape(), &[4]);
}
#[test]
fn from_pbo_metadata_handle_allocation_cost_is_the_full_pbo_byte_count() {
let (w, h, c) = (3840usize, 2160usize, 4usize); let expected_bytes = w * h * c * std::mem::size_of::<half::f16>();
assert_eq!(
expected_bytes, 66_355_200,
"sanity check on the 4K RGBA16F byte count itself"
);
let metadata_handle = TensorDyn::new(&[h, w, c], DType::F16, Some(TensorMemory::Mem), None)
.expect("the same allocation from_pbo performs for a real PBO of this shape");
let mapped = metadata_handle
.map_bytes(CpuAccess::Read)
.expect("a genuine TensorMemory::Mem handle must be host-mappable");
assert_eq!(
mapped.len(),
expected_bytes,
"from_pbo's metadata handle allocates the FULL pbo byte count as real host memory -- \
confirmed by task-18's review (F32); not a small metadata-only footprint, and not yet \
fixable without a new ef_tensor_* primitive (see this test's own doc comment)"
);
}
#[test]
fn capacity_bytes_reports_the_allocation_not_the_logical_size() {
let t = Tensor::<u8>::image(
295,
175,
PixelFormat::Nv12,
Some(TensorMemory::Mem),
CpuAccess::ReadWrite,
)
.expect("Mem-backed NV12 allocation");
let dyn_t: TensorDyn = t.into();
let logical = dyn_t.size();
let allocated = dyn_t.capacity_bytes();
assert_eq!(
logical,
dyn_t.shape().iter().product::<usize>(),
"size() is the shape's byte footprint for a u8 tensor"
);
assert!(
allocated > logical,
"an odd-width NV12 image pads its rows past the logical shape; \
capacity_bytes must report the padded allocation ({allocated}) not \
the logical size ({logical})"
);
}
#[test]
fn row_stride_is_the_recorded_pitch_and_effective_row_stride_is_not() {
let mut t: TensorDyn = bare_u8(&[48, 64, 1]).into();
t.set_format(PixelFormat::Grey).expect("set_format");
assert_eq!(
t.row_stride(),
None,
"a freshly-allocated tight tensor has no recorded row stride"
);
assert_eq!(
t.effective_row_stride(),
Some(64),
"...but its effective pitch is still the computed one -- if row_stride \
forwarded to this, a tight tensor's descriptor would claim 64 is required"
);
t.set_row_stride(128).expect("set_row_stride");
assert_eq!(t.row_stride(), Some(128), "the recorded pitch reads back");
assert_eq!(
t.effective_row_stride(),
Some(128),
"and so does the effective one"
);
}
#[test]
fn compression_reports_linear_for_an_ordinary_allocation() {
let t: TensorDyn = bare_u8(&[4, 4]).into();
assert_eq!(t.compression(), None);
}
#[test]
fn sync_brackets_succeed_on_host_memory_and_are_refused_by_a_pbo() {
let host: TensorDyn = bare_u8(&[4, 4]).into();
host.sync_for_cpu(CpuAccess::ReadWrite)
.expect("host memory is coherent");
host.sync_for_device(CpuAccess::ReadWrite)
.expect("and so is the release");
let ops = MockPboOps::new(16);
let pbo = edgefirst_tensor::PboTensor::<u8>::from_pbo(9, 16, &[4, 4, 1], None, ops)
.expect("PboTensor::from_pbo");
let pbo_t: TensorDyn = Tensor::<u8>::from_pbo(pbo)
.expect("Tensor::from_pbo")
.into();
match pbo_t.sync_for_cpu(CpuAccess::Read) {
Err(Error::NotImplemented(msg)) => assert!(
msg.contains("PBO") || msg.contains("glMapBufferRange"),
"the refusal must name the reason, got: {msg}"
),
other => panic!("a PBO has no sync window independent of its map, got: {other:?}"),
}
}
#[test]
fn sync_refuses_the_non_directional_access() {
let t: TensorDyn = bare_u8(&[4, 4]).into();
assert!(matches!(
t.sync_for_cpu(CpuAccess::None),
Err(Error::InvalidArgument(_))
));
assert!(matches!(
t.sync_for_device(CpuAccess::None),
Err(Error::InvalidArgument(_))
));
}
#[test]
fn batch_is_a_zero_copy_window_on_the_leading_dimension() {
let parent = bare_u8(&[4, 2, 3]);
let parent_dyn: TensorDyn = parent.into();
let element = parent_dyn.batch(2).expect("batch(2) of a 4-element batch");
assert_eq!(element.shape(), &[2, 3], "the leading dimension is dropped");
{
let mut m = element
.as_u8()
.expect("as_u8")
.map_with(CpuAccess::ReadWrite)
.expect("map the batch element");
m.as_mut_slice().fill(0x5A);
}
let m = parent_dyn
.as_u8()
.expect("as_u8")
.map_with(CpuAccess::Read)
.expect("map the parent");
let bytes = m.as_slice();
assert!(
bytes[12..18].iter().all(|&b| b == 0x5A),
"the write through batch(2) must land in the parent's own buffer"
);
assert!(
bytes[..12].iter().all(|&b| b == 0),
"and must not touch the elements before it"
);
}
#[test]
fn batch_rejects_an_index_past_the_leading_dimension() {
let t: TensorDyn = bare_u8(&[4, 2, 3]).into();
match t.batch(4) {
Err(Error::BatchIndexOutOfBounds { index: 4, batch: 4 }) => {}
other => {
panic!("index 4 of a 4-element batch must be refused as out of bounds, got: {other:?}")
}
}
match t.batch(usize::MAX) {
Err(Error::BatchIndexOutOfBounds { index, batch: 4 }) if index == usize::MAX => {}
other => panic!("a wild index must be refused as out of bounds, got: {other:?}"),
}
}
#[test]
fn batch_on_a_non_batched_tensor_names_that_and_not_the_index() {
let img = Tensor::<u8>::image(
640,
480,
PixelFormat::Nv12,
Some(TensorMemory::Mem),
CpuAccess::ReadWrite,
)
.expect("Mem-backed NV12 allocation");
let dyn_img: TensorDyn = img.into();
match dyn_img.batch(0) {
Err(Error::InvalidShape(msg)) => assert!(
msg.contains("not batched"),
"the refusal must carry the producing side's own diagnosis, got: {msg}"
),
Err(Error::BatchIndexOutOfBounds { index, batch }) => panic!(
"index {index} IS within a leading dimension of {batch} -- reporting it as \
out of bounds is a false diagnosis, not merely an unhelpful one"
),
other => panic!("expected the not-batched shape refusal, got: {other:?}"),
}
}
#[test]
fn the_two_batch_refusals_arrive_as_two_different_typed_errors() {
let batched: TensorDyn = bare_u8(&[4, 2, 3]).into();
match batched.batch(9) {
Err(Error::BatchIndexOutOfBounds { index: 9, batch: 4 }) => {}
other => panic!("an out-of-range index must keep its own typed variant, got: {other:?}"),
}
let img = Tensor::<u8>::image(
640,
480,
PixelFormat::Nv12,
Some(TensorMemory::Mem),
CpuAccess::ReadWrite,
)
.expect("Mem-backed NV12 allocation");
let dyn_img: TensorDyn = img.into();
match dyn_img.batch(0) {
Err(Error::InvalidShape(msg)) => assert!(
msg.contains("not batched"),
"the refusal must carry the producing side's own diagnosis, got: {msg}"
),
other => panic!("expected the not-batched shape refusal, got: {other:?}"),
}
}
#[test]
fn view_region_distinguishes_out_of_bounds_from_every_other_refusal() {
let img = Tensor::<u8>::image(
64,
48,
PixelFormat::Rgb,
Some(TensorMemory::Mem),
CpuAccess::ReadWrite,
)
.expect("Mem-backed RGB allocation");
let dyn_img: TensorDyn = img.into();
match dyn_img.view(Region::new(0, 0, 4096, 4096)) {
Err(Error::RegionOutOfBounds { bounds, .. }) => assert_eq!(
bounds,
(64, 48),
"the reported frame must be the tensor's real one"
),
other => panic!("a region past the frame is out of bounds, got: {other:?}"),
}
let bare: TensorDyn = bare_u8(&[8, 8]).into();
if let Err(Error::RegionOutOfBounds { bounds, .. }) = bare.view(Region::new(0, 0, 2, 2)) {
panic!(
"a tensor with no pixel format has no frame; reporting bounds {bounds:?} \
invents one"
);
}
}
#[cfg(target_os = "linux")]
#[test]
fn dmabuf_clone_refuses_host_memory_and_dups_a_real_dma_fd() {
let host: TensorDyn = bare_u8(&[4, 4]).into();
match host.dmabuf_clone() {
Err(Error::NotImplemented(msg)) => assert!(
msg.contains("dmabuf_clone requires DMA-backed tensor") && msg.contains("Mem"),
"the refusal must name the operation and the actual backing, got: {msg}"
),
other => panic!("host memory has no dma-buf fd, got: {other:?}"),
}
let dma = match Tensor::<u8>::new(&[64, 64], Some(TensorMemory::DmaBuf), None) {
Ok(t) => t,
Err(e) if platform_resource_absent(&e) => {
use std::io::Write;
let _ = writeln!(
std::io::stderr(),
"SKIP: dmabuf_clone_refuses_host_memory_and_dups_a_real_dma_fd -- \
no usable dma-buf heap on this host"
);
return;
}
Err(e) => panic!("unexpected dma-buf allocation failure: {e:?}"),
};
let dma_dyn: TensorDyn = dma.into();
let borrowed = { dma_dyn.plane0().expect("plane 0").handle };
let cloned = dma_dyn
.dmabuf_clone()
.expect("a DMA-backed tensor clones its fd");
use std::os::fd::AsRawFd;
assert_ne!(
cloned.as_raw_fd() as i64,
borrowed,
"dmabuf_clone must dup, so the caller can close its copy independently"
);
}
#[test]
fn cuda_map_is_none_without_a_handle_and_maps_with_one() {
struct MappingCudaGlOps(Mutex<Vec<u8>>);
impl edgefirst_tensor::CudaGlOps for MappingCudaGlOps {
fn map(&self, _resource: *mut std::ffi::c_void) -> Option<(*mut std::ffi::c_void, usize)> {
let mut g = self.0.lock().expect("lock");
let len = g.len();
Some((g.as_mut_ptr() as *mut std::ffi::c_void, len))
}
fn unmap(&self, _resource: *mut std::ffi::c_void) {}
fn unregister(&self, _resource: *mut std::ffi::c_void) {}
}
let mut t = bare_u8(&[4, 4]);
assert!(
t.cuda_map().is_none(),
"a tensor with no CUDA handle must fast-fail to None"
);
t.set_cuda_handle(edgefirst_tensor::CudaHandle::new_gl(
std::ptr::null_mut(),
16,
Arc::new(MappingCudaGlOps(Mutex::new(vec![0u8; 16])))
as Arc<dyn edgefirst_tensor::CudaGlOps>,
));
let dyn_t: TensorDyn = t.into();
let map = dyn_t
.cuda_map()
.expect("an attached CUDA handle must produce a map guard");
assert_eq!(map.len(), 16);
assert!(!map.device_ptr().is_null());
}
#[test]
fn an_oversized_pbos_capacity_is_the_gl_buffers_not_the_metadata_handles() {
const LOGICAL: usize = 16;
const ALLOCATED: usize = 24;
let ops = MockPboOps::new(ALLOCATED);
let pbo = edgefirst_tensor::PboTensor::<u8>::from_pbo(21, ALLOCATED, &[4, 4, 1], None, ops)
.expect("PboTensor::from_pbo permits size > shape.product()");
let t: TensorDyn = Tensor::<u8>::from_pbo(pbo)
.expect("Tensor::from_pbo")
.into();
assert_eq!(
t.size(),
LOGICAL,
"the logical footprint is the shape product"
);
assert_eq!(
t.capacity_bytes(),
ALLOCATED,
"capacity_bytes must report the GL buffer's real size, not the companion \
metadata handle's shape-sized allocation"
);
assert_eq!(
t.descriptor().capacity,
ALLOCATED as u64,
"...and the descriptor must carry it across the package boundary, or a \
consumer re-importing this PBO maps only part of the buffer"
);
}
#[test]
fn the_pbo_accessors_resolve_through_the_dtype_downcast() {
let plain: TensorDyn = bare_u8(&[4, 4]).into();
assert_eq!(plain.pbo_id(), None);
assert!(plain.pbo_vtable_ptr().is_none());
assert!(plain.pbo_keepalive().is_none());
let ops = MockPboOps::new(16);
let pbo = edgefirst_tensor::PboTensor::<u8>::from_pbo(11, 16, &[4, 4, 1], None, ops)
.expect("PboTensor::from_pbo");
let pbo_t: TensorDyn = Tensor::<u8>::from_pbo(pbo)
.expect("Tensor::from_pbo")
.into();
assert_eq!(
pbo_t.pbo_id(),
Some(11),
"the downcast must find the real GL buffer id, not a default"
);
assert!(
pbo_t.pbo_vtable_ptr().is_some_and(|p| !p.is_null()),
"a PBO-backed tensor must build a real cross-cdylib ops vtable"
);
assert!(
pbo_t.pbo_keepalive().is_some(),
"and a keepalive holding the PboHandle that vtable addresses"
);
let f32_ops = MockPboOps::new(64);
let f32_pbo = edgefirst_tensor::PboTensor::<f32>::from_pbo(12, 64, &[4, 4, 1], None, f32_ops)
.expect("PboTensor::<f32>::from_pbo");
let f32_t: TensorDyn = Tensor::<f32>::from_pbo(f32_pbo)
.expect("Tensor::from_pbo")
.into();
assert_eq!(
f32_t.pbo_id(),
Some(12),
"the dtype downcast must handle every element type, not just u8"
);
}
#[test]
fn a_retagged_typed_lens_erases_to_its_own_dtype_not_the_handles() {
let t_u8 = bare_u8(&[4, 4]);
assert_eq!(TensorTrait::memory(&t_u8), TensorMemory::Mem);
let t_i8: Tensor<i8> = unsafe { std::mem::transmute(t_u8) };
let erased: TensorDyn = t_i8.into();
assert_eq!(
erased.dtype(),
DType::I8,
"erasing a Tensor<i8> must yield an I8 tensor whatever dtype the \
underlying handle was minted with -- a wrong dtype propagates \
silently into quantization and inference rather than stopping \
anything"
);
}
#[test]
fn reshape_changes_the_shape_and_the_cache_follows() {
let mut t: TensorDyn = bare_u8(&[4, 6]).into();
t.reshape(&[2, 12]).expect("equal element count");
assert_eq!(
t.shape(),
&[2, 12],
"shape() serves a cache; it must be refreshed"
);
assert_eq!(t.size(), 24);
match t.reshape(&[5, 5]) {
Err(Error::ShapeMismatch(_)) => {}
other => panic!("a different element count must be refused: {other:?}"),
}
assert_eq!(
t.shape(),
&[2, 12],
"a refused reshape must leave the shape alone"
);
}
#[cfg(unix)]
#[test]
fn clone_fd_works_for_shm_not_only_dma() {
const SHAPE: [usize; 2] = [64, 64];
let shm = match Tensor::<u8>::new(&SHAPE, Some(TensorMemory::Shm), None) {
Ok(t) => t,
Err(e) if platform_resource_absent(&e) => {
use std::io::Write;
let _ = writeln!(
std::io::stderr(),
"SKIP: clone_fd_works_for_shm_not_only_dma -- no usable /dev/shm here"
);
return;
}
Err(e) => panic!("unexpected SHM allocation failure: {e:?}"),
};
{
let mut m = shm
.map_with(CpuAccess::ReadWrite)
.expect("map the SHM tensor");
m.as_mut_slice().fill(0x9C);
}
let dyn_shm: TensorDyn = shm.into();
assert_eq!(
TensorTrait::memory(dyn_shm.as_u8().unwrap()),
TensorMemory::Shm
);
let cloned = dyn_shm
.clone_fd()
.expect("an SHM-backed tensor has a real fd and must clone it");
let reimported =
Tensor::<u8>::from_fd(cloned, &SHAPE, None).expect("the cloned fd must import as a tensor");
let m = reimported
.map_with(CpuAccess::Read)
.expect("map the re-imported tensor");
assert!(
m.as_slice().iter().all(|&b| b == 0x9C),
"the cloned fd must address the SAME buffer -- a dup of an unrelated \
descriptor would also be >= 0 and would read anything but this"
);
}
#[test]
fn mapping_a_pbo_backed_tensor_reads_the_gl_buffer() {
let ops = MockPboOps::new(16);
let pbo = edgefirst_tensor::PboTensor::<u8>::from_pbo(31, 16, &[4, 4, 1], None, ops)
.expect("PboTensor::from_pbo");
let tensor = Tensor::<u8>::from_pbo(pbo).expect("Tensor::from_pbo");
{
let pbo_ref = tensor.as_pbo().expect("as_pbo");
let mut m = pbo_ref.map().expect("typed PBO map");
m.as_mut_slice().fill(0xC3);
}
let dyn_t: TensorDyn = tensor.into();
let bytes = dyn_t
.map_bytes(CpuAccess::Read)
.expect("a PBO-backed tensor must be mappable through TensorDyn");
assert_eq!(bytes.as_slice().len(), 16);
assert!(
bytes.as_slice().iter().all(|&b| b == 0xC3),
"the erased map must read the GL buffer, not the companion host allocation"
);
}
#[test]
fn a_retagged_pbo_still_resolves_its_buffer_id_vtable_and_map() {
let ops = MockPboOps::new(16);
let pbo = edgefirst_tensor::PboTensor::<u8>::from_pbo(41, 16, &[4, 4, 1], None, ops)
.expect("PboTensor::from_pbo -- allocated as u8, as the GL backend does");
let as_u8 = Tensor::<u8>::from_pbo(pbo).expect("Tensor::from_pbo");
{
let p = as_u8.as_pbo().expect("as_pbo before retagging");
let mut m = p.map().expect("typed PBO map");
m.as_mut_slice().fill(0x7E);
}
let as_i8: Tensor<i8> = unsafe { std::mem::transmute(as_u8) };
assert!(
as_i8.as_pbo().is_some(),
"as_pbo must find the stored PboTensor whatever element type it was created with"
);
let erased: TensorDyn = as_i8.into();
assert_eq!(erased.dtype(), DType::I8, "the handle's dtype is retagged");
assert_eq!(
erased.pbo_id(),
Some(41),
"...and the PBO accessors must still resolve -- a None here makes the \
descriptor carry no buffer id, which a cross-package re-import rejects"
);
assert!(erased.pbo_vtable_ptr().is_some_and(|p| !p.is_null()));
assert!(erased.pbo_keepalive().is_some());
assert_eq!(
erased.descriptor().handle,
41,
"the descriptor must carry the real GL buffer id"
);
let bytes = erased
.map_bytes(CpuAccess::Read)
.expect("a retagged PBO tensor must still map through the erased handle");
assert!(
bytes.as_slice().iter().all(|&b| b == 0x7E),
"and must still read the GL buffer"
);
}
#[cfg(target_os = "linux")]
#[test]
fn a_dma_tensor_answers_both_questions_the_gpu_import_sites_ask() {
let dma = match Tensor::<u8>::new(&[64, 64], Some(TensorMemory::DmaBuf), None) {
Ok(t) => t,
Err(e) => {
use std::io::Write;
let _ = writeln!(
std::io::stderr(),
"SKIP: a_dma_tensor_answers_both_questions_the_gpu_import_sites_ask -- \
no usable dma-buf heap here ({e:?})"
);
return;
}
};
assert_eq!(
TensorTrait::memory(&dma),
TensorMemory::DmaBuf,
"a DMA-backed tensor must report DmaBuf -- the probe that decides whether the \
G2D hardware path is even attempted"
);
use std::os::fd::AsRawFd;
let fd = dma
.dmabuf()
.expect("a DMA-backed tensor must yield its fd -- this is what the EGLImage import needs");
assert!(fd.as_raw_fd() >= 0, "and it must be a real descriptor");
let host = bare_u8(&[64, 64]);
assert_eq!(TensorTrait::memory(&host), TensorMemory::Mem);
assert!(host.dmabuf().is_err());
}
#[test]
fn geometry_mutators_keep_the_wrapped_pbo_in_step() {
fn pbo_tensor(bytes: usize, shape: &[usize]) -> TensorDyn {
let ops = MockPboOps::new(bytes);
let pbo = edgefirst_tensor::PboTensor::<u8>::from_pbo(51, bytes, shape, None, ops)
.expect("PboTensor::from_pbo");
Tensor::<u8>::from_pbo(pbo)
.expect("Tensor::from_pbo")
.into()
}
let mut t = pbo_tensor(16, &[4, 4, 1]);
t.reshape(&[2, 8]).expect("equal element count");
assert_eq!(t.shape(), &[2, 8], "the handle's shape follows");
assert_eq!(
t.as_u8().expect("as_u8").as_pbo().expect("as_pbo").shape,
vec![2, 8],
"and so must the wrapped PboTensor's -- edgefirst-image reads the GL buffer's \
geometry off as_pbo(), so a stale shape here is a wrong answer that never errors"
);
let mut t = pbo_tensor(16, &[4, 4, 1]);
t.set_logical_shape(&[2, 4])
.expect("8 bytes fits a 16-byte GL buffer");
assert_eq!(t.shape(), &[2, 4]);
assert_eq!(
t.as_u8().expect("as_u8").as_pbo().expect("as_pbo").shape,
vec![2, 4],
"the capacity-based reconfigure must reach the PboTensor too"
);
let mut t = pbo_tensor(64, &[8, 8, 1]);
t.configure_image(4, 4, PixelFormat::Grey)
.expect("a smaller image fits the 64-byte buffer");
assert_eq!(
t.as_u8().expect("as_u8").as_pbo().expect("as_pbo").shape,
t.shape().to_vec(),
"configure_image is the decode-into-a-pool path; the two geometries must agree \
after it, or the next GL import reads the old one"
);
}