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