diff options
Diffstat (limited to 'src/app/state.rs')
| -rw-r--r-- | src/app/state.rs | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/src/app/state.rs b/src/app/state.rs index 2482a06d..089834d8 100644 --- a/src/app/state.rs +++ b/src/app/state.rs @@ -1782,6 +1782,68 @@ impl AppState { self.selected, 0, "empty app state should keep selected workspace at 0" ); + assert!( + self.pane_id_aliases.is_empty(), + "empty app state must not keep raw pane aliases" + ); + assert!( + self.public_pane_id_aliases.is_empty(), + "empty app state must not keep public pane aliases" + ); + assert!( + self.previous_pane_focus.is_none(), + "empty app state must not keep previous pane focus" + ); + assert!( + self.plugin_panes.is_empty(), + "empty app state must not keep plugin pane records" + ); + assert!( + self.pending_agent_notifications.is_empty(), + "empty app state must not keep pending agent notifications" + ); + assert!( + self.copy_mode.is_none(), + "empty app state must not keep copy mode" + ); + assert!( + self.rename_pane_target.is_none(), + "empty app state must not keep rename pane target" + ); + assert!( + self.selection.is_none(), + "empty app state must not keep text selection" + ); + assert!( + self.selection_autoscroll.is_none(), + "empty app state must not keep selection autoscroll" + ); + if let Some(toast) = &self.toast { + assert!( + toast.target.is_none(), + "empty app state must not keep pane-targeted toast" + ); + } + assert!( + self.right_click_passthrough.is_none(), + "empty app state must not keep right-click passthrough gesture" + ); + assert!( + self.drag.is_none(), + "empty app state must not keep drag state" + ); + assert!( + self.workspace_press.is_none(), + "empty app state must not keep workspace press state" + ); + assert!( + self.tab_press.is_none(), + "empty app state must not keep tab press state" + ); + assert!( + self.context_menu.is_none(), + "empty app state must not keep context menu" + ); return; } @@ -1802,6 +1864,7 @@ impl AppState { ); let mut workspace_ids = std::collections::HashSet::new(); + let mut workspace_id_to_idx = std::collections::HashMap::new(); let mut pane_ids = std::collections::HashSet::new(); let mut attached_terminal_ids = std::collections::HashSet::new(); for (ws_idx, ws) in self.workspaces.iter().enumerate() { @@ -1811,6 +1874,7 @@ impl AppState { ws.id, ws_idx ); + workspace_id_to_idx.insert(ws.id.clone(), ws_idx); ws.assert_invariants_for_test(); for tab in &ws.tabs { @@ -1834,6 +1898,166 @@ impl AppState { } } } + + let assert_live_pane = |pane_id: PaneId, context: &str| { + assert!( + pane_ids.contains(&pane_id), + "{context} references missing pane {:?}", + pane_id + ); + }; + let assert_workspace_pane = |workspace_id: &str, pane_id: PaneId, context: &str| { + let ws_idx = workspace_id_to_idx + .get(workspace_id) + .copied() + .unwrap_or_else(|| panic!("{context} references missing workspace {workspace_id}")); + assert!( + self.workspaces[ws_idx].pane_state(pane_id).is_some(), + "{context} references pane {:?} outside workspace {}", + pane_id, + workspace_id + ); + }; + let assert_workspace_index = |ws_idx: usize, context: &str| { + assert!( + ws_idx < self.workspaces.len(), + "{context} references workspace index {} out of bounds for {} workspaces", + ws_idx, + self.workspaces.len() + ); + }; + let assert_tab_index = |ws_idx: usize, tab_idx: usize, context: &str| { + assert_workspace_index(ws_idx, context); + assert!( + tab_idx < self.workspaces[ws_idx].tabs.len(), + "{context} references tab index {} out of bounds for workspace {} with {} tabs", + tab_idx, + ws_idx, + self.workspaces[ws_idx].tabs.len() + ); + }; + + for (&raw, &pane_id) in &self.pane_id_aliases { + assert_live_pane(pane_id, &format!("raw pane alias {raw}")); + } + for (public_id, &pane_id) in &self.public_pane_id_aliases { + assert_live_pane(pane_id, &format!("public pane alias {public_id}")); + } + if let Some(focus) = &self.previous_pane_focus { + assert_workspace_pane(&focus.workspace_id, focus.pane_id, "previous pane focus"); + } + if let Some(toast) = &self.toast { + if let Some(target) = &toast.target { + assert_workspace_pane(&target.workspace_id, target.pane_id, "toast target"); + } + } + for (&pane_id, notification) in &self.pending_agent_notifications { + assert_eq!( + pane_id, notification.pane_id, + "pending agent notification map key must match payload pane id" + ); + assert_workspace_pane( + ¬ification.workspace_id, + notification.pane_id, + "pending agent notification", + ); + } + for &pane_id in self.plugin_panes.keys() { + assert_live_pane(pane_id, "plugin pane record"); + } + if let Some(copy_mode) = &self.copy_mode { + assert_live_pane(copy_mode.pane_id, "copy mode"); + } + if let Some(pane_id) = self.rename_pane_target { + assert_live_pane(pane_id, "rename pane target"); + } + if let Some(selection) = &self.selection { + assert_live_pane(selection.pane_id, "text selection"); + } else { + assert!( + self.selection_autoscroll.is_none(), + "selection autoscroll must not remain without an active text selection" + ); + } + if let Some(gesture) = &self.right_click_passthrough { + assert_live_pane(gesture.pane_info.id, "right-click passthrough gesture"); + } + if let Some(drag) = &self.drag { + match &drag.target { + DragTarget::WorkspaceReorder { + source_ws_idx, + insert_idx, + } => { + assert_workspace_index(*source_ws_idx, "workspace drag source"); + if let Some(insert_idx) = insert_idx { + assert!( + *insert_idx <= self.workspaces.len(), + "workspace drag insert index {} out of bounds for {} workspaces", + insert_idx, + self.workspaces.len() + ); + } + } + DragTarget::TabReorder { + ws_idx, + source_tab_idx, + insert_idx, + } => { + assert_tab_index(*ws_idx, *source_tab_idx, "tab drag source"); + if let Some(insert_idx) = insert_idx { + assert!( + *insert_idx <= self.workspaces[*ws_idx].tabs.len(), + "tab drag insert index {} out of bounds for workspace {} with {} tabs", + insert_idx, + ws_idx, + self.workspaces[*ws_idx].tabs.len() + ); + } + } + DragTarget::PaneScrollbar { pane_id, .. } => { + assert_live_pane(*pane_id, "pane scrollbar drag") + } + _ => {} + } + } + if let Some(press) = &self.workspace_press { + assert_workspace_index(press.ws_idx, "workspace press"); + } + if let Some(press) = &self.tab_press { + assert_tab_index(press.ws_idx, press.tab_idx, "tab press"); + } + if let Some(menu) = &self.context_menu { + match menu.kind { + ContextMenuKind::Workspace { ws_idx } + | ContextMenuKind::GitWorkspace { ws_idx, .. } => { + assert_workspace_index(ws_idx, "context menu workspace") + } + ContextMenuKind::Tab { ws_idx, tab_idx } => { + assert_tab_index(ws_idx, tab_idx, "context menu tab") + } + ContextMenuKind::Pane { + ws_idx, + tab_idx, + pane_id, + source_pane_id, + .. + } => { + assert_tab_index(ws_idx, tab_idx, "context menu pane tab"); + assert!( + self.workspaces[ws_idx].tabs[tab_idx] + .panes + .contains_key(&pane_id), + "context menu pane references pane {:?} outside workspace {} tab {}", + pane_id, + ws_idx, + tab_idx + ); + if let Some(source_pane_id) = source_pane_id { + assert_live_pane(source_pane_id, "context menu source pane"); + } + } + } + } } pub fn insert_test_runtime( |
