tests.rs

  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}