debugger.rs

  1mod go_locator {
  2    use collections::HashMap;
  3    use dap::{DapLocator, adapters::DebugAdapterName};
  4    use gpui::TestAppContext;
  5    use project::debugger::locators::go::{DelveLaunchRequest, GoLocator};
  6    use task::{HideStrategy, RevealStrategy, RevealTarget, SaveStrategy, Shell, TaskTemplate};
  7    #[gpui::test]
  8    async fn test_create_scenario_for_go_build(_: &mut TestAppContext) {
  9        let locator = GoLocator;
 10        let task = TaskTemplate {
 11            label: "go build".into(),
 12            command: "go".into(),
 13            args: vec!["build".into(), ".".into()],
 14            env: Default::default(),
 15            cwd: Some("${ZED_WORKTREE_ROOT}".into()),
 16            use_new_terminal: false,
 17            allow_concurrent_runs: false,
 18            reveal: RevealStrategy::Always,
 19            reveal_target: RevealTarget::Dock,
 20            hide: HideStrategy::Never,
 21            shell: Shell::System,
 22            tags: vec![],
 23            show_summary: true,
 24            show_command: true,
 25            save: SaveStrategy::default(),
 26        };
 27
 28        let scenario = locator
 29            .create_scenario(&task, "test label", &DebugAdapterName("Delve".into()))
 30            .await;
 31
 32        assert!(scenario.is_none());
 33    }
 34
 35    #[gpui::test]
 36    async fn test_skip_non_go_commands_with_non_delve_adapter(_: &mut TestAppContext) {
 37        let locator = GoLocator;
 38        let task = TaskTemplate {
 39            label: "cargo build".into(),
 40            command: "cargo".into(),
 41            args: vec!["build".into()],
 42            env: Default::default(),
 43            cwd: Some("${ZED_WORKTREE_ROOT}".into()),
 44            use_new_terminal: false,
 45            allow_concurrent_runs: false,
 46            reveal: RevealStrategy::Always,
 47            reveal_target: RevealTarget::Dock,
 48            hide: HideStrategy::Never,
 49            shell: Shell::System,
 50            tags: vec![],
 51            show_summary: true,
 52            show_command: true,
 53            save: SaveStrategy::default(),
 54        };
 55
 56        let scenario = locator
 57            .create_scenario(
 58                &task,
 59                "test label",
 60                &DebugAdapterName("SomeOtherAdapter".into()),
 61            )
 62            .await;
 63        assert!(scenario.is_none());
 64
 65        let scenario = locator
 66            .create_scenario(&task, "test label", &DebugAdapterName("Delve".into()))
 67            .await;
 68        assert!(scenario.is_none());
 69    }
 70    #[gpui::test]
 71    async fn test_go_locator_run(_: &mut TestAppContext) {
 72        let locator = GoLocator;
 73        let delve = DebugAdapterName("Delve".into());
 74
 75        let task = TaskTemplate {
 76            label: "go run with flags".into(),
 77            command: "go".into(),
 78            args: vec![
 79                "run".to_string(),
 80                "-race".to_string(),
 81                "-ldflags".to_string(),
 82                "-X main.version=1.0".to_string(),
 83                "./cmd/myapp".to_string(),
 84                "--config".to_string(),
 85                "production.yaml".to_string(),
 86                "--verbose".to_string(),
 87            ],
 88            env: {
 89                let mut env = HashMap::default();
 90                env.insert("GO_ENV".to_string(), "production".to_string());
 91                env
 92            },
 93            cwd: Some("/project/root".into()),
 94            ..Default::default()
 95        };
 96
 97        let scenario = locator
 98            .create_scenario(&task, "test run label", &delve)
 99            .await
100            .unwrap();
101
102        let config: DelveLaunchRequest = serde_json::from_value(scenario.config).unwrap();
103
104        assert_eq!(
105            config,
106            DelveLaunchRequest {
107                request: "launch".to_string(),
108                mode: "debug".to_string(),
109                program: "./cmd/myapp".to_string(),
110                build_flags: vec![
111                    "-race".to_string(),
112                    "-ldflags".to_string(),
113                    "-X main.version=1.0".to_string()
114                ],
115                args: vec![
116                    "--config".to_string(),
117                    "production.yaml".to_string(),
118                    "--verbose".to_string(),
119                ],
120                env: {
121                    let mut env = HashMap::default();
122                    env.insert("GO_ENV".to_string(), "production".to_string());
123                    env
124                },
125                cwd: Some("/project/root".to_string()),
126            }
127        );
128    }
129
130    #[gpui::test]
131    async fn test_go_locator_test(_: &mut TestAppContext) {
132        let locator = GoLocator;
133        let delve = DebugAdapterName("Delve".into());
134
135        // Test with tags and run flag
136        let task_with_tags = TaskTemplate {
137            label: "test".into(),
138            command: "go".into(),
139            args: vec![
140                "test".to_string(),
141                "-tags".to_string(),
142                "integration,unit".to_string(),
143                "-run".to_string(),
144                "Foo".to_string(),
145                ".".to_string(),
146            ],
147            ..Default::default()
148        };
149        let result = locator
150            .create_scenario(&task_with_tags, "", &delve)
151            .await
152            .unwrap();
153
154        let config: DelveLaunchRequest = serde_json::from_value(result.config).unwrap();
155
156        assert_eq!(
157            config,
158            DelveLaunchRequest {
159                request: "launch".to_string(),
160                mode: "test".to_string(),
161                program: ".".to_string(),
162                build_flags: vec!["-tags".to_string(), "integration,unit".to_string(),],
163                args: vec![
164                    "-test.run".to_string(),
165                    "Foo".to_string(),
166                    "-test.v".to_string()
167                ],
168                env: Default::default(),
169                cwd: None,
170            }
171        );
172    }
173
174    #[gpui::test]
175    async fn test_skip_unsupported_go_commands(_: &mut TestAppContext) {
176        let locator = GoLocator;
177        let task = TaskTemplate {
178            label: "go clean".into(),
179            command: "go".into(),
180            args: vec!["clean".into()],
181            env: Default::default(),
182            cwd: Some("${ZED_WORKTREE_ROOT}".into()),
183            use_new_terminal: false,
184            allow_concurrent_runs: false,
185            reveal: RevealStrategy::Always,
186            reveal_target: RevealTarget::Dock,
187            hide: HideStrategy::Never,
188            shell: Shell::System,
189            tags: vec![],
190            show_summary: true,
191            show_command: true,
192            save: SaveStrategy::default(),
193        };
194
195        let scenario = locator
196            .create_scenario(&task, "test label", &DebugAdapterName("Delve".into()))
197            .await;
198        assert!(scenario.is_none());
199    }
200}
201
202mod python_locator {
203    use dap::{DapLocator, adapters::DebugAdapterName};
204    use serde_json::json;
205
206    use project::debugger::locators::python::*;
207    use task::{DebugScenario, TaskTemplate};
208
209    #[gpui::test]
210    async fn test_python_locator() {
211        let adapter = DebugAdapterName("Debugpy".into());
212        let build_task = TaskTemplate {
213            label: "run module '$ZED_FILE'".into(),
214            command: "$ZED_CUSTOM_PYTHON_ACTIVE_ZED_TOOLCHAIN".into(),
215            args: vec!["-m".into(), "$ZED_CUSTOM_PYTHON_MODULE_NAME".into()],
216            env: Default::default(),
217            cwd: Some("$ZED_WORKTREE_ROOT".into()),
218            use_new_terminal: false,
219            allow_concurrent_runs: false,
220            reveal: task::RevealStrategy::Always,
221            reveal_target: task::RevealTarget::Dock,
222            hide: task::HideStrategy::Never,
223            tags: vec!["python-module-main-method".into()],
224            shell: task::Shell::System,
225            show_summary: false,
226            show_command: false,
227            save: task::SaveStrategy::default(),
228        };
229
230        let expected_scenario = DebugScenario {
231            adapter: "Debugpy".into(),
232            label: "run module 'main.py'".into(),
233            build: None,
234            config: json!({
235                "request": "launch",
236                "python": "$ZED_CUSTOM_PYTHON_ACTIVE_ZED_TOOLCHAIN",
237                "args": [],
238                "cwd": "$ZED_WORKTREE_ROOT",
239                "module": "$ZED_CUSTOM_PYTHON_MODULE_NAME",
240            }),
241            tcp_connection: None,
242        };
243
244        assert_eq!(
245            PythonLocator
246                .create_scenario(&build_task, "run module 'main.py'", &adapter)
247                .await
248                .expect("Failed to create a scenario"),
249            expected_scenario
250        );
251    }
252}
253
254mod memory {
255    use project::debugger::{
256        MemoryCell,
257        memory::{MemoryIterator, PageAddress, PageContents},
258    };
259
260    #[test]
261    fn iterate_over_unmapped_memory() {
262        let empty_iterator = MemoryIterator::new(0..=127, Default::default());
263        let actual = empty_iterator.collect::<Vec<_>>();
264        let expected = vec![MemoryCell(None); 128];
265        assert_eq!(actual.len(), expected.len());
266        assert_eq!(actual, expected);
267    }
268
269    #[test]
270    fn iterate_over_partially_mapped_memory() {
271        let it = MemoryIterator::new(
272            0..=127,
273            vec![(PageAddress(5), PageContents::mapped(vec![1]))].into_iter(),
274        );
275        let actual = it.collect::<Vec<_>>();
276        let expected = std::iter::repeat_n(MemoryCell(None), 5)
277            .chain(std::iter::once(MemoryCell(Some(1))))
278            .chain(std::iter::repeat_n(MemoryCell(None), 122))
279            .collect::<Vec<_>>();
280        assert_eq!(actual.len(), expected.len());
281        assert_eq!(actual, expected);
282    }
283
284    #[test]
285    fn reads_from_the_middle_of_a_page() {
286        let partial_iter = MemoryIterator::new(
287            20..=30,
288            vec![(PageAddress(0), PageContents::mapped((0..255).collect()))].into_iter(),
289        );
290        let actual = partial_iter.collect::<Vec<_>>();
291        let expected = (20..=30)
292            .map(|val| MemoryCell(Some(val)))
293            .collect::<Vec<_>>();
294        assert_eq!(actual.len(), expected.len());
295        assert_eq!(actual, expected);
296    }
297}