1952 lines · 59.9 KB · Rust
RawDownload
use crate::{    persistence::DebuggerPaneItem,    tests::{start_debug_session, start_debug_session_with},    *,};use dap::{    ErrorResponse, Message, RunInTerminalRequestArguments, SourceBreakpoint,    StartDebuggingRequestArguments, StartDebuggingRequestArgumentsRequest,    adapters::DebugTaskDefinition,    client::SessionId,    requests::{        Continue, Disconnect, Launch, Next, RunInTerminal, SetBreakpoints, StackTrace,        StartDebugging, StepBack, StepIn, StepOut, Threads,    },};use editor::{    ActiveDebugLine, Editor, EditorMode, MultiBuffer,    actions::{self},};use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};use project::{    FakeFs, Project,    debugger::session::{ThreadId, ThreadStatus},};use serde_json::json;use std::{    path::Path,    sync::{        Arc,        atomic::{AtomicBool, Ordering},    },};use terminal_view::terminal_panel::TerminalPanel;use tests::{active_debug_session_panel, init_test, init_test_workspace};use util::{path, rel_path::rel_path};use workspace::item::SaveOptions;use workspace::{Item, dock::Panel}; #[gpui::test]async fn test_basic_show_debug_panel(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(&workspace, cx, |_| {}).unwrap();    let client = session.update(cx, |session, _| session.adapter_client().unwrap());     client.on_request::<Threads, _>(move |_, _| {        Ok(dap::ThreadsResponse {            threads: vec![dap::Thread {                id: 1,                name: "Thread 1".into(),            }],        })    });     client.on_request::<StackTrace, _>(move |_, _| {        Ok(dap::StackTraceResponse {            stack_frames: Vec::default(),            total_frames: None,        })    });     cx.run_until_parked();     // assert we have a debug panel item before the session has stopped    workspace        .update(cx, |workspace, _window, cx| {            let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();            let active_session =                debug_panel.update(cx, |debug_panel, _| debug_panel.active_session().unwrap());             let running_state = active_session.update(cx, |active_session, _| {                active_session.running_state().clone()            });             debug_panel.update(cx, |this, cx| {                assert!(this.active_session().is_some());                assert!(running_state.read(cx).selected_thread_id().is_none());            });        })        .unwrap();     client        .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {            reason: dap::StoppedEventReason::Pause,            description: None,            thread_id: Some(1),            preserve_focus_hint: None,            text: None,            all_threads_stopped: None,            hit_breakpoint_ids: None,        }))        .await;     cx.run_until_parked();     workspace        .update(cx, |workspace, _window, cx| {            let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();            let active_session = debug_panel                .update(cx, |this, _| this.active_session())                .unwrap();             let running_state = active_session.update(cx, |active_session, _| {                active_session.running_state().clone()            });             assert_eq!(client.id(), running_state.read(cx).session_id());            assert_eq!(                ThreadId(1),                running_state.read(cx).selected_thread_id().unwrap()            );        })        .unwrap();     let shutdown_session = project.update(cx, |project, cx| {        project.dap_store().update(cx, |dap_store, cx| {            dap_store.shutdown_session(session.read(cx).session_id(), cx)        })    });     shutdown_session.await.unwrap();     // assert we still have a debug panel item after the client shutdown    workspace        .update(cx, |workspace, _window, cx| {            let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();             let active_session = debug_panel                .update(cx, |this, _| this.active_session())                .unwrap();             let running_state = active_session.update(cx, |active_session, _| {                active_session.running_state().clone()            });             debug_panel.update(cx, |this, cx| {                assert!(this.active_session().is_some());                assert_eq!(                    ThreadId(1),                    running_state.read(cx).selected_thread_id().unwrap()                );            });        })        .unwrap();} #[gpui::test]async fn test_we_can_only_have_one_panel_per_debug_session(    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(&workspace, cx, |_| {}).unwrap();    let client = session.update(cx, |session, _| session.adapter_client().unwrap());     client.on_request::<Threads, _>(move |_, _| {        Ok(dap::ThreadsResponse {            threads: vec![dap::Thread {                id: 1,                name: "Thread 1".into(),            }],        })    });     client.on_request::<StackTrace, _>(move |_, _| {        Ok(dap::StackTraceResponse {            stack_frames: Vec::default(),            total_frames: None,        })    });     cx.run_until_parked();     // assert we have a debug panel item before the session has stopped    workspace        .update(cx, |workspace, _window, cx| {            let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();             debug_panel.update(cx, |this, _| {                assert!(this.active_session().is_some());            });        })        .unwrap();     client        .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {            reason: dap::StoppedEventReason::Pause,            description: None,            thread_id: Some(1),            preserve_focus_hint: None,            text: None,            all_threads_stopped: None,            hit_breakpoint_ids: None,        }))        .await;     cx.run_until_parked();     // assert we added a debug panel item    workspace        .update(cx, |workspace, _window, cx| {            let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();            let active_session = debug_panel                .update(cx, |this, _| this.active_session())                .unwrap();             let running_state = active_session.update(cx, |active_session, _| {                active_session.running_state().clone()            });             assert_eq!(client.id(), active_session.read(cx).session_id(cx));            assert_eq!(                ThreadId(1),                running_state.read(cx).selected_thread_id().unwrap()            );        })        .unwrap();     client        .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {            reason: dap::StoppedEventReason::Pause,            description: None,            thread_id: Some(2),            preserve_focus_hint: None,            text: None,            all_threads_stopped: None,            hit_breakpoint_ids: None,        }))        .await;     cx.run_until_parked();     workspace        .update(cx, |workspace, _window, cx| {            let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();            let active_session = debug_panel                .update(cx, |this, _| this.active_session())                .unwrap();             let running_state = active_session.update(cx, |active_session, _| {                active_session.running_state().clone()            });             assert_eq!(client.id(), active_session.read(cx).session_id(cx));            assert_eq!(                ThreadId(1),                running_state.read(cx).selected_thread_id().unwrap()            );        })        .unwrap();     let shutdown_session = project.update(cx, |project, cx| {        project.dap_store().update(cx, |dap_store, cx| {            dap_store.shutdown_session(session.read(cx).session_id(), cx)        })    });     shutdown_session.await.unwrap();     // assert we still have a debug panel item after the client shutdown    workspace        .update(cx, |workspace, _window, cx| {            let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();            let active_session = debug_panel                .update(cx, |this, _| this.active_session())                .unwrap();             let running_state = active_session.update(cx, |active_session, _| {                active_session.running_state().clone()            });             debug_panel.update(cx, |this, cx| {                assert!(this.active_session().is_some());                assert_eq!(                    ThreadId(1),                    running_state.read(cx).selected_thread_id().unwrap()                );            });        })        .unwrap();} #[gpui::test]async fn test_handle_successful_run_in_terminal_reverse_request(    executor: BackgroundExecutor,    cx: &mut TestAppContext,) {    // needed because the debugger launches a terminal which starts a background PTY    cx.executor().allow_parking();    init_test(cx);     let send_response = Arc::new(AtomicBool::new(false));     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(&workspace, cx, |_| {}).unwrap();    let client = session.update(cx, |session, _| session.adapter_client().unwrap());     client        .on_response::<RunInTerminal, _>({            let send_response = send_response.clone();            move |response| {                send_response.store(true, Ordering::SeqCst);                 assert!(response.success);                assert!(response.body.is_some());            }        })        .await;     client        .fake_reverse_request::<RunInTerminal>(RunInTerminalRequestArguments {            kind: None,            title: None,            cwd: std::env::temp_dir().to_string_lossy().into_owned(),            args: vec![],            env: None,            args_can_be_interpreted_by_shell: None,        })        .await;     cx.run_until_parked();     assert!(        send_response.load(std::sync::atomic::Ordering::SeqCst),        "Expected to receive response from reverse request"    );     workspace        .update(cx, |workspace, _window, cx| {            let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();            let session = debug_panel.read(cx).active_session().unwrap();            let running = session.read(cx).running_state();            assert_eq!(                running                    .read(cx)                    .pane_items_status(cx)                    .get(&DebuggerPaneItem::Terminal),                Some(&true)            );            assert!(running.read(cx).debug_terminal.read(cx).terminal.is_some());        })        .unwrap();} #[gpui::test]async fn test_handle_start_debugging_request(    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(&workspace, cx, |_| {}).unwrap();    let client = session.update(cx, |session, _| session.adapter_client().unwrap());     let fake_config = json!({"one": "two"});    let launched_with = Arc::new(parking_lot::Mutex::new(None));     let _subscription = project::debugger::test::intercept_debug_sessions(cx, {        let launched_with = launched_with.clone();        move |client| {            let launched_with = launched_with.clone();            client.on_request::<dap::requests::Launch, _>(move |_, args| {                launched_with.lock().replace(args.raw);                Ok(())            });            client.on_request::<dap::requests::Attach, _>(move |_, _| {                assert!(false, "should not get attach request");                Ok(())            });        }    });     let sessions = workspace        .update(cx, |workspace, _window, cx| {            let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();            debug_panel.read(cx).sessions().collect::<Vec<_>>()        })        .unwrap();    assert_eq!(sessions.len(), 1);    client        .fake_reverse_request::<StartDebugging>(StartDebuggingRequestArguments {            request: StartDebuggingRequestArgumentsRequest::Launch,            configuration: fake_config.clone(),        })        .await;     cx.run_until_parked();     workspace        .update(cx, |workspace, _window, cx| {            let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();             // Active session changes on spawn, as the parent has never stopped.            let active_session = debug_panel                .read(cx)                .active_session()                .unwrap()                .read(cx)                .session(cx);            let current_sessions = debug_panel.read(cx).sessions().collect::<Vec<_>>();            assert_eq!(active_session, current_sessions[1].read(cx).session(cx));            assert_eq!(                active_session.read(cx).parent_session(),                Some(&current_sessions[0].read(cx).session(cx))            );             assert_eq!(current_sessions.len(), 2);            assert_eq!(current_sessions[0], sessions[0]);             let parent_session = current_sessions[1]                .read(cx)                .session(cx)                .read(cx)                .parent_session()                .unwrap();            assert_eq!(parent_session, &sessions[0].read(cx).session(cx));             // We should preserve the original binary (params to spawn process etc.) except for launch params            // (as they come from reverse spawn request).            let mut original_binary = parent_session.read(cx).binary().cloned().unwrap();            original_binary.request_args = StartDebuggingRequestArguments {                request: StartDebuggingRequestArgumentsRequest::Launch,                configuration: fake_config.clone(),            };             assert_eq!(                current_sessions[1]                    .read(cx)                    .session(cx)                    .read(cx)                    .binary()                    .unwrap(),                &original_binary            );        })        .unwrap();     assert_eq!(&fake_config, launched_with.lock().as_ref().unwrap());} // // covers that we always send a response back, if something when wrong,// // while spawning the terminal#[gpui::test]async fn test_handle_error_run_in_terminal_reverse_request(

Lines 1–500 of 1952.