Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T05:38:18.262Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

tests.rs

658 lines · 22.0 KB · rust
1use super::table_row::TableRow;
2use crate::{RedistributableColumnsState, ResizableColumnsState, TableResizeBehavior};
3use gpui::{AbsoluteLength, px};
4
5fn is_almost_eq(a: &[f32], b: &[f32]) -> bool {
6    a.len() == b.len() && a.iter().zip(b).all(|(x, y)| (x - y).abs() < 1e-6)
7}
8
9fn cols_to_str(cols: &[f32], total_size: f32) -> String {
10    cols.iter()
11        .map(|f| "*".repeat(f32::round(f * total_size) as usize))
12        .collect::<Vec<String>>()
13        .join("|")
14}
15
16fn parse_resize_behavior(
17    input: &str,
18    total_size: f32,
19    expected_cols: usize,
20) -> Vec<TableResizeBehavior> {
21    let mut resize_behavior = Vec::with_capacity(expected_cols);
22    for col in input.split('|') {
23        if col.starts_with('X') || col.is_empty() {
24            resize_behavior.push(TableResizeBehavior::None);
25        } else if col.starts_with('*') {
26            resize_behavior.push(TableResizeBehavior::MinSize(col.len() as f32 / total_size));
27        } else {
28            panic!("invalid test input: unrecognized resize behavior: {}", col);
29        }
30    }
31
32    if resize_behavior.len() != expected_cols {
33        panic!(
34            "invalid test input: expected {} columns, got {}",
35            expected_cols,
36            resize_behavior.len()
37        );
38    }
39    resize_behavior
40}
41
42mod reset_column_size {
43    use super::*;
44
45    fn parse(input: &str) -> (Vec<f32>, f32, Option<usize>) {
46        let mut widths = Vec::new();
47        let mut column_index = None;
48        for (index, col) in input.split('|').enumerate() {
49            widths.push(col.len() as f32);
50            if col.starts_with('X') {
51                column_index = Some(index);
52            }
53        }
54
55        for w in &widths {
56            assert!(w.is_finite(), "incorrect number of columns");
57        }
58        let total = widths.iter().sum::<f32>();
59        for width in &mut widths {
60            *width /= total;
61        }
62        (widths, total, column_index)
63    }
64
65    #[track_caller]
66    fn check_reset_size(initial_sizes: &str, widths: &str, expected: &str, resize_behavior: &str) {
67        let (initial_sizes, total_1, None) = parse(initial_sizes) else {
68            panic!("invalid test input: initial sizes should not be marked");
69        };
70        let (widths, total_2, Some(column_index)) = parse(widths) else {
71            panic!("invalid test input: widths should be marked");
72        };
73        assert_eq!(
74            total_1, total_2,
75            "invalid test input: total width not the same {total_1}, {total_2}"
76        );
77        let (expected, total_3, None) = parse(expected) else {
78            panic!("invalid test input: expected should not be marked: {expected:?}");
79        };
80        assert_eq!(
81            total_2, total_3,
82            "invalid test input: total width not the same"
83        );
84        let cols = initial_sizes.len();
85        let resize_behavior_vec = parse_resize_behavior(resize_behavior, total_1, cols);
86        let resize_behavior = TableRow::from_vec(resize_behavior_vec, cols);
87        let result = RedistributableColumnsState::reset_to_initial_size(
88            column_index,
89            TableRow::from_vec(widths, cols),
90            TableRow::from_vec(initial_sizes, cols),
91            &resize_behavior,
92        );
93        let result_slice = result.as_slice();
94        let is_eq = is_almost_eq(result_slice, &expected);
95        if !is_eq {
96            let result_str = cols_to_str(result_slice, total_1);
97            let expected_str = cols_to_str(&expected, total_1);
98            panic!(
99                "resize failed\ncomputed: {result_str}\nexpected: {expected_str}\n\ncomputed values: {result_slice:?}\nexpected values: {expected:?}\n:minimum widths: {resize_behavior:?}"
100            );
101        }
102    }
103
104    macro_rules! check_reset_size {
105        (columns: $cols:expr, starting: $initial:expr, snapshot: $current:expr, expected: $expected:expr, resizing: $resizing:expr $(,)?) => {
106            check_reset_size($initial, $current, $expected, $resizing);
107        };
108        ($name:ident, columns: $cols:expr, starting: $initial:expr, snapshot: $current:expr, expected: $expected:expr, minimums: $resizing:expr $(,)?) => {
109            #[test]
110            fn $name() {
111                check_reset_size($initial, $current, $expected, $resizing);
112            }
113        };
114    }
115
116    check_reset_size!(
117        basic_right,
118        columns: 5,
119        starting: "**|**|**|**|**",
120        snapshot: "**|**|X|***|**",
121        expected: "**|**|**|**|**",
122        minimums: "X|*|*|*|*",
123    );
124
125    check_reset_size!(
126        basic_left,
127        columns: 5,
128        starting: "**|**|**|**|**",
129        snapshot: "**|**|***|X|**",
130        expected: "**|**|**|**|**",
131        minimums: "X|*|*|*|**",
132    );
133
134    check_reset_size!(
135        squashed_left_reset_col2,
136        columns: 6,
137        starting: "*|***|**|**|****|*",
138        snapshot: "*|*|X|*|*|********",
139        expected: "*|*|**|*|*|*******",
140        minimums: "X|*|*|*|*|*",
141    );
142
143    check_reset_size!(
144        grow_cascading_right,
145        columns: 6,
146        starting: "*|***|****|**|***|*",
147        snapshot: "*|***|X|**|**|*****",
148        expected: "*|***|****|*|*|****",
149        minimums: "X|*|*|*|*|*",
150    );
151
152    check_reset_size!(
153       squashed_right_reset_col4,
154       columns: 6,
155       starting: "*|***|**|**|****|*",
156       snapshot: "*|********|*|*|X|*",
157       expected: "*|*****|*|*|****|*",
158       minimums: "X|*|*|*|*|*",
159    );
160
161    check_reset_size!(
162        reset_col6_right,
163        columns: 6,
164        starting: "*|***|**|***|***|**",
165        snapshot: "*|***|**|***|**|XXX",
166        expected: "*|***|**|***|***|**",
167        minimums: "X|*|*|*|*|*",
168    );
169
170    check_reset_size!(
171        reset_col6_left,
172        columns: 6,
173        starting: "*|***|**|***|***|**",
174        snapshot: "*|***|**|***|****|X",
175        expected: "*|***|**|***|***|**",
176        minimums: "X|*|*|*|*|*",
177    );
178
179    check_reset_size!(
180        last_column_grow_cascading,
181        columns: 6,
182        starting: "*|***|**|**|**|***",
183        snapshot: "*|*******|*|**|*|X",
184        expected: "*|******|*|*|*|***",
185        minimums: "X|*|*|*|*|*",
186    );
187
188    check_reset_size!(
189        goes_left_when_left_has_extreme_diff,
190        columns: 6,
191        starting: "*|***|****|**|**|***",
192        snapshot: "*|********|X|*|**|**",
193        expected: "*|*****|****|*|**|**",
194        minimums: "X|*|*|*|*|*",
195    );
196
197    check_reset_size!(
198        basic_shrink_right,
199        columns: 6,
200        starting: "**|**|**|**|**|**",
201        snapshot: "**|**|XXX|*|**|**",
202        expected: "**|**|**|**|**|**",
203        minimums: "X|*|*|*|*|*",
204    );
205
206    check_reset_size!(
207        shrink_should_go_left,
208        columns: 6,
209        starting: "*|***|**|*|*|*",
210        snapshot: "*|*|XXX|**|*|*",
211        expected: "*|**|**|**|*|*",
212        minimums: "X|*|*|*|*|*",
213    );
214
215    check_reset_size!(
216        shrink_should_go_right,
217        columns: 6,
218        starting: "*|***|**|**|**|*",
219        snapshot: "*|****|XXX|*|*|*",
220        expected: "*|****|**|**|*|*",
221        minimums: "X|*|*|*|*|*",
222    );
223}
224
225mod drag_handle {
226    use super::*;
227
228    fn parse(input: &str) -> (Vec<f32>, f32, Option<usize>) {
229        let mut widths = Vec::new();
230        let column_index = input.replace("*", "").find("I");
231        for col in input.replace("I", "|").split('|') {
232            widths.push(col.len() as f32);
233        }
234
235        for w in &widths {
236            assert!(w.is_finite(), "incorrect number of columns");
237        }
238        let total = widths.iter().sum::<f32>();
239        for width in &mut widths {
240            *width /= total;
241        }
242        (widths, total, column_index)
243    }
244
245    #[track_caller]
246    fn check(distance: i32, widths: &str, expected: &str, resize_behavior: &str) {
247        let (widths, total_1, Some(column_index)) = parse(widths) else {
248            panic!("invalid test input: widths should be marked");
249        };
250        let (expected, total_2, None) = parse(expected) else {
251            panic!("invalid test input: expected should not be marked: {expected:?}");
252        };
253        assert_eq!(
254            total_1, total_2,
255            "invalid test input: total width not the same"
256        );
257        let cols = widths.len();
258        let resize_behavior_vec = parse_resize_behavior(resize_behavior, total_1, cols);
259        let resize_behavior = TableRow::from_vec(resize_behavior_vec, cols);
260
261        let distance = distance as f32 / total_1;
262
263        let mut widths_table_row = TableRow::from_vec(widths, cols);
264        RedistributableColumnsState::drag_column_handle(
265            distance,
266            column_index,
267            &mut widths_table_row,
268            &resize_behavior,
269        );
270
271        let result_widths = widths_table_row.as_slice();
272        let is_eq = is_almost_eq(result_widths, &expected);
273        if !is_eq {
274            let result_str = cols_to_str(result_widths, total_1);
275            let expected_str = cols_to_str(&expected, total_1);
276            panic!(
277                "resize failed\ncomputed: {result_str}\nexpected: {expected_str}\n\ncomputed values: {result_widths:?}\nexpected values: {expected:?}\n:minimum widths: {resize_behavior:?}"
278            );
279        }
280    }
281
282    macro_rules! check {
283        (columns: $cols:expr, distance: $dist:expr, snapshot: $current:expr, expected: $expected:expr, resizing: $resizing:expr $(,)?) => {
284            check($dist, $current, $expected, $resizing);
285        };
286        ($name:ident, columns: $cols:expr, distance: $dist:expr, snapshot: $current:expr, expected: $expected:expr, minimums: $resizing:expr $(,)?) => {
287            #[test]
288            fn $name() {
289                check($dist, $current, $expected, $resizing);
290            }
291        };
292    }
293
294    check!(
295        basic_right_drag,
296        columns: 3,
297        distance: 1,
298        snapshot: "**|**I**",
299        expected: "**|***|*",
300        minimums: "X|*|*",
301    );
302
303    check!(
304        drag_left_against_mins,
305        columns: 5,
306        distance: -1,
307        snapshot: "*|*|*|*I*******",
308        expected: "*|*|*|*|*******",
309        minimums: "X|*|*|*|*",
310    );
311
312    check!(
313        drag_left,
314        columns: 5,
315        distance: -2,
316        snapshot: "*|*|*|*****I***",
317        expected: "*|*|*|***|*****",
318        minimums: "X|*|*|*|*",
319    );
320}
321
322mod drag_with_hidden_columns {
323    use super::*;
324
325    // Dragging with hidden (filtered) columns: the resize dividers are laid out using the
326    // *redistributed* (visible-only) widths, so `compute_drag_preview` must do its geometry in
327    // that same space and skip hidden columns when propagating the resize to a neighbor.
328
329    /// Mirrors how the renderer turns raw widths into the on-screen layout: hidden columns
330    /// collapse to zero and their space is redistributed across the visible columns.
331    fn redistributed(widths: &[f32], hidden: &[bool]) -> Vec<f32> {
332        let visible_sum: f32 = widths
333            .iter()
334            .zip(hidden)
335            .filter(|(_, is_hidden)| !**is_hidden)
336            .map(|(width, _)| *width)
337            .sum();
338        widths
339            .iter()
340            .zip(hidden)
341            .map(|(width, is_hidden)| {
342                if *is_hidden {
343                    0.0
344                } else {
345                    *width / visible_sum
346                }
347            })
348            .collect()
349    }
350
351    #[test]
352    fn drag_without_hidden_columns_is_unchanged() {
353        // Guards the pre-existing behavior: with no hidden mask the drag operates on the raw
354        // widths directly (the visible space and the raw space are the same).
355        let resize_behavior = TableRow::from_vec(vec![TableResizeBehavior::Resizable; 3], 3);
356        let widths = TableRow::from_vec(vec![1.0 / 3.0; 3], 3);
357
358        let result = RedistributableColumnsState::compute_drag_preview(
359            widths,
360            &resize_behavior,
361            None,
362            1,
363            0.8,
364            0.0,
365        );
366
367        let result = result.as_slice();
368        let boundary = result[0] + result[1];
369        assert!(
370            (boundary - 0.8).abs() < 1e-6,
371            "expected the boundary after column 1 to follow the cursor to 0.8: {result:?}",
372        );
373        assert!(
374            (result[0] - 1.0 / 3.0).abs() < 1e-6,
375            "column 0 must not be affected: {result:?}",
376        );
377    }
378
379    #[test]
380    fn drag_boundary_follows_cursor_with_hidden_column() {
381        // Three equal columns; column 0 is hidden. The two visible columns each render at 0.5
382        // of the container. The user grabs the divider between the visible columns (original
383        // index 1) and drags the cursor to 70% of the container. The boundary between the
384        // visible columns should follow the cursor to 0.7.
385        let resize_behavior = TableRow::from_vec(vec![TableResizeBehavior::Resizable; 3], 3);
386        let widths = TableRow::from_vec(vec![1.0 / 3.0; 3], 3);
387        let hidden = [true, false, false];
388        let hidden_mask = TableRow::from_vec(hidden.to_vec(), 3);
389
390        let result = RedistributableColumnsState::compute_drag_preview(
391            widths,
392            &resize_behavior,
393            Some(&hidden_mask),
394            1,
395            0.7,
396            0.0,
397        );
398
399        let rendered = redistributed(result.as_slice(), &hidden);
400        assert!(
401            (rendered[1] - 0.7).abs() < 1e-3,
402            "expected the visible boundary to follow the cursor to 0.7, got {} (raw {:?})",
403            rendered[1],
404            result.as_slice(),
405        );
406    }
407
408    #[test]
409    fn drag_does_not_resize_hidden_neighbor() {
410        // Three equal columns; the middle column (1) is hidden. The only divider the user can
411        // grab sits between visible columns 0 and 2 (original index 0). Dragging it must resize
412        // the next *visible* column (2) and leave the hidden column's width untouched.
413        let resize_behavior = TableRow::from_vec(vec![TableResizeBehavior::Resizable; 3], 3);
414        let widths = TableRow::from_vec(vec![1.0 / 3.0; 3], 3);
415        let hidden = [false, true, false];
416        let hidden_mask = TableRow::from_vec(hidden.to_vec(), 3);
417
418        let result = RedistributableColumnsState::compute_drag_preview(
419            widths,
420            &resize_behavior,
421            Some(&hidden_mask),
422            0,
423            0.7,
424            0.0,
425        );
426
427        let result = result.as_slice();
428        assert!(
429            (result[1] - 1.0 / 3.0).abs() < 1e-6,
430            "hidden column width must be preserved, but it changed: {result:?}",
431        );
432        // The drag moved width from visible column 2 to visible column 0, so the total is
433        // unchanged and the next *visible* column absorbed the resize.
434        let total: f32 = result.iter().sum();
435        assert!(
436            (total - 1.0).abs() < 1e-6,
437            "total must be preserved: {result:?}"
438        );
439        assert!(
440            result[0] > 1.0 / 3.0 && result[2] < 1.0 / 3.0,
441            "expected the resize to be absorbed by the next visible column: {result:?}",
442        );
443    }
444}
445
446mod resizable_drag {
447    use super::*;
448
449    const REM: f32 = 16.;
450
451    fn state(widths_px: &[f32], behavior: Vec<TableResizeBehavior>) -> ResizableColumnsState {
452        let widths: Vec<AbsoluteLength> = widths_px
453            .iter()
454            .map(|w| AbsoluteLength::Pixels(px(*w)))
455            .collect();
456        ResizableColumnsState::new(widths.len(), widths, behavior)
457    }
458
459    fn widths_px(state: &ResizableColumnsState) -> Vec<f32> {
460        state
461            .widths
462            .as_slice()
463            .iter()
464            .map(|w| f32::from(w.to_pixels(px(REM))))
465            .collect()
466    }
467
468    #[test]
469    fn drag_first_column_right() {
470        let mut s = state(&[100., 100., 100.], vec![TableResizeBehavior::None; 3]);
471        s.drag_to(0, px(150.), px(REM));
472        assert_eq!(widths_px(&s), vec![150., 100., 100.]);
473    }
474
475    #[test]
476    fn drag_middle_column_right() {
477        let mut s = state(&[100., 100., 100.], vec![TableResizeBehavior::None; 3]);
478        s.drag_to(1, px(250.), px(REM));
479        assert_eq!(widths_px(&s), vec![100., 150., 100.]);
480    }
481
482    #[test]
483    fn drag_does_not_affect_other_columns() {
484        let mut s = state(&[100., 100., 100.], vec![TableResizeBehavior::None; 3]);
485        s.drag_to(1, px(280.), px(REM));
486        let w = widths_px(&s);
487        assert_eq!(w[0], 100.);
488        assert_eq!(w[2], 100.);
489    }
490
491    #[test]
492    fn drag_below_min_clamps_to_min_size() {
493        // MinSize(2.0) with rem=16 → min_px = 32
494        let mut s = state(
495            &[100., 100.],
496            vec![TableResizeBehavior::MinSize(2.0), TableResizeBehavior::None],
497        );
498        s.drag_to(0, px(5.), px(REM));
499        assert_eq!(widths_px(&s), vec![32., 100.]);
500    }
501
502    #[test]
503    fn drag_x_below_left_edge_clamps_via_min() {
504        // drag_x < left_edge would yield negative width; min clamping must catch it.
505        let mut s = state(
506            &[100., 100.],
507            vec![TableResizeBehavior::MinSize(1.0), TableResizeBehavior::None],
508        );
509        s.drag_to(0, px(-50.), px(REM));
510        assert_eq!(widths_px(&s), vec![16., 100.]);
511    }
512
513    #[test]
514    fn pinned_and_scrollable_width_split() {
515        let s = state(&[100., 100., 100.], vec![TableResizeBehavior::None; 3]);
516        assert_eq!(f32::from(s.pinned_width(2, px(REM))), 200.);
517        assert_eq!(f32::from(s.scrollable_width(2, px(REM))), 100.);
518    }
519}
520
521mod pin_layout {
522    use super::super::is_pinned_layout;
523
524    #[test]
525    fn zero_pinned_falls_back_to_single_section() {
526        assert!(!is_pinned_layout(0, 5));
527    }
528
529    #[test]
530    fn all_pinned_falls_back_to_single_section() {
531        assert!(!is_pinned_layout(5, 5));
532    }
533
534    #[test]
535    fn more_than_total_falls_back_to_single_section() {
536        assert!(!is_pinned_layout(6, 5));
537    }
538
539    #[test]
540    fn partial_pinning_uses_split_layout() {
541        assert!(is_pinned_layout(1, 5));
542        assert!(is_pinned_layout(2, 5));
543        assert!(is_pinned_layout(4, 5));
544    }
545}
546
547mod column_filter {
548    use super::super::column_is_visible;
549    use super::*;
550    use crate::{redistribute_hidden_fractions, redistribute_hidden_widths};
551    use gpui::{DefiniteLength, Length};
552
553    fn frac_row(values: &[f32]) -> TableRow<f32> {
554        TableRow::from_vec(values.to_vec(), values.len())
555    }
556
557    fn hidden_row(values: &[bool]) -> TableRow<bool> {
558        TableRow::from_vec(values.to_vec(), values.len())
559    }
560
561    fn width_row(values: &[f32]) -> TableRow<Length> {
562        TableRow::from_vec(
563            values
564                .iter()
565                .map(|fraction| Length::Definite(DefiniteLength::Fraction(*fraction)))
566                .collect(),
567            values.len(),
568        )
569    }
570
571    fn width_fractions(widths: &TableRow<Length>) -> Vec<f32> {
572        widths
573            .as_slice()
574            .iter()
575            .map(|length| match length {
576                Length::Definite(DefiniteLength::Fraction(fraction)) => *fraction,
577                other => panic!("expected fraction, got {other:?}"),
578            })
579            .collect()
580    }
581
582    #[test]
583    fn column_is_visible_respects_mask() {
584        let filter = Some(hidden_row(&[false, true, false]));
585        assert!(column_is_visible(&filter, 0));
586        assert!(!column_is_visible(&filter, 1));
587        assert!(column_is_visible(&filter, 2));
588        // Indices outside the mask default to visible.
589        assert!(column_is_visible(&filter, 5));
590    }
591
592    #[test]
593    fn column_is_visible_without_filter_is_always_visible() {
594        let filter: Option<TableRow<bool>> = None;
595        assert!(column_is_visible(&filter, 0));
596        assert!(column_is_visible(&filter, 100));
597    }
598
599    #[test]
600    fn redistribute_widths_is_identity_without_hidden_columns() {
601        let widths = width_row(&[0.25, 0.25, 0.25, 0.25]);
602        assert_eq!(
603            width_fractions(&redistribute_hidden_widths(&widths, None)),
604            vec![0.25, 0.25, 0.25, 0.25]
605        );
606
607        let none_hidden = hidden_row(&[false, false, false, false]);
608        assert_eq!(
609            width_fractions(&redistribute_hidden_widths(&widths, Some(&none_hidden))),
610            vec![0.25, 0.25, 0.25, 0.25]
611        );
612    }
613
614    #[test]
615    fn redistribute_widths_scales_visible_columns_to_fill() {
616        let widths = width_row(&[0.25, 0.25, 0.25, 0.25]);
617        let hidden = hidden_row(&[false, true, false, false]);
618        let result = width_fractions(&redistribute_hidden_widths(&widths, Some(&hidden)));
619
620        // The hidden column keeps its stored width rather than being zeroed out (it is simply
621        // not rendered), so its width is restored intact when it is shown again.
622        assert_eq!(result[1], 0.25);
623        // The visible columns expand to fill the container.
624        let visible_sum: f32 = result[0] + result[2] + result[3];
625        assert!(
626            (visible_sum - 1.0).abs() < 1e-6,
627            "visible sum was {visible_sum}"
628        );
629        // Equal initial fractions stay equal after redistribution.
630        assert!((result[0] - result[2]).abs() < 1e-6);
631        assert!((result[0] - result[3]).abs() < 1e-6);
632    }
633
634    #[test]
635    fn redistribute_fractions_scales_visible_columns_to_fill() {
636        let fractions = frac_row(&[0.25, 0.25, 0.25, 0.25]);
637        let hidden = hidden_row(&[false, true, false, false]);
638        let result = redistribute_hidden_fractions(&fractions, Some(&hidden));
639        let result = result.as_slice();
640
641        assert_eq!(result[1], 0.25);
642        let visible_sum: f32 = result[0] + result[2] + result[3];
643        assert!(
644            (visible_sum - 1.0).abs() < 1e-6,
645            "visible sum was {visible_sum}"
646        );
647    }
648
649    #[test]
650    fn redistribute_fractions_is_identity_without_hidden_columns() {
651        let fractions = frac_row(&[0.2, 0.3, 0.5]);
652        assert_eq!(
653            redistribute_hidden_fractions(&fractions, None).as_slice(),
654            &[0.2, 0.3, 0.5]
655        );
656    }
657}
658
Served at tenant.openagents/omega Member data and write actions are omitted.