#[cfg(feature = "dynamic")]
mod storage_tests {
use crate::dynamic::{Element, storage::DynamicTensor};
#[test]
fn shared_storage_on_reshape() {
let data = vec![
Element::Int(1),
Element::Int(2),
Element::Int(3),
Element::Int(4),
];
let t = DynamicTensor::from_vec(data, vec![2, 2]);
let r = t.reshape(vec![4]).unwrap();
assert!(std::sync::Arc::ptr_eq(&t.storage, &r.storage));
}
#[test]
fn reshape_fails_on_size_mismatch() {
let t = DynamicTensor::from_vec(vec![Element::Int(1); 4], vec![2, 2]);
assert!(t.reshape(vec![3]).is_none());
}
#[test]
fn slice_indices_shares_storage() {
let data: Vec<Element> = (0..6).map(|i| Element::Int(i as i64)).collect();
let t = DynamicTensor::from_vec(data, vec![2, 3]);
let s = t.slice_indices(vec![0, 1, 2], vec![3]);
assert!(std::sync::Arc::ptr_eq(&t.storage, &s.storage));
assert_eq!(s.get_flat(0), Some(&Element::Int(0)));
assert_eq!(s.get_flat(1), Some(&Element::Int(1)));
}
#[test]
fn materialize_produces_contiguous_copy() {
let data: Vec<Element> = (0..6).map(|i| Element::Int(i as i64)).collect();
let t = DynamicTensor::from_vec(data, vec![2, 3]);
let mut s = t.slice_indices(vec![0, 2, 4], vec![3]);
assert!(!s.is_unique()); s.materialize();
assert!(s.is_unique());
assert_eq!(s.get_flat(0), Some(&Element::Int(0)));
assert_eq!(s.get_flat(1), Some(&Element::Int(2)));
assert_eq!(s.get_flat(2), Some(&Element::Int(4)));
}
#[test]
fn no_reference_cycles() {
let data = vec![Element::Int(1)];
let t = DynamicTensor::from_vec(data, vec![1]);
let s = t.slice_indices(vec![0], vec![1]);
drop(t);
assert_eq!(s.get_flat(0), Some(&Element::Int(1)));
}
}
#[cfg(feature = "dynamic")]
mod utility_tests {
use crate::Tensor;
use crate::dynamic::Element;
#[test]
fn none_mask_basic() {
let t = Tensor::from_elements(
vec![Element::Float(1.0), Element::None, Element::Float(3.0)],
&[3],
);
let mask = t.none_mask();
assert_eq!(mask.as_slice(), &[0.0, 1.0, 0.0]);
}
#[test]
fn count_none_basic() {
let t = Tensor::from_elements(
vec![
Element::None,
Element::Int(1),
Element::None,
Element::Float(2.0),
],
&[4],
);
assert_eq!(t.count_none(), 2);
}
#[test]
fn forward_fill_none_basic() {
let t = Tensor::from_elements(
vec![
Element::Float(1.0),
Element::None,
Element::None,
Element::Float(4.0),
],
&[4],
);
let filled = t.forward_fill_none(Element::Float(0.0));
assert_eq!(filled.get_element(&[1]), Some(Element::Float(1.0)));
assert_eq!(filled.get_element(&[2]), Some(Element::Float(1.0)));
assert_eq!(filled.get_element(&[3]), Some(Element::Float(4.0)));
}
#[test]
fn forward_fill_none_leading_uses_fallback() {
let t = Tensor::from_elements(
vec![Element::None, Element::None, Element::Float(5.0)],
&[3],
);
let filled = t.forward_fill_none(Element::Float(-1.0));
assert_eq!(filled.get_element(&[0]), Some(Element::Float(-1.0)));
assert_eq!(filled.get_element(&[1]), Some(Element::Float(-1.0)));
assert_eq!(filled.get_element(&[2]), Some(Element::Float(5.0)));
}
#[test]
fn sum_skip_none_basic() {
let t = Tensor::from_elements(
vec![Element::Float(1.0), Element::None, Element::Int(3)],
&[3],
);
assert_eq!(t.sum_skip_none(), 4.0);
}
#[test]
fn sum_skip_none_all_none_is_zero() {
let t = Tensor::from_elements(vec![Element::None; 3], &[3]);
assert_eq!(t.sum_skip_none(), 0.0);
}
}
#[test]
fn dynamic_feature_is_off_by_default_in_non_dynamic_tests() {
let _ = 42_u8; }
#[cfg(feature = "dynamic")]
mod is_none_mask_tests {
use crate::Tensor;
use crate::dynamic::Element;
#[test]
fn is_none_mask_matches_none_mask() {
let t = Tensor::from_elements(
vec![Element::Float(1.0), Element::None, Element::Float(3.0)],
&[3],
);
assert_eq!(t.none_mask(), t.is_none_mask());
}
#[test]
fn is_none_mask_shape_and_values() {
let t = Tensor::from_elements(vec![Element::None, Element::Int(1), Element::None], &[3]);
let mask = t.is_none_mask();
assert_eq!(mask.shape(), &[3]);
assert_eq!(mask.as_slice(), &[1.0, 0.0, 1.0]);
}
}
#[cfg(feature = "dynamic")]
mod lifecycle_tests {
use crate::Tensor;
use crate::dynamic::Element;
#[test]
fn dynamic_len_equals_shape_product() {
let t = Tensor::from_elements(
vec![Element::Float(1.0), Element::text("a"), Element::None],
&[3],
);
assert_eq!(t.len(), 3, "dynamic len() must equal shape product");
}
#[test]
fn dynamic_len_2d() {
let t = Tensor::from_elements(
vec![
Element::Int(1),
Element::Int(2),
Element::Int(3),
Element::Int(4),
],
&[2, 2],
);
assert_eq!(t.len(), 4);
}
#[test]
fn dynamic_debug_not_empty() {
let t = Tensor::from_elements(vec![Element::Float(1.5), Element::text("hello")], &[2]);
let dbg = format!("{t:?}");
assert!(
!dbg.contains("data: []") || dbg.contains("dynamic"),
"Debug output should not appear empty: {dbg}"
);
}
#[test]
fn dynamic_reshape_is_unsupported() {
let t = Tensor::from_elements(vec![Element::Int(1), Element::Int(2)], &[2]);
let result = std::panic::catch_unwind(|| t.reshape(&[1, 2]));
assert!(
result.is_err(),
"reshape on dynamic tensor must panic (unsupported)"
);
}
#[test]
fn dynamic_flatten_is_unsupported() {
let t = Tensor::from_elements(
vec![
Element::Int(1),
Element::Int(2),
Element::Int(3),
Element::Int(4),
],
&[2, 2],
);
let result = std::panic::catch_unwind(|| t.flatten());
assert!(
result.is_err(),
"flatten on dynamic tensor must panic (unsupported)"
);
}
#[test]
fn dynamic_slice_builder_is_unsupported() {
let t = Tensor::from_elements(
vec![Element::Int(1), Element::Int(2), Element::Int(3)],
&[3],
);
let result = t.slice_str("0:2");
assert!(
result.is_err(),
"slice_str on dynamic tensor must return Err"
);
}
#[test]
fn dynamic_arithmetic_is_unsupported() {
let t = Tensor::from_elements(vec![Element::Float(1.0), Element::Float(2.0)], &[2]);
let result = std::panic::catch_unwind(|| {
let _ = &t + &t;
});
assert!(
result.is_err(),
"arithmetic on dynamic tensor must panic (unsupported)"
);
}
#[test]
fn dynamic_sum_is_unsupported() {
let t = Tensor::from_elements(vec![Element::Float(1.0)], &[1]);
let result = std::panic::catch_unwind(|| t.sum());
assert!(
result.is_err(),
"sum on dynamic tensor must panic (unsupported)"
);
}
}