@@ -18,6 +18,7 @@ use super::ListViewVectorMut;
1818use crate :: Vector ;
1919use crate :: match_each_integer_pvector;
2020use crate :: match_each_integer_pvector_pair;
21+ use crate :: primitive:: PVector ;
2122use crate :: primitive:: PrimitiveVector ;
2223use crate :: vector_ops:: VectorMutOps ;
2324use crate :: vector_ops:: VectorOps ;
@@ -110,10 +111,10 @@ fn listview_eq_impl<O, S>(
110111 validity : & Mask ,
111112 self_elements : & Vector ,
112113 other_elements : & Vector ,
113- self_offsets : & crate :: primitive :: PVector < O > ,
114- other_offsets : & crate :: primitive :: PVector < O > ,
115- self_sizes : & crate :: primitive :: PVector < S > ,
116- other_sizes : & crate :: primitive :: PVector < S > ,
114+ self_offsets : & PVector < O > ,
115+ other_offsets : & PVector < O > ,
116+ self_sizes : & PVector < S > ,
117+ other_sizes : & PVector < S > ,
117118) -> bool
118119where
119120 O : vortex_dtype:: NativePType + Copy ,
@@ -459,3 +460,191 @@ fn validate_views_bound(
459460
460461 Ok ( ( ) )
461462}
463+
464+ #[ cfg( test) ]
465+ mod tests {
466+ use std:: sync:: Arc ;
467+
468+ use vortex_buffer:: Buffer ;
469+ use vortex_mask:: Mask ;
470+
471+ use super :: * ;
472+ use crate :: primitive:: PVector ;
473+
474+ /// Helper to create a ListViewVector with i32 elements and u32 offsets/sizes
475+ fn make_listview (
476+ elements : Vec < i32 > ,
477+ offsets : Vec < u32 > ,
478+ sizes : Vec < u32 > ,
479+ validity : Mask ,
480+ ) -> ListViewVector {
481+ let elem_validity = Mask :: new_true ( elements. len ( ) ) ;
482+ let elements = PVector :: new ( Buffer :: from ( elements) , elem_validity) ;
483+ let offsets_len = offsets. len ( ) ;
484+ let sizes_len = sizes. len ( ) ;
485+ let offsets = PVector :: new ( Buffer :: from ( offsets) , Mask :: new_true ( offsets_len) ) ;
486+ let sizes = PVector :: new ( Buffer :: from ( sizes) , Mask :: new_true ( sizes_len) ) ;
487+ ListViewVector :: try_new (
488+ Arc :: new ( Vector :: from ( elements) ) ,
489+ PrimitiveVector :: from ( offsets) ,
490+ PrimitiveVector :: from ( sizes) ,
491+ validity,
492+ )
493+ . unwrap ( )
494+ }
495+
496+ #[ test]
497+ fn test_listview_eq_all_valid ( ) {
498+ // All lists valid - direct element comparison
499+ let v1 = make_listview (
500+ vec ! [ 1 , 2 , 3 , 4 , 5 ] ,
501+ vec ! [ 0 , 2 , 3 ] ,
502+ vec ! [ 2 , 1 , 2 ] ,
503+ Mask :: new_true ( 3 ) ,
504+ ) ;
505+ let v2 = make_listview (
506+ vec ! [ 1 , 2 , 3 , 4 , 5 ] ,
507+ vec ! [ 0 , 2 , 3 ] ,
508+ vec ! [ 2 , 1 , 2 ] ,
509+ Mask :: new_true ( 3 ) ,
510+ ) ;
511+ assert_eq ! ( v1, v2) ;
512+
513+ // Different elements should not be equal
514+ let v3 = make_listview (
515+ vec ! [ 1 , 2 , 99 , 4 , 5 ] ,
516+ vec ! [ 0 , 2 , 3 ] ,
517+ vec ! [ 2 , 1 , 2 ] ,
518+ Mask :: new_true ( 3 ) ,
519+ ) ;
520+ assert_ne ! ( v1, v3) ;
521+ }
522+
523+ #[ test]
524+ fn test_listview_eq_all_invalid ( ) {
525+ // All lists invalid - elements don't matter
526+ let v1 = make_listview (
527+ vec ! [ 1 , 2 , 3 , 4 , 5 ] ,
528+ vec ! [ 0 , 2 , 3 ] ,
529+ vec ! [ 2 , 1 , 2 ] ,
530+ Mask :: new_false ( 3 ) ,
531+ ) ;
532+ let v2 = make_listview (
533+ vec ! [ 99 , 99 , 99 , 99 , 99 ] ,
534+ vec ! [ 0 , 2 , 3 ] ,
535+ vec ! [ 2 , 1 , 2 ] ,
536+ Mask :: new_false ( 3 ) ,
537+ ) ;
538+ assert_eq ! ( v1, v2) ;
539+ }
540+
541+ #[ test]
542+ fn test_listview_eq_mixed_validity ( ) {
543+ // Lists: [1,2], null, [4,5]
544+ // Elements at positions 2 (index of null list's elements) don't matter
545+ let validity = Mask :: from_indices ( 3 , vec ! [ 0 , 2 ] ) ;
546+
547+ let v1 = make_listview (
548+ vec ! [ 1 , 2 , 3 , 4 , 5 ] ,
549+ vec ! [ 0 , 2 , 3 ] ,
550+ vec ! [ 2 , 1 , 2 ] ,
551+ validity. clone ( ) ,
552+ ) ;
553+ let v2 = make_listview (
554+ vec ! [ 1 , 2 , 3 , 4 , 5 ] ,
555+ vec ! [ 0 , 2 , 3 ] ,
556+ vec ! [ 2 , 1 , 2 ] ,
557+ validity. clone ( ) ,
558+ ) ;
559+ assert_eq ! ( v1, v2) ;
560+
561+ // Element at position 2 is only used by the invalid list - should still be equal
562+ let v3 = make_listview (
563+ vec ! [ 1 , 2 , 99 , 4 , 5 ] ,
564+ vec ! [ 0 , 2 , 3 ] ,
565+ vec ! [ 2 , 1 , 2 ] ,
566+ validity. clone ( ) ,
567+ ) ;
568+ assert_eq ! ( v1, v3, "Invalid list's elements should be ignored" ) ;
569+
570+ // Element at position 0 is used by valid list 0 - should NOT be equal
571+ let v4 = make_listview (
572+ vec ! [ 99 , 2 , 3 , 4 , 5 ] ,
573+ vec ! [ 0 , 2 , 3 ] ,
574+ vec ! [ 2 , 1 , 2 ] ,
575+ validity. clone ( ) ,
576+ ) ;
577+ assert_ne ! ( v1, v4, "Valid list's elements must match" ) ;
578+ }
579+
580+ #[ test]
581+ fn test_listview_eq_overlapping_slices ( ) {
582+ // Overlapping ranges: list0=[0..3], list1=[1..4] (overlapping at positions 1,2)
583+ // This tests that the Vec<bool> approach handles overlaps correctly
584+ let v1 = make_listview ( vec ! [ 1 , 2 , 3 , 4 ] , vec ! [ 0 , 1 ] , vec ! [ 3 , 3 ] , Mask :: new_true ( 2 ) ) ;
585+ let v2 = make_listview ( vec ! [ 1 , 2 , 3 , 4 ] , vec ! [ 0 , 1 ] , vec ! [ 3 , 3 ] , Mask :: new_true ( 2 ) ) ;
586+ assert_eq ! ( v1, v2) ;
587+
588+ // Different element in overlapping region
589+ let v3 = make_listview ( vec ! [ 1 , 99 , 3 , 4 ] , vec ! [ 0 , 1 ] , vec ! [ 3 , 3 ] , Mask :: new_true ( 2 ) ) ;
590+ assert_ne ! ( v1, v3) ;
591+ }
592+
593+ #[ test]
594+ fn test_listview_eq_overlapping_with_invalid ( ) {
595+ // list0=[0..3] valid, list1=[1..4] invalid
596+ // Positions 1,2 are in overlap but list1 is invalid, so only list0's view matters
597+ let validity = Mask :: from_indices ( 2 , vec ! [ 0 ] ) ; // only list 0 is valid
598+
599+ let v1 = make_listview ( vec ! [ 1 , 2 , 3 , 4 ] , vec ! [ 0 , 1 ] , vec ! [ 3 , 3 ] , validity. clone ( ) ) ;
600+
601+ // Element at position 3 is only used by invalid list1 - can differ
602+ let v2 = make_listview ( vec ! [ 1 , 2 , 3 , 99 ] , vec ! [ 0 , 1 ] , vec ! [ 3 , 3 ] , validity. clone ( ) ) ;
603+ assert_eq ! ( v1, v2, "Element used only by invalid list can differ" ) ;
604+
605+ // Element at position 2 is used by valid list0 - must match
606+ let v3 = make_listview ( vec ! [ 1 , 2 , 99 , 4 ] , vec ! [ 0 , 1 ] , vec ! [ 3 , 3 ] , validity. clone ( ) ) ;
607+ assert_ne ! ( v1, v3, "Element used by valid list must match" ) ;
608+ }
609+
610+ #[ test]
611+ fn test_listview_eq_different_offsets_sizes ( ) {
612+ // Same elements but different offsets at valid positions
613+ let v1 = make_listview ( vec ! [ 1 , 2 , 3 , 4 ] , vec ! [ 0 , 2 ] , vec ! [ 2 , 2 ] , Mask :: new_true ( 2 ) ) ;
614+ let v2 = make_listview (
615+ vec ! [ 1 , 2 , 3 , 4 ] ,
616+ vec ! [ 0 , 1 ] , // different offset for list1
617+ vec ! [ 2 , 2 ] ,
618+ Mask :: new_true ( 2 ) ,
619+ ) ;
620+ assert_ne ! ( v1, v2, "Different offsets at valid positions" ) ;
621+
622+ // Different sizes at valid positions
623+ let v3 = make_listview (
624+ vec ! [ 1 , 2 , 3 , 4 ] ,
625+ vec ! [ 0 , 2 ] ,
626+ vec ! [ 2 , 1 ] , // different size for list1
627+ Mask :: new_true ( 2 ) ,
628+ ) ;
629+ assert_ne ! ( v1, v3, "Different sizes at valid positions" ) ;
630+ }
631+
632+ #[ test]
633+ fn test_listview_eq_different_validity ( ) {
634+ let v1 = make_listview ( vec ! [ 1 , 2 , 3 , 4 ] , vec ! [ 0 , 2 ] , vec ! [ 2 , 2 ] , Mask :: new_true ( 2 ) ) ;
635+ let v2 = make_listview (
636+ vec ! [ 1 , 2 , 3 , 4 ] ,
637+ vec ! [ 0 , 2 ] ,
638+ vec ! [ 2 , 2 ] ,
639+ Mask :: from_indices ( 2 , vec ! [ 0 ] ) , // only first list valid
640+ ) ;
641+ assert_ne ! ( v1, v2, "Different validity patterns" ) ;
642+ }
643+
644+ #[ test]
645+ fn test_listview_eq_empty ( ) {
646+ let v1 = make_listview ( vec ! [ ] , vec ! [ ] , vec ! [ ] , Mask :: new_true ( 0 ) ) ;
647+ let v2 = make_listview ( vec ! [ ] , vec ! [ ] , vec ! [ ] , Mask :: new_true ( 0 ) ) ;
648+ assert_eq ! ( v1, v2) ;
649+ }
650+ }
0 commit comments