tests.rs

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