use std::sync::Arc;
use rstest::rstest;
use vortex_buffer::buffer;
use crate::IntoArray;
use crate::arrays::BoolArray;
use crate::arrays::ListViewArray;
use crate::arrays::PrimitiveArray;
use crate::arrays::listview::ListViewArrayExt;
use crate::dtype::DType;
use crate::dtype::Nullability;
use crate::dtype::PType;
use crate::scalar::Scalar;
use crate::validity::Validity;
#[test]
fn test_nullable_listview_comprehensive() {
let elements = buffer![1i32, 2, 3, 4, 5, 6].into_array();
let offsets = buffer![0i32, 2, 4].into_array();
let sizes = buffer![2i32, 2, 2].into_array();
let validity = Validity::from_iter([true, false, true]);
let listview = unsafe {
ListViewArray::new_unchecked(elements, offsets, sizes, validity)
.with_zero_copy_to_list(true)
};
assert_eq!(listview.len(), 3);
assert!(listview.is_valid(0).unwrap());
assert!(listview.is_invalid(1).unwrap());
assert!(listview.is_valid(2).unwrap());
assert!(matches!(
listview.dtype(),
DType::List(_, Nullability::Nullable)
));
let first = listview.scalar_at(0).unwrap();
assert!(!first.is_null());
assert_eq!(
first,
Scalar::list(
Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
vec![1i32.into(), 2i32.into()],
Nullability::Nullable,
)
);
let second = listview.scalar_at(1).unwrap();
assert!(second.is_null());
let third = listview.scalar_at(2).unwrap();
assert!(!third.is_null());
assert_eq!(
third,
Scalar::list(
Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
vec![5i32.into(), 6i32.into()],
Nullability::Nullable,
)
);
let null_list_data = listview.list_elements_at(1).unwrap();
assert_eq!(null_list_data.len(), 2);
assert_eq!(null_list_data.scalar_at(0).unwrap(), 3i32.into());
assert_eq!(null_list_data.scalar_at(1).unwrap(), 4i32.into());
}
#[rstest]
#[case::all_nulls(Validity::AllInvalid, vec![false, false, false])]
#[case::all_valid(Validity::AllValid, vec![true, true, true])]
#[case::mixed(Validity::from_iter([false, true, false]), vec![false, true, false])]
fn test_nullable_patterns(#[case] validity: Validity, #[case] expected_validity: Vec<bool>) {
let elements = buffer![1i32, 2, 3, 4, 5, 6].into_array();
let offsets = buffer![0i32, 2, 4].into_array();
let sizes = buffer![2i32, 2, 2].into_array();
let listview = unsafe { ListViewArray::new_unchecked(elements, offsets, sizes, validity) };
for (i, &expected) in expected_validity.iter().enumerate() {
assert_eq!(listview.is_valid(i).unwrap(), expected);
}
}
#[test]
fn test_nullable_elements() {
let elements =
PrimitiveArray::from_option_iter([Some(1i32), None, Some(3), None, Some(5), Some(6)])
.into_array();
let offsets = buffer![0i32, 2, 4].into_array();
let sizes = buffer![2i32, 2, 2].into_array();
let listview = unsafe {
ListViewArray::new_unchecked(elements, offsets, sizes, Validity::AllValid)
.with_zero_copy_to_list(true)
};
let first_list = listview.list_elements_at(0).unwrap();
assert_eq!(first_list.len(), 2);
assert!(!first_list.scalar_at(0).unwrap().is_null());
assert_eq!(first_list.scalar_at(0).unwrap(), 1i32.into());
assert!(first_list.scalar_at(1).unwrap().is_null());
let second_list = listview.list_elements_at(1).unwrap();
assert!(!second_list.scalar_at(0).unwrap().is_null());
assert_eq!(second_list.scalar_at(0).unwrap(), 3i32.into());
assert!(second_list.scalar_at(1).unwrap().is_null());
let third_list = listview.list_elements_at(2).unwrap();
assert!(!third_list.scalar_at(0).unwrap().is_null());
assert_eq!(third_list.scalar_at(0).unwrap(), 5i32.into());
assert!(!third_list.scalar_at(1).unwrap().is_null());
assert_eq!(third_list.scalar_at(1).unwrap(), 6i32.into());
assert!(matches!(
listview.elements().dtype(),
DType::Primitive(PType::I32, Nullability::Nullable)
));
}
#[test]
fn test_validity_length_mismatch() {
let elements = buffer![1i32, 2, 3, 4].into_array();
let offsets = buffer![0i32, 2].into_array();
let sizes = buffer![2i32, 2].into_array();
let validity = Validity::Array(BoolArray::from_iter(vec![true, false, true]).into_array());
let result = ListViewArray::try_new(elements, offsets, sizes, validity);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
err.to_string().contains("validity") && err.to_string().contains("size"),
"Unexpected error: {err}"
);
}