Skip to repository content658 lines · 22.0 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T05:38:18.262Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
tests.rs
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