module_list.rs

  1use crate::{
  2    debugger_panel::DebugPanel,
  3    tests::{active_debug_session_panel, init_test, init_test_workspace, start_debug_session},
  4};
  5use dap::{
  6    StoppedEvent,
  7    requests::{Initialize, Modules},
  8};
  9use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
 10use project::{FakeFs, Project};
 11use std::sync::{
 12    Arc,
 13    atomic::{AtomicBool, AtomicI32, Ordering},
 14};
 15use util::path;
 16
 17#[gpui::test]
 18async fn test_module_list(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 19    init_test(cx);
 20
 21    let fs = FakeFs::new(executor.clone());
 22
 23    let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
 24    let workspace = init_test_workspace(&project, cx).await;
 25    workspace
 26        .update(cx, |workspace, window, cx| {
 27            workspace.focus_panel::<DebugPanel>(window, cx);
 28        })
 29        .unwrap();
 30    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 31
 32    let session = start_debug_session(&workspace, cx, |client| {
 33        client.on_request::<Initialize, _>(move |_, _| {
 34            Ok(dap::Capabilities {
 35                supports_modules_request: Some(true),
 36                ..Default::default()
 37            })
 38        });
 39    })
 40    .unwrap();
 41
 42    let client = session.update(cx, |session, _| session.adapter_client().unwrap());
 43
 44    let called_modules = Arc::new(AtomicBool::new(false));
 45    let modules = vec![
 46        dap::Module {
 47            id: dap::ModuleId::Number(1),
 48            name: "First Module".into(),
 49            address_range: None,
 50            date_time_stamp: None,
 51            path: None,
 52            symbol_file_path: None,
 53            symbol_status: None,
 54            version: None,
 55            is_optimized: None,
 56            is_user_code: None,
 57        },
 58        dap::Module {
 59            id: dap::ModuleId::Number(2),
 60            name: "Second Module".into(),
 61            address_range: None,
 62            date_time_stamp: None,
 63            path: None,
 64            symbol_file_path: None,
 65            symbol_status: None,
 66            version: None,
 67            is_optimized: None,
 68            is_user_code: None,
 69        },
 70    ];
 71
 72    client.on_request::<Modules, _>({
 73        let called_modules = called_modules.clone();
 74        let modules_request_count = AtomicI32::new(0);
 75        let modules = modules.clone();
 76        move |_, _| {
 77            modules_request_count.fetch_add(1, Ordering::SeqCst);
 78            assert_eq!(
 79                1,
 80                modules_request_count.load(Ordering::SeqCst),
 81                "This request should only be called once from the host"
 82            );
 83            called_modules.store(true, Ordering::SeqCst);
 84
 85            Ok(dap::ModulesResponse {
 86                modules: modules.clone(),
 87                total_modules: Some(2u64),
 88            })
 89        }
 90    });
 91
 92    client
 93        .fake_event(dap::messages::Events::Stopped(StoppedEvent {
 94            reason: dap::StoppedEventReason::Pause,
 95            description: None,
 96            thread_id: Some(1),
 97            preserve_focus_hint: None,
 98            text: None,
 99            all_threads_stopped: None,
100            hit_breakpoint_ids: None,
101        }))
102        .await;
103
104    cx.run_until_parked();
105
106    let running_state =
107        active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
108            cx.focus_self(window);
109            item.running_state().clone()
110        });
111
112    running_state.update_in(cx, |this, window, cx| {
113        this.activate_item(crate::persistence::DebuggerPaneItem::Modules, window, cx);
114        cx.refresh_windows();
115    });
116
117    cx.run_until_parked();
118
119    assert!(
120        called_modules.load(std::sync::atomic::Ordering::SeqCst),
121        "Request Modules should be called because a user clicked on the module list"
122    );
123
124    active_debug_session_panel(workspace, cx).update(cx, |_, cx| {
125        let actual_modules = running_state.update(cx, |state, cx| {
126            state.module_list().update(cx, |list, cx| list.modules(cx))
127        });
128
129        assert_eq!(modules, actual_modules);
130    });
131
132    // Test all module events now
133    // New Module
134    // Changed
135    // Removed
136
137    let new_module = dap::Module {
138        id: dap::ModuleId::Number(3),
139        name: "Third Module".into(),
140        address_range: None,
141        date_time_stamp: None,
142        path: None,
143        symbol_file_path: None,
144        symbol_status: None,
145        version: None,
146        is_optimized: None,
147        is_user_code: None,
148    };
149
150    client
151        .fake_event(dap::messages::Events::Module(dap::ModuleEvent {
152            reason: dap::ModuleEventReason::New,
153            module: new_module.clone(),
154        }))
155        .await;
156
157    cx.run_until_parked();
158
159    active_debug_session_panel(workspace, cx).update(cx, |_, cx| {
160        let actual_modules = running_state.update(cx, |state, cx| {
161            state.module_list().update(cx, |list, cx| list.modules(cx))
162        });
163        assert_eq!(actual_modules.len(), 3);
164        assert!(actual_modules.contains(&new_module));
165    });
166
167    let changed_module = dap::Module {
168        id: dap::ModuleId::Number(2),
169        name: "Modified Second Module".into(),
170        address_range: None,
171        date_time_stamp: None,
172        path: None,
173        symbol_file_path: None,
174        symbol_status: None,
175        version: None,
176        is_optimized: None,
177        is_user_code: None,
178    };
179
180    client
181        .fake_event(dap::messages::Events::Module(dap::ModuleEvent {
182            reason: dap::ModuleEventReason::Changed,
183            module: changed_module.clone(),
184        }))
185        .await;
186
187    cx.run_until_parked();
188
189    active_debug_session_panel(workspace, cx).update(cx, |_, cx| {
190        let actual_modules = running_state.update(cx, |state, cx| {
191            state.module_list().update(cx, |list, cx| list.modules(cx))
192        });
193
194        assert_eq!(actual_modules.len(), 3);
195        assert!(actual_modules.contains(&changed_module));
196    });
197
198    client
199        .fake_event(dap::messages::Events::Module(dap::ModuleEvent {
200            reason: dap::ModuleEventReason::Removed,
201            module: changed_module.clone(),
202        }))
203        .await;
204
205    cx.run_until_parked();
206
207    active_debug_session_panel(workspace, cx).update(cx, |_, cx| {
208        let actual_modules = running_state.update(cx, |state, cx| {
209            state.module_list().update(cx, |list, cx| list.modules(cx))
210        });
211
212        assert_eq!(actual_modules.len(), 2);
213        assert!(!actual_modules.contains(&changed_module));
214    });
215}