dap_logger.rs

  1use crate::tests::{init_test, init_test_workspace, start_debug_session};
  2use dap::requests::{StackTrace, Threads};
  3use debugger_tools::LogStore;
  4use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
  5use project::Project;
  6use serde_json::json;
  7use std::cell::OnceCell;
  8use util::path;
  9
 10#[gpui::test]
 11async fn test_dap_logger_captures_all_session_rpc_messages(
 12    executor: BackgroundExecutor,
 13    cx: &mut TestAppContext,
 14) {
 15    let log_store_cell = std::rc::Rc::new(OnceCell::new());
 16
 17    cx.update(|cx| {
 18        let log_store_cell = log_store_cell.clone();
 19        cx.observe_new::<LogStore>(move |_, _, cx| {
 20            log_store_cell.set(cx.entity()).unwrap();
 21        })
 22        .detach();
 23        debugger_tools::init(cx);
 24    });
 25    init_test(cx);
 26
 27    let log_store = log_store_cell.get().unwrap().clone();
 28
 29    // Create a filesystem with a simple project
 30    let fs = project::FakeFs::new(executor.clone());
 31    fs.insert_tree(
 32        path!("/project"),
 33        json!({
 34            "main.rs": "fn main() {\n    println!(\"Hello, world!\");\n}"
 35        }),
 36    )
 37    .await;
 38
 39    assert!(
 40        log_store.read_with(cx, |log_store, _| log_store
 41            .contained_session_ids()
 42            .is_empty()),
 43        "log_store shouldn't contain any session IDs before any sessions were created"
 44    );
 45
 46    let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
 47
 48    let workspace = init_test_workspace(&project, cx).await;
 49    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 50
 51    // Start a debug session
 52    let session = start_debug_session(&workspace, cx, |_| {}).unwrap();
 53    let session_id = session.read_with(cx, |session, _| session.session_id());
 54    let client = session.update(cx, |session, _| session.adapter_client().unwrap());
 55
 56    assert_eq!(
 57        log_store.read_with(cx, |log_store, _| log_store.contained_session_ids().len()),
 58        1,
 59    );
 60
 61    assert!(
 62        log_store.read_with(cx, |log_store, _| log_store
 63            .contained_session_ids()
 64            .contains(&session_id)),
 65        "log_store should contain the session IDs of the started session"
 66    );
 67
 68    assert!(
 69        !log_store.read_with(cx, |log_store, _| log_store
 70            .rpc_messages_for_session_id(session_id)
 71            .is_empty()),
 72        "We should have the initialization sequence in the log store"
 73    );
 74
 75    // Set up basic responses for common requests
 76    client.on_request::<Threads, _>(move |_, _| {
 77        Ok(dap::ThreadsResponse {
 78            threads: vec![dap::Thread {
 79                id: 1,
 80                name: "Thread 1".into(),
 81            }],
 82        })
 83    });
 84
 85    client.on_request::<StackTrace, _>(move |_, _| {
 86        Ok(dap::StackTraceResponse {
 87            stack_frames: Vec::default(),
 88            total_frames: None,
 89        })
 90    });
 91
 92    // Run until all pending tasks are executed
 93    cx.run_until_parked();
 94
 95    // Simulate a stopped event to generate more DAP messages
 96    client
 97        .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
 98            reason: dap::StoppedEventReason::Pause,
 99            description: None,
100            thread_id: Some(1),
101            preserve_focus_hint: None,
102            text: None,
103            all_threads_stopped: None,
104            hit_breakpoint_ids: None,
105        }))
106        .await;
107}