Closes #ISSUE Before you mark this PR as ready for review, make sure that you have: - [ ] Added a solid test coverage and/or screenshots from doing manual testing - [ ] Done a self-review taking into account security and performance aspects - [ ] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) Release Notes: - N/A
321 lines
9.8 KiB
Rust
321 lines
9.8 KiB
Rust
#![expect(clippy::result_large_err)]
|
|
use crate::{
|
|
attach_modal::{Candidate, ModalIntent},
|
|
tests::start_debug_session_with,
|
|
*,
|
|
};
|
|
use attach_modal::AttachModal;
|
|
use dap::{FakeAdapter, adapters::DebugTaskDefinition};
|
|
use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
|
|
use menu::Confirm;
|
|
use project::{FakeFs, Project};
|
|
use serde_json::json;
|
|
use task::{AttachRequest, SharedTaskContext};
|
|
use tests::{init_test, init_test_workspace};
|
|
use util::path;
|
|
|
|
#[gpui::test]
|
|
async fn test_direct_attach_to_process(executor: BackgroundExecutor, cx: &mut TestAppContext) {
|
|
init_test(cx);
|
|
|
|
let fs = FakeFs::new(executor.clone());
|
|
|
|
fs.insert_tree(
|
|
path!("/project"),
|
|
json!({
|
|
"main.rs": "First line\nSecond line\nThird line\nFourth line",
|
|
}),
|
|
)
|
|
.await;
|
|
|
|
let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
|
|
let workspace = init_test_workspace(&project, cx).await;
|
|
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
|
|
|
let _session = start_debug_session_with(
|
|
&workspace,
|
|
cx,
|
|
DebugTaskDefinition {
|
|
adapter: "fake-adapter".into(),
|
|
label: "label".into(),
|
|
config: json!({
|
|
"request": "attach",
|
|
"process_id": 10,
|
|
}),
|
|
tcp_connection: None,
|
|
},
|
|
|client| {
|
|
client.on_request::<dap::requests::Attach, _>(move |_, args| {
|
|
let raw = &args.raw;
|
|
assert_eq!(raw["request"], "attach");
|
|
assert_eq!(raw["process_id"], 10);
|
|
|
|
Ok(())
|
|
});
|
|
},
|
|
)
|
|
.unwrap();
|
|
|
|
cx.run_until_parked();
|
|
|
|
// assert we didn't show the attach modal
|
|
workspace
|
|
.update(cx, |workspace, _window, cx| {
|
|
assert!(
|
|
workspace
|
|
.workspace()
|
|
.read(cx)
|
|
.active_modal::<AttachModal>(cx)
|
|
.is_none()
|
|
);
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_show_attach_modal_and_select_process(
|
|
executor: BackgroundExecutor,
|
|
cx: &mut TestAppContext,
|
|
) {
|
|
init_test(cx);
|
|
|
|
let fs = FakeFs::new(executor.clone());
|
|
|
|
fs.insert_tree(
|
|
path!("/project"),
|
|
json!({
|
|
"main.rs": "First line\nSecond line\nThird line\nFourth line",
|
|
}),
|
|
)
|
|
.await;
|
|
|
|
let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
|
|
let workspace = init_test_workspace(&project, cx).await;
|
|
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
|
// Set up handlers for sessions spawned via modal.
|
|
let _initialize_subscription =
|
|
project::debugger::test::intercept_debug_sessions(cx, |client| {
|
|
client.on_request::<dap::requests::Attach, _>(move |_, args| {
|
|
let raw = &args.raw;
|
|
assert_eq!(raw["request"], "attach");
|
|
assert_eq!(raw["process_id"], 1);
|
|
|
|
Ok(())
|
|
});
|
|
});
|
|
let attach_modal = workspace
|
|
.update(cx, |multi, window, cx| {
|
|
let workspace_handle = multi.workspace().downgrade();
|
|
multi.toggle_modal(window, cx, |window, cx| {
|
|
AttachModal::with_processes(
|
|
workspace_handle,
|
|
vec![
|
|
Candidate {
|
|
pid: 0,
|
|
name: "fake-binary-1".into(),
|
|
command: vec![],
|
|
},
|
|
Candidate {
|
|
pid: 3,
|
|
name: "real-binary-1".into(),
|
|
command: vec![],
|
|
},
|
|
Candidate {
|
|
pid: 1,
|
|
name: "fake-binary-2".into(),
|
|
command: vec![],
|
|
},
|
|
]
|
|
.into_iter()
|
|
.collect(),
|
|
true,
|
|
ModalIntent::AttachToProcess(task::ZedDebugConfig {
|
|
adapter: FakeAdapter::ADAPTER_NAME.into(),
|
|
request: dap::DebugRequest::Attach(AttachRequest::default()),
|
|
label: "attach example".into(),
|
|
stop_on_entry: None,
|
|
}),
|
|
window,
|
|
cx,
|
|
)
|
|
});
|
|
|
|
multi.active_modal::<AttachModal>(cx).unwrap()
|
|
})
|
|
.unwrap();
|
|
|
|
cx.run_until_parked();
|
|
|
|
// assert we got the expected processes
|
|
workspace
|
|
.update(cx, |_, window, cx| {
|
|
let names = attach_modal.update(cx, |modal, cx| attach_modal::process_names(modal, cx));
|
|
// Initially all processes are visible.
|
|
assert_eq!(3, names.len());
|
|
attach_modal.update(cx, |this, cx| {
|
|
this.picker.update(cx, |this, cx| {
|
|
this.set_query("fakb", window, cx);
|
|
})
|
|
})
|
|
})
|
|
.unwrap();
|
|
cx.run_until_parked();
|
|
// assert we got the expected processes
|
|
workspace
|
|
.update(cx, |_, _, cx| {
|
|
let names = attach_modal.update(cx, |modal, cx| attach_modal::process_names(modal, cx));
|
|
// Initially all processes are visible.
|
|
assert_eq!(2, names.len());
|
|
})
|
|
.unwrap();
|
|
// select the only existing process
|
|
cx.dispatch_action(Confirm);
|
|
|
|
cx.run_until_parked();
|
|
|
|
// assert attach modal was dismissed
|
|
workspace
|
|
.update(cx, |workspace, _window, cx| {
|
|
assert!(workspace.active_modal::<AttachModal>(cx).is_none());
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_attach_with_pick_pid_variable(executor: BackgroundExecutor, cx: &mut TestAppContext) {
|
|
init_test(cx);
|
|
|
|
let fs = FakeFs::new(executor.clone());
|
|
|
|
fs.insert_tree(
|
|
path!("/project"),
|
|
json!({
|
|
"main.rs": "First line\nSecond line\nThird line\nFourth line",
|
|
}),
|
|
)
|
|
.await;
|
|
|
|
let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
|
|
let workspace = init_test_workspace(&project, cx).await;
|
|
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
|
|
|
let _initialize_subscription =
|
|
project::debugger::test::intercept_debug_sessions(cx, |client| {
|
|
client.on_request::<dap::requests::Attach, _>(move |_, args| {
|
|
let raw = &args.raw;
|
|
assert_eq!(raw["request"], "attach");
|
|
assert_eq!(
|
|
raw["process_id"], "42",
|
|
"verify process id has been replaced"
|
|
);
|
|
|
|
Ok(())
|
|
});
|
|
});
|
|
|
|
let pick_pid_placeholder = task::VariableName::PickProcessId.template_value();
|
|
workspace
|
|
.update(cx, |multi, window, cx| {
|
|
multi.workspace().update(cx, |workspace, cx| {
|
|
workspace.start_debug_session(
|
|
DebugTaskDefinition {
|
|
adapter: FakeAdapter::ADAPTER_NAME.into(),
|
|
label: "attach with picker".into(),
|
|
config: json!({
|
|
"request": "attach",
|
|
"process_id": pick_pid_placeholder,
|
|
}),
|
|
tcp_connection: None,
|
|
}
|
|
.to_scenario(),
|
|
SharedTaskContext::default(),
|
|
None,
|
|
None,
|
|
window,
|
|
cx,
|
|
);
|
|
})
|
|
})
|
|
.unwrap();
|
|
|
|
cx.run_until_parked();
|
|
|
|
let attach_modal = workspace
|
|
.update(cx, |workspace, _window, cx| {
|
|
workspace.active_modal::<AttachModal>(cx)
|
|
})
|
|
.unwrap();
|
|
|
|
assert!(
|
|
attach_modal.is_some(),
|
|
"Attach modal should open when config contains ZED_PICK_PID"
|
|
);
|
|
|
|
let attach_modal = attach_modal.unwrap();
|
|
|
|
workspace
|
|
.update(cx, |_, window, cx| {
|
|
attach_modal.update(cx, |modal, cx| {
|
|
attach_modal::set_candidates(
|
|
modal,
|
|
vec![
|
|
Candidate {
|
|
pid: 10,
|
|
name: "process-1".into(),
|
|
command: vec![],
|
|
},
|
|
Candidate {
|
|
pid: 42,
|
|
name: "target-process".into(),
|
|
command: vec![],
|
|
},
|
|
Candidate {
|
|
pid: 99,
|
|
name: "process-3".into(),
|
|
command: vec![],
|
|
},
|
|
]
|
|
.into_iter()
|
|
.collect(),
|
|
window,
|
|
cx,
|
|
)
|
|
})
|
|
})
|
|
.unwrap();
|
|
|
|
cx.run_until_parked();
|
|
|
|
workspace
|
|
.update(cx, |_, window, cx| {
|
|
attach_modal.update(cx, |modal, cx| {
|
|
modal.picker.update(cx, |picker, cx| {
|
|
picker.set_query("target", window, cx);
|
|
})
|
|
})
|
|
})
|
|
.unwrap();
|
|
|
|
cx.run_until_parked();
|
|
|
|
workspace
|
|
.update(cx, |_, _, cx| {
|
|
let names = attach_modal.update(cx, |modal, cx| attach_modal::process_names(modal, cx));
|
|
assert_eq!(names.len(), 1);
|
|
assert_eq!(names[0], " 42 target-process");
|
|
})
|
|
.unwrap();
|
|
|
|
cx.dispatch_action(Confirm);
|
|
cx.run_until_parked();
|
|
|
|
workspace
|
|
.update(cx, |workspace, _window, cx| {
|
|
assert!(
|
|
workspace.active_modal::<AttachModal>(cx).is_none(),
|
|
"Attach modal should be dismissed after selection"
|
|
);
|
|
})
|
|
.unwrap();
|
|
}
|