Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/cmux-tui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,9 @@ jobs:
if-no-files-found: error
retention-days: 1

# Lint in every hosted mode. When this ran only in full mode, warnings
# accumulated on main unnoticed because the full lane was rarely used.
- name: cargo clippy
if: inputs.mode == 'full'
working-directory: cmux-tui
run: cargo clippy --workspace --all-targets --locked -- -D warnings

Expand Down
24 changes: 0 additions & 24 deletions cmux-tui/crates/chatmux-relay/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1442,17 +1442,6 @@ async fn run_spec(
RunOutcome::Done { exit_code: exited.unwrap_or(0), output: text.into_owned() }
}

#[cfg(unix)]
fn signal_process_group_id<F>(pid: Option<u32>, kill: bool, send: F) -> bool
where
F: FnOnce(libc::pid_t, libc::c_int) -> libc::c_int,
{
let Some(pid) = pid else { return false };
let signal = if kill { libc::SIGKILL } else { libc::SIGTERM };
let group = -(pid as i32);
send(group, signal) == 0
}

#[cfg(windows)]
struct WindowsJob {
handle: std::os::windows::io::OwnedHandle,
Expand Down Expand Up @@ -2126,19 +2115,6 @@ mod tests {
std::fs::canonicalize(&dir).unwrap()
}

#[cfg(unix)]
#[test]
fn process_group_failure_does_not_fallback_to_numeric_pid() {
let mut targets = Vec::new();
let signalled = signal_process_group_id(Some(4_321), false, |target, _| {
targets.push(target);
-1
});

assert!(!signalled);
assert_eq!(targets, vec![-4_321]);
}

#[cfg(windows)]
#[test]
fn create_parent_dirs_accepts_windows_drive_prefix() {
Expand Down
2 changes: 1 addition & 1 deletion cmux-tui/crates/chatmux-relay/src/enrollment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ mod tests {
let path = fixture(&enrollment(), 0o600, "oversized");
let current_len = std::fs::metadata(&path).unwrap().len() as usize;
let padding = vec![b' '; MAX_ENROLLMENT_BYTES + 1 - current_len];
let mut file = std::fs::OpenOptions::new().append(true).open(&path).unwrap();
let mut file = OpenOptions::new().append(true).open(&path).unwrap();
file.write_all(&padding).unwrap();

let error = load_managed_enrollment_file(&path, NOW).expect_err("oversized enrollment");
Expand Down
5 changes: 3 additions & 2 deletions cmux-tui/crates/cmux-tui-core/src/journal_checkpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,7 @@ mod tests {
terminal_output: None,
};
assert!(restore_preview(&checkpoint, &[record.clone(), record.clone()], 4).is_err());
assert!(restore_preview(&checkpoint, &[record.clone()], 5).is_err());
assert!(restore_preview(&checkpoint, std::slice::from_ref(&record), 5).is_err());
let mut gapped = record;
gapped.sequence = 5;
assert!(restore_preview(&checkpoint, &[gapped], 5).is_err());
Expand Down Expand Up @@ -976,7 +976,8 @@ mod tests {
};

for cursor in [None, Some(Value::Null)] {
let preview = restore_preview(&checkpoint(cursor), &[record.clone()], 4).unwrap();
let preview =
restore_preview(&checkpoint(cursor), std::slice::from_ref(&record), 4).unwrap();
assert_eq!(preview["fully_reducible"], false);
}
}
Expand Down
6 changes: 3 additions & 3 deletions cmux-tui/crates/cmux-tui-core/src/journal_ingress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1291,9 +1291,9 @@ mod tests {
fn retry_wait_releases_mux_before_waiting() {
let mux = Mux::new("journal-retry-wait-drop", crate::SurfaceOptions::default());
let journal = mux.shared_journal_handle();
let (released_tx, released_rx) = std::sync::mpsc::sync_channel(1);
let (proceed_tx, proceed_rx) = std::sync::mpsc::sync_channel(1);
let (done_tx, done_rx) = std::sync::mpsc::sync_channel(1);
let (released_tx, released_rx) = sync_channel(1);
let (proceed_tx, proceed_rx) = sync_channel(1);
let (done_tx, done_rx) = sync_channel(1);
let waiter_mux = Arc::clone(&mux);
let waiter = std::thread::spawn(move || {
wait_for_journal_retry_after_release(waiter_mux, Duration::from_secs(60), || {
Expand Down
4 changes: 2 additions & 2 deletions cmux-tui/crates/cmux-tui-core/src/mux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16665,7 +16665,7 @@ fn restore_resource_state(
for screen in &topology.screens {
let expected_panes =
panes_by_screen.get(&screen.public_id).unwrap_or(&empty_expected_panes);
crate::workspace_registry::validate_registry_screen_projection(screen, &expected_panes)?;
crate::workspace_registry::validate_registry_screen_projection(screen, expected_panes)?;
let id = screen_slots[&screen.public_id];
let root =
restore_layout_node(&screen.layout, &pane_slots, &mut split_slots, &mut allocate)?;
Expand Down Expand Up @@ -19275,7 +19275,7 @@ mod tests {
let expected = expected_panes_by_screen(&duplicate_panes);
assert_eq!(expected.get(&first_screen).map(|panes| panes.len()), Some(4));
assert_eq!(expected.get(&second_screen).map(|panes| panes.len()), Some(1));
assert!(expected.get(&restore_screen_id(999)).is_none());
assert!(!expected.contains_key(&restore_screen_id(999)));

let mut mismatched = topology;
mismatched.panes[3].screen_id = second_screen.clone();
Expand Down
8 changes: 4 additions & 4 deletions cmux-tui/crates/cmux-tui-core/src/surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4366,10 +4366,10 @@ impl Surface {
}

#[cfg(test)]
pub(crate) fn install_terminal_reaper_that_finishes_for_test(
fn install_terminal_reaper_that_finishes_for_test(
self: &Arc<Self>,
started: std::sync::mpsc::SyncSender<()>,
proceed: std::sync::mpsc::Receiver<()>,
started: SyncSender<()>,
proceed: Receiver<()>,
) -> Arc<ReaderCompletion> {
let pty = self.as_pty().expect("test reaper requires a PTY surface");
pty.reaper_completion.reset();
Expand Down Expand Up @@ -6295,7 +6295,7 @@ struct StartupChild {
}

#[cfg(test)]
impl cmux_pty::ChildKiller for StartupChild {
impl ChildKiller for StartupChild {
fn kill(&mut self) -> std::io::Result<()> {
self.state.kill_count.fetch_add(1, Ordering::Relaxed);
Ok(())
Expand Down
16 changes: 14 additions & 2 deletions cmux-tui/crates/cmux-tui-core/src/terminal_host_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,18 @@ impl TerminalExit {
}
}

/// Wait for the native child hidden behind cmux-pty without collapsing Unix
/// signal/core information into its display-only fallback status.
///
/// cmux-pty's Unix backend returns `std::process::Child`, so failure to downcast
/// is an alternate backend and becomes an explicit unknown outcome.
#[cfg(test)]
pub(crate) fn wait_for_native_child_status(
child: &mut (dyn cmux_pty::Child + Send + Sync),
) -> TerminalExit {
wait_for_native_child_status_with_reap_result(child).0
}

/// Wait for a PTY child and report whether the wait reaped it successfully.
///
/// Callers that retain an owning guard can use the boolean to avoid issuing a
Expand Down Expand Up @@ -795,7 +807,7 @@ impl FrameDecoder {
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{mpsc, Arc, Mutex};
use std::sync::{Arc, Mutex, mpsc};

/// Test-only stand-in for a direct pipe reader. The bounded queue models
/// the byte pump, while the mutex is the single parser owner.
Expand Down Expand Up @@ -1104,7 +1116,7 @@ mod tests {
let mut command = cmux_pty::PtyCommand::new("/bin/sh");
command.args(["-c", script]);
let mut spawned = pty.spawn(command).unwrap();
wait_for_native_child_status_with_reap_result(spawned.child.as_mut()).0.outcome
wait_for_native_child_status(spawned.child.as_mut()).outcome
}

assert_eq!(run("exit 17"), TerminalExitOutcome::Exit { code: 17 });
Expand Down
14 changes: 6 additions & 8 deletions cmux-tui/crates/cmux-tui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6421,9 +6421,7 @@ const VIEWPORT_ANIMATION_SYNC_OPERATION_BUDGET: usize = PTY_OPERATION_QUEUE_CAPA

#[cfg(test)]
thread_local! {
static PANE_AREA_PROJECTION_WORK: std::cell::Cell<usize> = const {
std::cell::Cell::new(0)
};
static PANE_AREA_PROJECTION_WORK: Cell<usize> = const { Cell::new(0) };
}

#[cfg(test)]
Expand Down Expand Up @@ -25112,7 +25110,7 @@ mod tests {

#[test]
fn host_input_runtime_shutdown_joins_reader_before_returning() {
let mut runtime = HostInputRuntime::new();
let runtime = HostInputRuntime::new();
let ingress = runtime.ingress.clone();
let (events_tx, events_rx) = crossbeam_channel::bounded(1);
events_tx.send(AppEvent::HostInputReady).unwrap();
Expand All @@ -25139,7 +25137,7 @@ mod tests {

#[test]
fn host_input_runtime_shutdown_serializes_concurrent_callers() {
let mut runtime = HostInputRuntime::new();
let runtime = HostInputRuntime::new();
let ingress = runtime.ingress.clone();
let (closed_tx, closed_rx) = std::sync::mpsc::sync_channel(1);
let (release_tx, release_rx) = std::sync::mpsc::sync_channel(1);
Expand All @@ -25161,7 +25159,7 @@ mod tests {
});
closed_rx.recv().unwrap();

let second_shutdown = shutdown.clone();
let second_shutdown = shutdown;
let (second_done_tx, second_done_rx) = std::sync::mpsc::sync_channel(1);
let second = std::thread::spawn(move || {
second_shutdown.shutdown();
Expand Down Expand Up @@ -32894,7 +32892,7 @@ mod tests {
#[test]
fn graphics_changed_rect_bound_stays_within_linear_comparison_budget() {
let mux = Mux::new("graphics-diff-complexity-test", SurfaceOptions::default());
let mut app = test_app(Session::Local(mux));
let app = test_app(Session::Local(mux));
let count = 512usize;
let previous = (0..count)
.map(|index| GraphicIdentity {
Expand Down Expand Up @@ -34933,7 +34931,7 @@ mod tests {
surface_filter: None,
cancellation: cancellation.clone(),
};
let forwarder_recovery_generation = recovery_generation.clone();
let forwarder_recovery_generation = recovery_generation;
let forwarder = std::thread::spawn(move || {
forward_mux_events(
event_source,
Expand Down
9 changes: 5 additions & 4 deletions cmux-tui/crates/cmux-tui/src/plugin_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ fn validate_git_source(source: &str) -> anyhow::Result<()> {
Ok(())
}

#[cfg(test)]
fn is_sensitive_env_name(name: &str) -> bool {
let name = name.to_ascii_uppercase();
name.contains("TOKEN")
Expand Down Expand Up @@ -1068,12 +1069,12 @@ mod tests {
#[cfg(unix)]
#[test]
fn plugin_build_command_terminates_after_timeout() {
let mut command = std::process::Command::new("sh");
let mut command = Command::new("sh");
command.args(["-c", "while :; do :; done"]);
let started = std::time::Instant::now();
let error = run_plugin_build_command(&mut command, std::time::Duration::from_millis(20))
let started = Instant::now();
let error = run_plugin_build_command(&mut command, Duration::from_millis(20))
.expect_err("a busy build must time out");
assert!(started.elapsed() < std::time::Duration::from_secs(2));
assert!(started.elapsed() < Duration::from_secs(2));
assert!(error.to_string().contains("timed out"));
}
}
3 changes: 3 additions & 0 deletions cmux-tui/crates/cmux-tui/src/session/remote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1087,14 +1087,17 @@ impl PendingRemoteRequests {
progressed
}

#[cfg(test)]
fn is_empty(&self) -> bool {
self.requests.is_empty()
}

#[cfg(test)]
fn len(&self) -> usize {
self.requests.len()
}

#[cfg(test)]
fn values(&self) -> impl Iterator<Item = &PendingRemoteRequest> {
self.requests.values()
}
Expand Down
23 changes: 9 additions & 14 deletions cmux-tui/crates/cmux-tui/src/session/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ pub struct TabNotificationView {
}

impl TreeView {
#[cfg(test)]
pub(crate) fn from_parts(
workspaces: Vec<WorkspaceView>,
workspace_revision: u64,
Expand Down Expand Up @@ -349,11 +350,7 @@ impl TreeView {
true
}

pub fn active_workspace_mut(&mut self) -> Option<&mut WorkspaceView> {
self.invalidate_location_index();
self.workspaces.get_mut(self.active_workspace)
}

#[cfg(test)]
pub fn active_workspace_mut_screen(&mut self) -> Option<&mut ScreenView> {
self.invalidate_location_index();
let workspace = self.workspaces.get_mut(self.active_workspace)?;
Expand All @@ -376,6 +373,7 @@ impl TreeView {
.filter(|pane| pane.id == id)
}

#[cfg(test)]
pub fn pane_mut(&mut self, id: PaneId) -> Option<&mut PaneView> {
self.invalidate_location_index();
self.workspaces
Expand All @@ -398,6 +396,7 @@ impl TreeView {
.filter(|tab| tab.surface == id)
}

#[cfg(test)]
pub(crate) fn update_surface_title(&mut self, id: SurfaceId, title: &str) -> bool {
let (workspace_index, screen_index, pane_index, tab_index) = match self.surface_location(id)
{
Expand All @@ -418,15 +417,12 @@ impl TreeView {
[workspace_index, screen_index, pane_index, tab_index]: [usize; 4],
title: &str,
) -> Option<bool> {
let Some(tab) = self
let tab = self
.workspaces
.get_mut(workspace_index)
.and_then(|workspace| workspace.screens.get_mut(screen_index))
.and_then(|screen| screen.panes.get_mut(pane_index))
.and_then(|pane| pane.tabs.get_mut(tab_index))
else {
return None;
};
.and_then(|pane| pane.tabs.get_mut(tab_index))?;
if tab.surface != id {
return None;
}
Expand Down Expand Up @@ -833,8 +829,7 @@ fn parse_pane(value: &Value) -> Option<PaneView> {
.collect()
})
.unwrap_or_default(),
active_tab: active_tab
.unwrap_or_else(|| if active_tab_is_declared { usize::MAX } else { 0 }),
active_tab: active_tab.unwrap_or(if active_tab_is_declared { usize::MAX } else { 0 }),
})
}

Expand Down Expand Up @@ -967,8 +962,8 @@ pub(super) fn parse_tree_with_capabilities(
}
}
}
view.active_screen = active_screen
.unwrap_or_else(|| if active_screen_is_invalid { usize::MAX } else { 0 });
view.active_screen =
active_screen.unwrap_or(if active_screen_is_invalid { usize::MAX } else { 0 });
}
tree.workspaces.push(view);
}
Expand Down
4 changes: 2 additions & 2 deletions cmux-tui/crates/cmux-tui/src/ui/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ mod tests {

#[test]
fn visible_cursor_uses_terminal_cell_width() {
let mut input = text_input("界a");
let input = text_input("界a");

let (shown, cursor) = input.visible_text_and_cursor(4);

Expand Down Expand Up @@ -567,7 +567,7 @@ mod tests {

#[test]
fn visible_cursor_counts_halfwidth_sound_marks_as_terminal_cells() {
let mut input = text_input("ガa");
let input = text_input("ガa");

let (shown, cursor) = input.visible_text_and_cursor(4);

Expand Down
8 changes: 5 additions & 3 deletions cmux-tui/crates/cmux-tui/src/ui/terminal_grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -872,9 +872,11 @@ mod tests {
columns,
"Ghostty width {width:?} must remain authoritative"
);
let expected_diff_option = forced
.then(|| CellDiffOption::ForcedWidth(NonZeroU16::new(columns).unwrap()))
.unwrap_or(CellDiffOption::None);
let expected_diff_option = if forced {
CellDiffOption::ForcedWidth(NonZeroU16::new(columns).unwrap())
} else {
CellDiffOption::None
};
assert_eq!(target.diff_option, expected_diff_option);
}
}
Expand Down
Loading