1use super::*;
2
3fn is_almost_eq(a: &[f32], b: &[f32]) -> bool {
4 a.len() == b.len() && a.iter().zip(b).all(|(x, y)| (x - y).abs() < 1e-6)
5}
6
7fn cols_to_str(cols: &[f32], total_size: f32) -> String {
8 cols.iter()
9 .map(|f| "*".repeat(f32::round(f * total_size) as usize))
10 .collect::<Vec<String>>()
11 .join("|")
12}
13
14fn parse_resize_behavior(
15 input: &str,
16 total_size: f32,
17 expected_cols: usize,
18) -> Vec<TableResizeBehavior> {
19 let mut resize_behavior = Vec::with_capacity(expected_cols);
20 for col in input.split('|') {
21 if col.starts_with('X') || col.is_empty() {
22 resize_behavior.push(TableResizeBehavior::None);
23 } else if col.starts_with('*') {
24 resize_behavior.push(TableResizeBehavior::MinSize(col.len() as f32 / total_size));
25 } else {
26 panic!("invalid test input: unrecognized resize behavior: {}", col);
27 }
28 }
29
30 if resize_behavior.len() != expected_cols {
31 panic!(
32 "invalid test input: expected {} columns, got {}",
33 expected_cols,
34 resize_behavior.len()
35 );
36 }
37 resize_behavior
38}
39
40mod reset_column_size {
41 use super::*;
42
43 fn parse(input: &str) -> (Vec<f32>, f32, Option<usize>) {
44 let mut widths = Vec::new();
45 let mut column_index = None;
46 for (index, col) in input.split('|').enumerate() {
47 widths.push(col.len() as f32);
48 if col.starts_with('X') {
49 column_index = Some(index);
50 }
51 }
52
53 for w in &widths {
54 assert!(w.is_finite(), "incorrect number of columns");
55 }
56 let total = widths.iter().sum::<f32>();
57 for width in &mut widths {
58 *width /= total;
59 }
60 (widths, total, column_index)
61 }
62
63 #[track_caller]
64 fn check_reset_size(initial_sizes: &str, widths: &str, expected: &str, resize_behavior: &str) {
65 let (initial_sizes, total_1, None) = parse(initial_sizes) else {
66 panic!("invalid test input: initial sizes should not be marked");
67 };
68 let (widths, total_2, Some(column_index)) = parse(widths) else {
69 panic!("invalid test input: widths should be marked");
70 };
71 assert_eq!(
72 total_1, total_2,
73 "invalid test input: total width not the same {total_1}, {total_2}"
74 );
75 let (expected, total_3, None) = parse(expected) else {
76 panic!("invalid test input: expected should not be marked: {expected:?}");
77 };
78 assert_eq!(
79 total_2, total_3,
80 "invalid test input: total width not the same"
81 );
82 let cols = initial_sizes.len();
83 let resize_behavior_vec = parse_resize_behavior(resize_behavior, total_1, cols);
84 let resize_behavior = TableRow::from_vec(resize_behavior_vec, cols);
85 let result = TableColumnWidths::reset_to_initial_size(
86 column_index,
87 TableRow::from_vec(widths, cols),
88 TableRow::from_vec(initial_sizes, cols),
89 &resize_behavior,
90 );
91 let result_slice = result.as_slice();
92 let is_eq = is_almost_eq(result_slice, &expected);
93 if !is_eq {
94 let result_str = cols_to_str(result_slice, total_1);
95 let expected_str = cols_to_str(&expected, total_1);
96 panic!(
97 "resize failed\ncomputed: {result_str}\nexpected: {expected_str}\n\ncomputed values: {result_slice:?}\nexpected values: {expected:?}\n:minimum widths: {resize_behavior:?}"
98 );
99 }
100 }
101
102 macro_rules! check_reset_size {
103 (columns: $cols:expr, starting: $initial:expr, snapshot: $current:expr, expected: $expected:expr, resizing: $resizing:expr $(,)?) => {
104 check_reset_size($initial, $current, $expected, $resizing);
105 };
106 ($name:ident, columns: $cols:expr, starting: $initial:expr, snapshot: $current:expr, expected: $expected:expr, minimums: $resizing:expr $(,)?) => {
107 #[test]
108 fn $name() {
109 check_reset_size($initial, $current, $expected, $resizing);
110 }
111 };
112 }
113
114 check_reset_size!(
115 basic_right,
116 columns: 5,
117 starting: "**|**|**|**|**",
118 snapshot: "**|**|X|***|**",
119 expected: "**|**|**|**|**",
120 minimums: "X|*|*|*|*",
121 );
122
123 check_reset_size!(
124 basic_left,
125 columns: 5,
126 starting: "**|**|**|**|**",
127 snapshot: "**|**|***|X|**",
128 expected: "**|**|**|**|**",
129 minimums: "X|*|*|*|**",
130 );
131
132 check_reset_size!(
133 squashed_left_reset_col2,
134 columns: 6,
135 starting: "*|***|**|**|****|*",
136 snapshot: "*|*|X|*|*|********",
137 expected: "*|*|**|*|*|*******",
138 minimums: "X|*|*|*|*|*",
139 );
140
141 check_reset_size!(
142 grow_cascading_right,
143 columns: 6,
144 starting: "*|***|****|**|***|*",
145 snapshot: "*|***|X|**|**|*****",
146 expected: "*|***|****|*|*|****",
147 minimums: "X|*|*|*|*|*",
148 );
149
150 check_reset_size!(
151 squashed_right_reset_col4,
152 columns: 6,
153 starting: "*|***|**|**|****|*",
154 snapshot: "*|********|*|*|X|*",
155 expected: "*|*****|*|*|****|*",
156 minimums: "X|*|*|*|*|*",
157 );
158
159 check_reset_size!(
160 reset_col6_right,
161 columns: 6,
162 starting: "*|***|**|***|***|**",
163 snapshot: "*|***|**|***|**|XXX",
164 expected: "*|***|**|***|***|**",
165 minimums: "X|*|*|*|*|*",
166 );
167
168 check_reset_size!(
169 reset_col6_left,
170 columns: 6,
171 starting: "*|***|**|***|***|**",
172 snapshot: "*|***|**|***|****|X",
173 expected: "*|***|**|***|***|**",
174 minimums: "X|*|*|*|*|*",
175 );
176
177 check_reset_size!(
178 last_column_grow_cascading,
179 columns: 6,
180 starting: "*|***|**|**|**|***",
181 snapshot: "*|*******|*|**|*|X",
182 expected: "*|******|*|*|*|***",
183 minimums: "X|*|*|*|*|*",
184 );
185
186 check_reset_size!(
187 goes_left_when_left_has_extreme_diff,
188 columns: 6,
189 starting: "*|***|****|**|**|***",
190 snapshot: "*|********|X|*|**|**",
191 expected: "*|*****|****|*|**|**",
192 minimums: "X|*|*|*|*|*",
193 );
194
195 check_reset_size!(
196 basic_shrink_right,
197 columns: 6,
198 starting: "**|**|**|**|**|**",
199 snapshot: "**|**|XXX|*|**|**",
200 expected: "**|**|**|**|**|**",
201 minimums: "X|*|*|*|*|*",
202 );
203
204 check_reset_size!(
205 shrink_should_go_left,
206 columns: 6,
207 starting: "*|***|**|*|*|*",
208 snapshot: "*|*|XXX|**|*|*",
209 expected: "*|**|**|**|*|*",
210 minimums: "X|*|*|*|*|*",
211 );
212
213 check_reset_size!(
214 shrink_should_go_right,
215 columns: 6,
216 starting: "*|***|**|**|**|*",
217 snapshot: "*|****|XXX|*|*|*",
218 expected: "*|****|**|**|*|*",
219 minimums: "X|*|*|*|*|*",
220 );
221}
222
223mod drag_handle {
224 use super::*;
225
226 fn parse(input: &str) -> (Vec<f32>, f32, Option<usize>) {
227 let mut widths = Vec::new();
228 let column_index = input.replace("*", "").find("I");
229 for col in input.replace("I", "|").split('|') {
230 widths.push(col.len() as f32);
231 }
232
233 for w in &widths {
234 assert!(w.is_finite(), "incorrect number of columns");
235 }
236 let total = widths.iter().sum::<f32>();
237 for width in &mut widths {
238 *width /= total;
239 }
240 (widths, total, column_index)
241 }
242
243 #[track_caller]
244 fn check(distance: i32, widths: &str, expected: &str, resize_behavior: &str) {
245 let (widths, total_1, Some(column_index)) = parse(widths) else {
246 panic!("invalid test input: widths should be marked");
247 };
248 let (expected, total_2, None) = parse(expected) else {
249 panic!("invalid test input: expected should not be marked: {expected:?}");
250 };
251 assert_eq!(
252 total_1, total_2,
253 "invalid test input: total width not the same"
254 );
255 let cols = widths.len();
256 let resize_behavior_vec = parse_resize_behavior(resize_behavior, total_1, cols);
257 let resize_behavior = TableRow::from_vec(resize_behavior_vec, cols);
258
259 let distance = distance as f32 / total_1;
260
261 let mut widths_table_row = TableRow::from_vec(widths, cols);
262 TableColumnWidths::drag_column_handle(
263 distance,
264 column_index,
265 &mut widths_table_row,
266 &resize_behavior,
267 );
268
269 let result_widths = widths_table_row.as_slice();
270 let is_eq = is_almost_eq(result_widths, &expected);
271 if !is_eq {
272 let result_str = cols_to_str(result_widths, total_1);
273 let expected_str = cols_to_str(&expected, total_1);
274 panic!(
275 "resize failed\ncomputed: {result_str}\nexpected: {expected_str}\n\ncomputed values: {result_widths:?}\nexpected values: {expected:?}\n:minimum widths: {resize_behavior:?}"
276 );
277 }
278 }
279
280 macro_rules! check {
281 (columns: $cols:expr, distance: $dist:expr, snapshot: $current:expr, expected: $expected:expr, resizing: $resizing:expr $(,)?) => {
282 check($dist, $current, $expected, $resizing);
283 };
284 ($name:ident, columns: $cols:expr, distance: $dist:expr, snapshot: $current:expr, expected: $expected:expr, minimums: $resizing:expr $(,)?) => {
285 #[test]
286 fn $name() {
287 check($dist, $current, $expected, $resizing);
288 }
289 };
290 }
291
292 check!(
293 basic_right_drag,
294 columns: 3,
295 distance: 1,
296 snapshot: "**|**I**",
297 expected: "**|***|*",
298 minimums: "X|*|*",
299 );
300
301 check!(
302 drag_left_against_mins,
303 columns: 5,
304 distance: -1,
305 snapshot: "*|*|*|*I*******",
306 expected: "*|*|*|*|*******",
307 minimums: "X|*|*|*|*",
308 );
309
310 check!(
311 drag_left,
312 columns: 5,
313 distance: -2,
314 snapshot: "*|*|*|*****I***",
315 expected: "*|*|*|***|*****",
316 minimums: "X|*|*|*|*",
317 );
318}