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