vortex_array/arrays/
assertions.rs1use std::fmt::Display;
5
6use itertools::Itertools;
7use vortex_error::VortexExpect;
8
9use crate::ArrayRef;
10use crate::ExecutionCtx;
11use crate::IntoArray;
12use crate::RecursiveCanonical;
13use crate::aggregate_fn::fns::all_non_distinct::all_non_distinct;
14
15fn format_indices<I: IntoIterator<Item = usize>>(indices: I) -> impl Display {
16 indices.into_iter().format(",")
17}
18
19fn execute_to_canonical(array: ArrayRef, ctx: &mut ExecutionCtx) -> ArrayRef {
21 array
22 .execute::<RecursiveCanonical>(ctx)
23 .vortex_expect("failed to execute array to recursive canonical form")
24 .0
25 .into_array()
26}
27
28#[expect(clippy::unwrap_used)]
30fn find_mismatched_indices(
31 left: &ArrayRef,
32 right: &ArrayRef,
33 ctx: &mut ExecutionCtx,
34) -> Vec<usize> {
35 assert_eq!(left.len(), right.len());
36 (0..left.len())
37 .filter(|i| left.execute_scalar(*i, ctx).unwrap() != right.execute_scalar(*i, ctx).unwrap())
38 .collect()
39}
40
41#[macro_export]
52macro_rules! assert_nth_scalar {
53 ($arr:expr, $n:expr, $expected:expr, $ctx:expr) => {{
54 use $crate::IntoArray as _;
55 let arr_ref: $crate::ArrayRef = $crate::IntoArray::into_array($arr.clone());
56 let expected = $expected.try_into().unwrap();
57 assert_eq!(arr_ref.execute_scalar($n, $ctx).unwrap(), expected);
58 }};
59}
60
61#[macro_export]
70macro_rules! assert_nth_scalar_is_null {
71 ($arr:expr, $n:expr, $ctx:expr) => {{
72 let arr_ref: $crate::ArrayRef = $crate::IntoArray::into_array($arr.clone());
73 let scalar = arr_ref.execute_scalar($n, $ctx).unwrap();
74 assert!(
75 scalar.is_null(),
76 "expected scalar at index {} to be null, but was {:?}",
77 $n,
78 scalar
79 );
80 }};
81}
82
83#[macro_export]
84macro_rules! assert_arrays_eq {
85 ($left:expr, $right:expr, $ctx:expr) => {{
86 let left: $crate::ArrayRef = $crate::IntoArray::into_array($left.clone());
87 let right: $crate::ArrayRef = $crate::IntoArray::into_array($right.clone());
88 if left.dtype() != right.dtype() {
89 let ctx: &$crate::ExecutionCtx = &*$ctx;
90 panic!(
91 "assertion left == right failed: arrays differ in type: {} != {}.\n left: {}\n right: {}",
92 left.dtype(),
93 right.dtype(),
94 left.display_values(ctx),
95 right.display_values(ctx)
96 )
97 }
98
99 if left.len() != right.len() {
100 let ctx: &$crate::ExecutionCtx = &*$ctx;
101 panic!(
102 "assertion left == right failed: arrays differ in length: {} != {}.\n left: {}\n right: {}",
103 left.len(),
104 right.len(),
105 left.display_values(ctx),
106 right.display_values(ctx)
107 );
108 }
109
110 let left = left.clone();
111 let right = right.clone();
112 $crate::arrays::assert_arrays_eq_impl(&left, &right, $ctx);
113 }};
114}
115
116#[track_caller]
119#[expect(clippy::panic)]
120pub fn assert_arrays_eq_impl(left: &ArrayRef, right: &ArrayRef, ctx: &mut ExecutionCtx) {
121 let executed = execute_to_canonical(left.clone(), ctx);
122
123 let left_right_the_same =
124 all_non_distinct(left, right, ctx).vortex_expect("failed to compare left and right");
125 let executed_right_the_same = all_non_distinct(&executed, right, ctx)
126 .vortex_expect("failed to compare executed left and right");
127
128 if !left_right_the_same || !executed_right_the_same {
129 let left_right = find_mismatched_indices(left, right, ctx);
130
131 let mut msg = String::new();
132 if !left_right.is_empty() {
133 msg.push_str(&format!(
134 "\n left != right at indices: {}",
135 format_indices(left_right)
136 ));
137 }
138
139 let executed_right = find_mismatched_indices(&executed, right, ctx);
140 if !executed_right.is_empty() {
141 msg.push_str(&format!(
142 "\n executed != right at indices: {}",
143 format_indices(executed_right)
144 ));
145 }
146 panic!(
147 "assertion failed: arrays do not match:{}\n left: {}\n right: {}\n executed: {}",
148 msg,
149 left.display_values(ctx),
150 right.display_values(ctx),
151 executed.display_values(ctx)
152 )
153 }
154}