1use std::sync::{
2 Arc,
3 atomic::{AtomicBool, Ordering},
4};
5
6use crate::{
7 DebugPanel,
8 session::running::variable_list::{CollapseSelectedEntry, ExpandSelectedEntry},
9 tests::{active_debug_session_panel, init_test, init_test_workspace},
10};
11use collections::HashMap;
12use dap::{
13 Scope, StackFrame, Variable,
14 requests::{Initialize, Launch, Scopes, StackTrace, Variables},
15};
16use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
17use menu::{SelectFirst, SelectNext, SelectPrevious};
18use project::{FakeFs, Project};
19use serde_json::json;
20use task::LaunchConfig;
21use unindent::Unindent as _;
22use util::path;
23
24/// This only tests fetching one scope and 2 variables for a single stackframe
25#[gpui::test]
26async fn test_basic_fetch_initial_scope_and_variables(
27 executor: BackgroundExecutor,
28 cx: &mut TestAppContext,
29) {
30 init_test(cx);
31
32 let fs = FakeFs::new(executor.clone());
33
34 let test_file_content = r#"
35 const variable1 = "Value 1";
36 const variable2 = "Value 2";
37 "#
38 .unindent();
39
40 fs.insert_tree(
41 path!("/project"),
42 json!({
43 "src": {
44 "test.js": test_file_content,
45 }
46 }),
47 )
48 .await;
49
50 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
51 let workspace = init_test_workspace(&project, cx).await;
52 workspace
53 .update(cx, |workspace, window, cx| {
54 workspace.focus_panel::<DebugPanel>(window, cx);
55 })
56 .unwrap();
57 let cx = &mut VisualTestContext::from_window(*workspace, cx);
58
59 let task = project.update(cx, |project, cx| {
60 project.fake_debug_session(
61 dap::DebugRequestType::Launch(LaunchConfig::default()),
62 None,
63 false,
64 cx,
65 )
66 });
67
68 let session = task.await.unwrap();
69 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
70
71 client
72 .on_request::<dap::requests::Threads, _>(move |_, _| {
73 Ok(dap::ThreadsResponse {
74 threads: vec![dap::Thread {
75 id: 1,
76 name: "Thread 1".into(),
77 }],
78 })
79 })
80 .await;
81
82 let stack_frames = vec![StackFrame {
83 id: 1,
84 name: "Stack Frame 1".into(),
85 source: Some(dap::Source {
86 name: Some("test.js".into()),
87 path: Some(path!("/project/src/test.js").into()),
88 source_reference: None,
89 presentation_hint: None,
90 origin: None,
91 sources: None,
92 adapter_data: None,
93 checksums: None,
94 }),
95 line: 1,
96 column: 1,
97 end_line: None,
98 end_column: None,
99 can_restart: None,
100 instruction_pointer_reference: None,
101 module_id: None,
102 presentation_hint: None,
103 }];
104
105 client
106 .on_request::<StackTrace, _>({
107 let stack_frames = Arc::new(stack_frames.clone());
108 move |_, args| {
109 assert_eq!(1, args.thread_id);
110
111 Ok(dap::StackTraceResponse {
112 stack_frames: (*stack_frames).clone(),
113 total_frames: None,
114 })
115 }
116 })
117 .await;
118
119 let scopes = vec![Scope {
120 name: "Scope 1".into(),
121 presentation_hint: None,
122 variables_reference: 2,
123 named_variables: None,
124 indexed_variables: None,
125 expensive: false,
126 source: None,
127 line: None,
128 column: None,
129 end_line: None,
130 end_column: None,
131 }];
132
133 client
134 .on_request::<Scopes, _>({
135 let scopes = Arc::new(scopes.clone());
136 move |_, args| {
137 assert_eq!(1, args.frame_id);
138
139 Ok(dap::ScopesResponse {
140 scopes: (*scopes).clone(),
141 })
142 }
143 })
144 .await;
145
146 let variables = vec![
147 Variable {
148 name: "variable1".into(),
149 value: "value 1".into(),
150 type_: None,
151 presentation_hint: None,
152 evaluate_name: None,
153 variables_reference: 0,
154 named_variables: None,
155 indexed_variables: None,
156 memory_reference: None,
157 declaration_location_reference: None,
158 value_location_reference: None,
159 },
160 Variable {
161 name: "variable2".into(),
162 value: "value 2".into(),
163 type_: None,
164 presentation_hint: None,
165 evaluate_name: None,
166 variables_reference: 0,
167 named_variables: None,
168 indexed_variables: None,
169 memory_reference: None,
170 declaration_location_reference: None,
171 value_location_reference: None,
172 },
173 ];
174
175 client
176 .on_request::<Variables, _>({
177 let variables = Arc::new(variables.clone());
178 move |_, args| {
179 assert_eq!(2, args.variables_reference);
180
181 Ok(dap::VariablesResponse {
182 variables: (*variables).clone(),
183 })
184 }
185 })
186 .await;
187
188 client
189 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
190 reason: dap::StoppedEventReason::Pause,
191 description: None,
192 thread_id: Some(1),
193 preserve_focus_hint: None,
194 text: None,
195 all_threads_stopped: None,
196 hit_breakpoint_ids: None,
197 }))
198 .await;
199
200 cx.run_until_parked();
201
202 let running_state =
203 active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
204 cx.focus_self(window);
205 item.mode()
206 .as_running()
207 .expect("Session should be running by this point")
208 .clone()
209 });
210 running_state.update_in(cx, |this, window, cx| {
211 this.activate_variable_list(window, cx);
212 });
213 cx.run_until_parked();
214
215 running_state.update(cx, |running_state, cx| {
216 let (stack_frame_list, stack_frame_id) =
217 running_state.stack_frame_list().update(cx, |list, _| {
218 (list.flatten_entries(), list.current_stack_frame_id())
219 });
220
221 assert_eq!(stack_frames, stack_frame_list);
222 assert_eq!(Some(1), stack_frame_id);
223
224 running_state
225 .variable_list()
226 .update(cx, |variable_list, _| {
227 assert_eq!(scopes, variable_list.scopes());
228 assert_eq!(
229 vec![variables[0].clone(), variables[1].clone(),],
230 variable_list.variables()
231 );
232
233 variable_list.assert_visual_entries(vec![
234 "v Scope 1",
235 " > variable1",
236 " > variable2",
237 ]);
238 });
239 });
240
241 let shutdown_session = project.update(cx, |project, cx| {
242 project.dap_store().update(cx, |dap_store, cx| {
243 dap_store.shutdown_session(session.read(cx).session_id(), cx)
244 })
245 });
246
247 shutdown_session.await.unwrap();
248}
249
250/// This tests fetching multiple scopes and variables for them with a single stackframe
251#[gpui::test]
252async fn test_fetch_variables_for_multiple_scopes(
253 executor: BackgroundExecutor,
254 cx: &mut TestAppContext,
255) {
256 init_test(cx);
257
258 let fs = FakeFs::new(executor.clone());
259
260 let test_file_content = r#"
261 const variable1 = {
262 nested1: "Nested 1",
263 nested2: "Nested 2",
264 };
265 const variable2 = "Value 2";
266 const variable3 = "Value 3";
267 "#
268 .unindent();
269
270 fs.insert_tree(
271 path!("/project"),
272 json!({
273 "src": {
274 "test.js": test_file_content,
275 }
276 }),
277 )
278 .await;
279
280 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
281 let workspace = init_test_workspace(&project, cx).await;
282 workspace
283 .update(cx, |workspace, window, cx| {
284 workspace.focus_panel::<DebugPanel>(window, cx);
285 })
286 .unwrap();
287 let cx = &mut VisualTestContext::from_window(*workspace, cx);
288
289 let task = project.update(cx, |project, cx| {
290 project.fake_debug_session(
291 dap::DebugRequestType::Launch(LaunchConfig::default()),
292 None,
293 false,
294 cx,
295 )
296 });
297
298 let session = task.await.unwrap();
299 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
300
301 client
302 .on_request::<dap::requests::Threads, _>(move |_, _| {
303 Ok(dap::ThreadsResponse {
304 threads: vec![dap::Thread {
305 id: 1,
306 name: "Thread 1".into(),
307 }],
308 })
309 })
310 .await;
311
312 client
313 .on_request::<Initialize, _>(move |_, _| {
314 Ok(dap::Capabilities {
315 supports_step_back: Some(false),
316 ..Default::default()
317 })
318 })
319 .await;
320
321 client.on_request::<Launch, _>(move |_, _| Ok(())).await;
322
323 let stack_frames = vec![StackFrame {
324 id: 1,
325 name: "Stack Frame 1".into(),
326 source: Some(dap::Source {
327 name: Some("test.js".into()),
328 path: Some(path!("/project/src/test.js").into()),
329 source_reference: None,
330 presentation_hint: None,
331 origin: None,
332 sources: None,
333 adapter_data: None,
334 checksums: None,
335 }),
336 line: 1,
337 column: 1,
338 end_line: None,
339 end_column: None,
340 can_restart: None,
341 instruction_pointer_reference: None,
342 module_id: None,
343 presentation_hint: None,
344 }];
345
346 client
347 .on_request::<StackTrace, _>({
348 let stack_frames = Arc::new(stack_frames.clone());
349 move |_, args| {
350 assert_eq!(1, args.thread_id);
351
352 Ok(dap::StackTraceResponse {
353 stack_frames: (*stack_frames).clone(),
354 total_frames: None,
355 })
356 }
357 })
358 .await;
359
360 let scopes = vec![
361 Scope {
362 name: "Scope 1".into(),
363 presentation_hint: Some(dap::ScopePresentationHint::Locals),
364 variables_reference: 2,
365 named_variables: None,
366 indexed_variables: None,
367 expensive: false,
368 source: None,
369 line: None,
370 column: None,
371 end_line: None,
372 end_column: None,
373 },
374 Scope {
375 name: "Scope 2".into(),
376 presentation_hint: None,
377 variables_reference: 3,
378 named_variables: None,
379 indexed_variables: None,
380 expensive: false,
381 source: None,
382 line: None,
383 column: None,
384 end_line: None,
385 end_column: None,
386 },
387 ];
388
389 client
390 .on_request::<Scopes, _>({
391 let scopes = Arc::new(scopes.clone());
392 move |_, args| {
393 assert_eq!(1, args.frame_id);
394
395 Ok(dap::ScopesResponse {
396 scopes: (*scopes).clone(),
397 })
398 }
399 })
400 .await;
401
402 let mut variables = HashMap::default();
403 variables.insert(
404 2,
405 vec![
406 Variable {
407 name: "variable1".into(),
408 value: "{nested1: \"Nested 1\", nested2: \"Nested 2\"}".into(),
409 type_: None,
410 presentation_hint: None,
411 evaluate_name: None,
412 variables_reference: 0,
413 named_variables: None,
414 indexed_variables: None,
415 memory_reference: None,
416 declaration_location_reference: None,
417 value_location_reference: None,
418 },
419 Variable {
420 name: "variable2".into(),
421 value: "Value 2".into(),
422 type_: None,
423 presentation_hint: None,
424 evaluate_name: None,
425 variables_reference: 0,
426 named_variables: None,
427 indexed_variables: None,
428 memory_reference: None,
429 declaration_location_reference: None,
430 value_location_reference: None,
431 },
432 ],
433 );
434 variables.insert(
435 3,
436 vec![Variable {
437 name: "variable3".into(),
438 value: "Value 3".into(),
439 type_: None,
440 presentation_hint: None,
441 evaluate_name: None,
442 variables_reference: 0,
443 named_variables: None,
444 indexed_variables: None,
445 memory_reference: None,
446 declaration_location_reference: None,
447 value_location_reference: None,
448 }],
449 );
450
451 client
452 .on_request::<Variables, _>({
453 let variables = Arc::new(variables.clone());
454 move |_, args| {
455 Ok(dap::VariablesResponse {
456 variables: variables.get(&args.variables_reference).unwrap().clone(),
457 })
458 }
459 })
460 .await;
461
462 client
463 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
464 reason: dap::StoppedEventReason::Pause,
465 description: None,
466 thread_id: Some(1),
467 preserve_focus_hint: None,
468 text: None,
469 all_threads_stopped: None,
470 hit_breakpoint_ids: None,
471 }))
472 .await;
473
474 cx.run_until_parked();
475
476 let running_state =
477 active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
478 cx.focus_self(window);
479 item.mode()
480 .as_running()
481 .expect("Session should be running by this point")
482 .clone()
483 });
484 running_state.update_in(cx, |this, window, cx| {
485 this.activate_variable_list(window, cx);
486 });
487 cx.run_until_parked();
488
489 running_state.update(cx, |running_state, cx| {
490 let (stack_frame_list, stack_frame_id) =
491 running_state.stack_frame_list().update(cx, |list, _| {
492 (list.flatten_entries(), list.current_stack_frame_id())
493 });
494
495 assert_eq!(Some(1), stack_frame_id);
496 assert_eq!(stack_frames, stack_frame_list);
497
498 running_state
499 .variable_list()
500 .update(cx, |variable_list, _| {
501 assert_eq!(2, variable_list.scopes().len());
502 assert_eq!(scopes, variable_list.scopes());
503 let variables_by_scope = variable_list.variables_per_scope();
504
505 // scope 1
506 assert_eq!(
507 vec![
508 variables.get(&2).unwrap()[0].clone(),
509 variables.get(&2).unwrap()[1].clone(),
510 ],
511 variables_by_scope[0].1
512 );
513
514 // scope 2
515 let empty_vec: Vec<dap::Variable> = vec![];
516 assert_eq!(empty_vec, variables_by_scope[1].1);
517
518 variable_list.assert_visual_entries(vec![
519 "v Scope 1",
520 " > variable1",
521 " > variable2",
522 "> Scope 2",
523 ]);
524 });
525 });
526
527 let shutdown_session = project.update(cx, |project, cx| {
528 project.dap_store().update(cx, |dap_store, cx| {
529 dap_store.shutdown_session(session.read(cx).session_id(), cx)
530 })
531 });
532
533 shutdown_session.await.unwrap();
534}
535
536// tests that toggling a variable will fetch its children and shows it
537#[gpui::test]
538async fn test_keyboard_navigation(executor: BackgroundExecutor, cx: &mut TestAppContext) {
539 init_test(cx);
540
541 let fs = FakeFs::new(executor.clone());
542
543 let test_file_content = r#"
544 const variable1 = {
545 nested1: "Nested 1",
546 nested2: "Nested 2",
547 };
548 const variable2 = "Value 2";
549 const variable3 = "Value 3";
550 "#
551 .unindent();
552
553 fs.insert_tree(
554 path!("/project"),
555 json!({
556 "src": {
557 "test.js": test_file_content,
558 }
559 }),
560 )
561 .await;
562
563 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
564 let workspace = init_test_workspace(&project, cx).await;
565 workspace
566 .update(cx, |workspace, window, cx| {
567 workspace.focus_panel::<DebugPanel>(window, cx);
568 })
569 .unwrap();
570 let cx = &mut VisualTestContext::from_window(*workspace, cx);
571
572 let task = project.update(cx, |project, cx| {
573 project.fake_debug_session(
574 dap::DebugRequestType::Launch(LaunchConfig::default()),
575 None,
576 false,
577 cx,
578 )
579 });
580
581 let session = task.await.unwrap();
582 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
583
584 client
585 .on_request::<dap::requests::Threads, _>(move |_, _| {
586 Ok(dap::ThreadsResponse {
587 threads: vec![dap::Thread {
588 id: 1,
589 name: "Thread 1".into(),
590 }],
591 })
592 })
593 .await;
594
595 client
596 .on_request::<Initialize, _>(move |_, _| {
597 Ok(dap::Capabilities {
598 supports_step_back: Some(false),
599 ..Default::default()
600 })
601 })
602 .await;
603
604 client.on_request::<Launch, _>(move |_, _| Ok(())).await;
605
606 let stack_frames = vec![StackFrame {
607 id: 1,
608 name: "Stack Frame 1".into(),
609 source: Some(dap::Source {
610 name: Some("test.js".into()),
611 path: Some(path!("/project/src/test.js").into()),
612 source_reference: None,
613 presentation_hint: None,
614 origin: None,
615 sources: None,
616 adapter_data: None,
617 checksums: None,
618 }),
619 line: 1,
620 column: 1,
621 end_line: None,
622 end_column: None,
623 can_restart: None,
624 instruction_pointer_reference: None,
625 module_id: None,
626 presentation_hint: None,
627 }];
628
629 client
630 .on_request::<StackTrace, _>({
631 let stack_frames = Arc::new(stack_frames.clone());
632 move |_, args| {
633 assert_eq!(1, args.thread_id);
634
635 Ok(dap::StackTraceResponse {
636 stack_frames: (*stack_frames).clone(),
637 total_frames: None,
638 })
639 }
640 })
641 .await;
642
643 let scopes = vec![
644 Scope {
645 name: "Scope 1".into(),
646 presentation_hint: Some(dap::ScopePresentationHint::Locals),
647 variables_reference: 2,
648 named_variables: None,
649 indexed_variables: None,
650 expensive: false,
651 source: None,
652 line: None,
653 column: None,
654 end_line: None,
655 end_column: None,
656 },
657 Scope {
658 name: "Scope 2".into(),
659 presentation_hint: None,
660 variables_reference: 4,
661 named_variables: None,
662 indexed_variables: None,
663 expensive: false,
664 source: None,
665 line: None,
666 column: None,
667 end_line: None,
668 end_column: None,
669 },
670 ];
671
672 client
673 .on_request::<Scopes, _>({
674 let scopes = Arc::new(scopes.clone());
675 move |_, args| {
676 assert_eq!(1, args.frame_id);
677
678 Ok(dap::ScopesResponse {
679 scopes: (*scopes).clone(),
680 })
681 }
682 })
683 .await;
684
685 let scope1_variables = vec![
686 Variable {
687 name: "variable1".into(),
688 value: "{nested1: \"Nested 1\", nested2: \"Nested 2\"}".into(),
689 type_: None,
690 presentation_hint: None,
691 evaluate_name: None,
692 variables_reference: 3,
693 named_variables: None,
694 indexed_variables: None,
695 memory_reference: None,
696 declaration_location_reference: None,
697 value_location_reference: None,
698 },
699 Variable {
700 name: "variable2".into(),
701 value: "Value 2".into(),
702 type_: None,
703 presentation_hint: None,
704 evaluate_name: None,
705 variables_reference: 0,
706 named_variables: None,
707 indexed_variables: None,
708 memory_reference: None,
709 declaration_location_reference: None,
710 value_location_reference: None,
711 },
712 ];
713
714 let nested_variables = vec![
715 Variable {
716 name: "nested1".into(),
717 value: "Nested 1".into(),
718 type_: None,
719 presentation_hint: None,
720 evaluate_name: None,
721 variables_reference: 0,
722 named_variables: None,
723 indexed_variables: None,
724 memory_reference: None,
725 declaration_location_reference: None,
726 value_location_reference: None,
727 },
728 Variable {
729 name: "nested2".into(),
730 value: "Nested 2".into(),
731 type_: None,
732 presentation_hint: None,
733 evaluate_name: None,
734 variables_reference: 0,
735 named_variables: None,
736 indexed_variables: None,
737 memory_reference: None,
738 declaration_location_reference: None,
739 value_location_reference: None,
740 },
741 ];
742
743 let scope2_variables = vec![Variable {
744 name: "variable3".into(),
745 value: "Value 3".into(),
746 type_: None,
747 presentation_hint: None,
748 evaluate_name: None,
749 variables_reference: 0,
750 named_variables: None,
751 indexed_variables: None,
752 memory_reference: None,
753 declaration_location_reference: None,
754 value_location_reference: None,
755 }];
756
757 client
758 .on_request::<Variables, _>({
759 let scope1_variables = Arc::new(scope1_variables.clone());
760 let nested_variables = Arc::new(nested_variables.clone());
761 let scope2_variables = Arc::new(scope2_variables.clone());
762 move |_, args| match args.variables_reference {
763 4 => Ok(dap::VariablesResponse {
764 variables: (*scope2_variables).clone(),
765 }),
766 3 => Ok(dap::VariablesResponse {
767 variables: (*nested_variables).clone(),
768 }),
769 2 => Ok(dap::VariablesResponse {
770 variables: (*scope1_variables).clone(),
771 }),
772 id => unreachable!("unexpected variables reference {id}"),
773 }
774 })
775 .await;
776
777 client
778 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
779 reason: dap::StoppedEventReason::Pause,
780 description: None,
781 thread_id: Some(1),
782 preserve_focus_hint: None,
783 text: None,
784 all_threads_stopped: None,
785 hit_breakpoint_ids: None,
786 }))
787 .await;
788
789 cx.run_until_parked();
790 let running_state =
791 active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
792 cx.focus_self(window);
793 let running = item
794 .mode()
795 .as_running()
796 .expect("Session should be running by this point")
797 .clone();
798
799 let variable_list = running.read_with(cx, |state, _| state.variable_list().clone());
800 variable_list.update(cx, |_, cx| cx.focus_self(window));
801 running
802 });
803 running_state.update_in(cx, |this, window, cx| {
804 this.activate_variable_list(window, cx);
805 });
806 cx.run_until_parked();
807 cx.dispatch_action(SelectFirst);
808 cx.dispatch_action(SelectFirst);
809 cx.run_until_parked();
810
811 running_state.update(cx, |running_state, cx| {
812 running_state
813 .variable_list()
814 .update(cx, |variable_list, _| {
815 variable_list.assert_visual_entries(vec![
816 "v Scope 1 <=== selected",
817 " > variable1",
818 " > variable2",
819 "> Scope 2",
820 ]);
821 });
822 });
823
824 cx.dispatch_action(SelectNext);
825 cx.run_until_parked();
826
827 running_state.update(cx, |running_state, cx| {
828 running_state
829 .variable_list()
830 .update(cx, |variable_list, _| {
831 variable_list.assert_visual_entries(vec![
832 "v Scope 1",
833 " > variable1 <=== selected",
834 " > variable2",
835 "> Scope 2",
836 ]);
837 });
838 });
839
840 // expand the nested variables of variable 1
841 cx.dispatch_action(ExpandSelectedEntry);
842 cx.run_until_parked();
843 running_state.update(cx, |running_state, cx| {
844 running_state
845 .variable_list()
846 .update(cx, |variable_list, _| {
847 variable_list.assert_visual_entries(vec![
848 "v Scope 1",
849 " v variable1 <=== selected",
850 " > nested1",
851 " > nested2",
852 " > variable2",
853 "> Scope 2",
854 ]);
855 });
856 });
857
858 // select the first nested variable of variable 1
859 cx.dispatch_action(SelectNext);
860 cx.run_until_parked();
861 running_state.update(cx, |debug_panel_item, cx| {
862 debug_panel_item
863 .variable_list()
864 .update(cx, |variable_list, _| {
865 variable_list.assert_visual_entries(vec![
866 "v Scope 1",
867 " v variable1",
868 " > nested1 <=== selected",
869 " > nested2",
870 " > variable2",
871 "> Scope 2",
872 ]);
873 });
874 });
875
876 // select the second nested variable of variable 1
877 cx.dispatch_action(SelectNext);
878 cx.run_until_parked();
879 running_state.update(cx, |debug_panel_item, cx| {
880 debug_panel_item
881 .variable_list()
882 .update(cx, |variable_list, _| {
883 variable_list.assert_visual_entries(vec![
884 "v Scope 1",
885 " v variable1",
886 " > nested1",
887 " > nested2 <=== selected",
888 " > variable2",
889 "> Scope 2",
890 ]);
891 });
892 });
893
894 // select variable 2 of scope 1
895 cx.dispatch_action(SelectNext);
896 cx.run_until_parked();
897 running_state.update(cx, |debug_panel_item, cx| {
898 debug_panel_item
899 .variable_list()
900 .update(cx, |variable_list, _| {
901 variable_list.assert_visual_entries(vec![
902 "v Scope 1",
903 " v variable1",
904 " > nested1",
905 " > nested2",
906 " > variable2 <=== selected",
907 "> Scope 2",
908 ]);
909 });
910 });
911
912 // select scope 2
913 cx.dispatch_action(SelectNext);
914 cx.run_until_parked();
915 running_state.update(cx, |debug_panel_item, cx| {
916 debug_panel_item
917 .variable_list()
918 .update(cx, |variable_list, _| {
919 variable_list.assert_visual_entries(vec![
920 "v Scope 1",
921 " v variable1",
922 " > nested1",
923 " > nested2",
924 " > variable2",
925 "> Scope 2 <=== selected",
926 ]);
927 });
928 });
929
930 // expand the nested variables of scope 2
931 cx.dispatch_action(ExpandSelectedEntry);
932 cx.run_until_parked();
933 running_state.update(cx, |debug_panel_item, cx| {
934 debug_panel_item
935 .variable_list()
936 .update(cx, |variable_list, _| {
937 variable_list.assert_visual_entries(vec![
938 "v Scope 1",
939 " v variable1",
940 " > nested1",
941 " > nested2",
942 " > variable2",
943 "v Scope 2 <=== selected",
944 " > variable3",
945 ]);
946 });
947 });
948
949 // select variable 3 of scope 2
950 cx.dispatch_action(SelectNext);
951 cx.run_until_parked();
952 running_state.update(cx, |debug_panel_item, cx| {
953 debug_panel_item
954 .variable_list()
955 .update(cx, |variable_list, _| {
956 variable_list.assert_visual_entries(vec![
957 "v Scope 1",
958 " v variable1",
959 " > nested1",
960 " > nested2",
961 " > variable2",
962 "v Scope 2",
963 " > variable3 <=== selected",
964 ]);
965 });
966 });
967
968 // select scope 2
969 cx.dispatch_action(SelectPrevious);
970 cx.run_until_parked();
971 running_state.update(cx, |debug_panel_item, cx| {
972 debug_panel_item
973 .variable_list()
974 .update(cx, |variable_list, _| {
975 variable_list.assert_visual_entries(vec![
976 "v Scope 1",
977 " v variable1",
978 " > nested1",
979 " > nested2",
980 " > variable2",
981 "v Scope 2 <=== selected",
982 " > variable3",
983 ]);
984 });
985 });
986
987 // collapse variables of scope 2
988 cx.dispatch_action(CollapseSelectedEntry);
989 cx.run_until_parked();
990 running_state.update(cx, |debug_panel_item, cx| {
991 debug_panel_item
992 .variable_list()
993 .update(cx, |variable_list, _| {
994 variable_list.assert_visual_entries(vec![
995 "v Scope 1",
996 " v variable1",
997 " > nested1",
998 " > nested2",
999 " > variable2",
1000 "> Scope 2 <=== selected",
1001 ]);
1002 });
1003 });
1004
1005 // select variable 2 of scope 1
1006 cx.dispatch_action(SelectPrevious);
1007 cx.run_until_parked();
1008 running_state.update(cx, |debug_panel_item, cx| {
1009 debug_panel_item
1010 .variable_list()
1011 .update(cx, |variable_list, _| {
1012 variable_list.assert_visual_entries(vec![
1013 "v Scope 1",
1014 " v variable1",
1015 " > nested1",
1016 " > nested2",
1017 " > variable2 <=== selected",
1018 "> Scope 2",
1019 ]);
1020 });
1021 });
1022
1023 // select nested2 of variable 1
1024 cx.dispatch_action(SelectPrevious);
1025 cx.run_until_parked();
1026 running_state.update(cx, |debug_panel_item, cx| {
1027 debug_panel_item
1028 .variable_list()
1029 .update(cx, |variable_list, _| {
1030 variable_list.assert_visual_entries(vec![
1031 "v Scope 1",
1032 " v variable1",
1033 " > nested1",
1034 " > nested2 <=== selected",
1035 " > variable2",
1036 "> Scope 2",
1037 ]);
1038 });
1039 });
1040
1041 // select nested1 of variable 1
1042 cx.dispatch_action(SelectPrevious);
1043 cx.run_until_parked();
1044 running_state.update(cx, |debug_panel_item, cx| {
1045 debug_panel_item
1046 .variable_list()
1047 .update(cx, |variable_list, _| {
1048 variable_list.assert_visual_entries(vec![
1049 "v Scope 1",
1050 " v variable1",
1051 " > nested1 <=== selected",
1052 " > nested2",
1053 " > variable2",
1054 "> Scope 2",
1055 ]);
1056 });
1057 });
1058
1059 // select variable 1 of scope 1
1060 cx.dispatch_action(SelectPrevious);
1061 cx.run_until_parked();
1062 running_state.update(cx, |debug_panel_item, cx| {
1063 debug_panel_item
1064 .variable_list()
1065 .update(cx, |variable_list, _| {
1066 variable_list.assert_visual_entries(vec![
1067 "v Scope 1",
1068 " v variable1 <=== selected",
1069 " > nested1",
1070 " > nested2",
1071 " > variable2",
1072 "> Scope 2",
1073 ]);
1074 });
1075 });
1076
1077 // collapse variables of variable 1
1078 cx.dispatch_action(CollapseSelectedEntry);
1079 cx.run_until_parked();
1080 running_state.update(cx, |debug_panel_item, cx| {
1081 debug_panel_item
1082 .variable_list()
1083 .update(cx, |variable_list, _| {
1084 variable_list.assert_visual_entries(vec![
1085 "v Scope 1",
1086 " > variable1 <=== selected",
1087 " > variable2",
1088 "> Scope 2",
1089 ]);
1090 });
1091 });
1092
1093 // select scope 1
1094 cx.dispatch_action(SelectPrevious);
1095 cx.run_until_parked();
1096 running_state.update(cx, |running_state, cx| {
1097 running_state
1098 .variable_list()
1099 .update(cx, |variable_list, _| {
1100 variable_list.assert_visual_entries(vec![
1101 "v Scope 1 <=== selected",
1102 " > variable1",
1103 " > variable2",
1104 "> Scope 2",
1105 ]);
1106 });
1107 });
1108
1109 // collapse variables of scope 1
1110 cx.dispatch_action(CollapseSelectedEntry);
1111 cx.run_until_parked();
1112 running_state.update(cx, |debug_panel_item, cx| {
1113 debug_panel_item
1114 .variable_list()
1115 .update(cx, |variable_list, _| {
1116 variable_list.assert_visual_entries(vec!["> Scope 1 <=== selected", "> Scope 2"]);
1117 });
1118 });
1119
1120 // select scope 2 backwards
1121 cx.dispatch_action(SelectPrevious);
1122 cx.run_until_parked();
1123 running_state.update(cx, |debug_panel_item, cx| {
1124 debug_panel_item
1125 .variable_list()
1126 .update(cx, |variable_list, _| {
1127 variable_list.assert_visual_entries(vec!["> Scope 1", "> Scope 2 <=== selected"]);
1128 });
1129 });
1130
1131 // select scope 1 backwards
1132 cx.dispatch_action(SelectNext);
1133 cx.run_until_parked();
1134 running_state.update(cx, |debug_panel_item, cx| {
1135 debug_panel_item
1136 .variable_list()
1137 .update(cx, |variable_list, _| {
1138 variable_list.assert_visual_entries(vec!["> Scope 1 <=== selected", "> Scope 2"]);
1139 });
1140 });
1141
1142 // test stepping through nested with ExpandSelectedEntry/CollapseSelectedEntry actions
1143
1144 cx.dispatch_action(ExpandSelectedEntry);
1145 cx.run_until_parked();
1146 running_state.update(cx, |debug_panel_item, cx| {
1147 debug_panel_item
1148 .variable_list()
1149 .update(cx, |variable_list, _| {
1150 variable_list.assert_visual_entries(vec![
1151 "v Scope 1 <=== selected",
1152 " > variable1",
1153 " > variable2",
1154 "> Scope 2",
1155 ]);
1156 });
1157 });
1158
1159 cx.dispatch_action(ExpandSelectedEntry);
1160 cx.run_until_parked();
1161 running_state.update(cx, |debug_panel_item, cx| {
1162 debug_panel_item
1163 .variable_list()
1164 .update(cx, |variable_list, _| {
1165 variable_list.assert_visual_entries(vec![
1166 "v Scope 1",
1167 " > variable1 <=== selected",
1168 " > variable2",
1169 "> Scope 2",
1170 ]);
1171 });
1172 });
1173
1174 cx.dispatch_action(ExpandSelectedEntry);
1175 cx.run_until_parked();
1176 running_state.update(cx, |debug_panel_item, cx| {
1177 debug_panel_item
1178 .variable_list()
1179 .update(cx, |variable_list, _| {
1180 variable_list.assert_visual_entries(vec![
1181 "v Scope 1",
1182 " v variable1 <=== selected",
1183 " > nested1",
1184 " > nested2",
1185 " > variable2",
1186 "> Scope 2",
1187 ]);
1188 });
1189 });
1190
1191 cx.dispatch_action(ExpandSelectedEntry);
1192 cx.run_until_parked();
1193 running_state.update(cx, |debug_panel_item, cx| {
1194 debug_panel_item
1195 .variable_list()
1196 .update(cx, |variable_list, _| {
1197 variable_list.assert_visual_entries(vec![
1198 "v Scope 1",
1199 " v variable1",
1200 " > nested1 <=== selected",
1201 " > nested2",
1202 " > variable2",
1203 "> Scope 2",
1204 ]);
1205 });
1206 });
1207
1208 cx.dispatch_action(ExpandSelectedEntry);
1209 cx.run_until_parked();
1210 running_state.update(cx, |debug_panel_item, cx| {
1211 debug_panel_item
1212 .variable_list()
1213 .update(cx, |variable_list, _| {
1214 variable_list.assert_visual_entries(vec![
1215 "v Scope 1",
1216 " v variable1",
1217 " > nested1",
1218 " > nested2 <=== selected",
1219 " > variable2",
1220 "> Scope 2",
1221 ]);
1222 });
1223 });
1224
1225 cx.dispatch_action(ExpandSelectedEntry);
1226 cx.run_until_parked();
1227 running_state.update(cx, |debug_panel_item, cx| {
1228 debug_panel_item
1229 .variable_list()
1230 .update(cx, |variable_list, _| {
1231 variable_list.assert_visual_entries(vec![
1232 "v Scope 1",
1233 " v variable1",
1234 " > nested1",
1235 " > nested2",
1236 " > variable2 <=== selected",
1237 "> Scope 2",
1238 ]);
1239 });
1240 });
1241
1242 cx.dispatch_action(CollapseSelectedEntry);
1243 cx.run_until_parked();
1244 running_state.update(cx, |debug_panel_item, cx| {
1245 debug_panel_item
1246 .variable_list()
1247 .update(cx, |variable_list, _| {
1248 variable_list.assert_visual_entries(vec![
1249 "v Scope 1",
1250 " v variable1",
1251 " > nested1",
1252 " > nested2 <=== selected",
1253 " > variable2",
1254 "> Scope 2",
1255 ]);
1256 });
1257 });
1258
1259 cx.dispatch_action(CollapseSelectedEntry);
1260 cx.run_until_parked();
1261 running_state.update(cx, |debug_panel_item, cx| {
1262 debug_panel_item
1263 .variable_list()
1264 .update(cx, |variable_list, _| {
1265 variable_list.assert_visual_entries(vec![
1266 "v Scope 1",
1267 " v variable1",
1268 " > nested1 <=== selected",
1269 " > nested2",
1270 " > variable2",
1271 "> Scope 2",
1272 ]);
1273 });
1274 });
1275
1276 cx.dispatch_action(CollapseSelectedEntry);
1277 cx.run_until_parked();
1278 running_state.update(cx, |debug_panel_item, cx| {
1279 debug_panel_item
1280 .variable_list()
1281 .update(cx, |variable_list, _| {
1282 variable_list.assert_visual_entries(vec![
1283 "v Scope 1",
1284 " v variable1 <=== selected",
1285 " > nested1",
1286 " > nested2",
1287 " > variable2",
1288 "> Scope 2",
1289 ]);
1290 });
1291 });
1292
1293 cx.dispatch_action(CollapseSelectedEntry);
1294 cx.run_until_parked();
1295 running_state.update(cx, |debug_panel_item, cx| {
1296 debug_panel_item
1297 .variable_list()
1298 .update(cx, |variable_list, _| {
1299 variable_list.assert_visual_entries(vec![
1300 "v Scope 1",
1301 " > variable1 <=== selected",
1302 " > variable2",
1303 "> Scope 2",
1304 ]);
1305 });
1306 });
1307
1308 cx.dispatch_action(CollapseSelectedEntry);
1309 cx.run_until_parked();
1310 running_state.update(cx, |debug_panel_item, cx| {
1311 debug_panel_item
1312 .variable_list()
1313 .update(cx, |variable_list, _| {
1314 variable_list.assert_visual_entries(vec![
1315 "v Scope 1 <=== selected",
1316 " > variable1",
1317 " > variable2",
1318 "> Scope 2",
1319 ]);
1320 });
1321 });
1322
1323 cx.dispatch_action(CollapseSelectedEntry);
1324 cx.run_until_parked();
1325 running_state.update(cx, |debug_panel_item, cx| {
1326 debug_panel_item
1327 .variable_list()
1328 .update(cx, |variable_list, _| {
1329 variable_list.assert_visual_entries(vec!["> Scope 1 <=== selected", "> Scope 2"]);
1330 });
1331 });
1332
1333 let shutdown_session = project.update(cx, |project, cx| {
1334 project.dap_store().update(cx, |dap_store, cx| {
1335 dap_store.shutdown_session(session.read(cx).session_id(), cx)
1336 })
1337 });
1338
1339 shutdown_session.await.unwrap();
1340}
1341
1342#[gpui::test]
1343async fn test_variable_list_only_sends_requests_when_rendering(
1344 executor: BackgroundExecutor,
1345 cx: &mut TestAppContext,
1346) {
1347 init_test(cx);
1348
1349 let fs = FakeFs::new(executor.clone());
1350
1351 let test_file_content = r#"
1352 import { SOME_VALUE } './module.js';
1353
1354 console.log(SOME_VALUE);
1355 "#
1356 .unindent();
1357
1358 let module_file_content = r#"
1359 export SOME_VALUE = 'some value';
1360 "#
1361 .unindent();
1362
1363 fs.insert_tree(
1364 path!("/project"),
1365 json!({
1366 "src": {
1367 "test.js": test_file_content,
1368 "module.js": module_file_content,
1369 }
1370 }),
1371 )
1372 .await;
1373
1374 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
1375 let workspace = init_test_workspace(&project, cx).await;
1376 let cx = &mut VisualTestContext::from_window(*workspace, cx);
1377
1378 let task = project.update(cx, |project, cx| {
1379 project.fake_debug_session(
1380 dap::DebugRequestType::Launch(LaunchConfig::default()),
1381 None,
1382 false,
1383 cx,
1384 )
1385 });
1386
1387 let session = task.await.unwrap();
1388 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
1389
1390 client
1391 .on_request::<dap::requests::Threads, _>(move |_, _| {
1392 Ok(dap::ThreadsResponse {
1393 threads: vec![dap::Thread {
1394 id: 1,
1395 name: "Thread 1".into(),
1396 }],
1397 })
1398 })
1399 .await;
1400
1401 client
1402 .on_request::<Initialize, _>(move |_, _| {
1403 Ok(dap::Capabilities {
1404 supports_step_back: Some(false),
1405 ..Default::default()
1406 })
1407 })
1408 .await;
1409
1410 client.on_request::<Launch, _>(move |_, _| Ok(())).await;
1411
1412 let stack_frames = vec![
1413 StackFrame {
1414 id: 1,
1415 name: "Stack Frame 1".into(),
1416 source: Some(dap::Source {
1417 name: Some("test.js".into()),
1418 path: Some(path!("/project/src/test.js").into()),
1419 source_reference: None,
1420 presentation_hint: None,
1421 origin: None,
1422 sources: None,
1423 adapter_data: None,
1424 checksums: None,
1425 }),
1426 line: 3,
1427 column: 1,
1428 end_line: None,
1429 end_column: None,
1430 can_restart: None,
1431 instruction_pointer_reference: None,
1432 module_id: None,
1433 presentation_hint: None,
1434 },
1435 StackFrame {
1436 id: 2,
1437 name: "Stack Frame 2".into(),
1438 source: Some(dap::Source {
1439 name: Some("module.js".into()),
1440 path: Some(path!("/project/src/module.js").into()),
1441 source_reference: None,
1442 presentation_hint: None,
1443 origin: None,
1444 sources: None,
1445 adapter_data: None,
1446 checksums: None,
1447 }),
1448 line: 1,
1449 column: 1,
1450 end_line: None,
1451 end_column: None,
1452 can_restart: None,
1453 instruction_pointer_reference: None,
1454 module_id: None,
1455 presentation_hint: None,
1456 },
1457 ];
1458
1459 client
1460 .on_request::<StackTrace, _>({
1461 let stack_frames = Arc::new(stack_frames.clone());
1462 move |_, args| {
1463 assert_eq!(1, args.thread_id);
1464
1465 Ok(dap::StackTraceResponse {
1466 stack_frames: (*stack_frames).clone(),
1467 total_frames: None,
1468 })
1469 }
1470 })
1471 .await;
1472
1473 let frame_1_scopes = vec![Scope {
1474 name: "Frame 1 Scope 1".into(),
1475 presentation_hint: None,
1476 variables_reference: 2,
1477 named_variables: None,
1478 indexed_variables: None,
1479 expensive: false,
1480 source: None,
1481 line: None,
1482 column: None,
1483 end_line: None,
1484 end_column: None,
1485 }];
1486
1487 let made_scopes_request = Arc::new(AtomicBool::new(false));
1488
1489 client
1490 .on_request::<Scopes, _>({
1491 let frame_1_scopes = Arc::new(frame_1_scopes.clone());
1492 let made_scopes_request = made_scopes_request.clone();
1493 move |_, args| {
1494 assert_eq!(1, args.frame_id);
1495 assert!(
1496 !made_scopes_request.load(Ordering::SeqCst),
1497 "We should be caching the scope request"
1498 );
1499
1500 made_scopes_request.store(true, Ordering::SeqCst);
1501
1502 Ok(dap::ScopesResponse {
1503 scopes: (*frame_1_scopes).clone(),
1504 })
1505 }
1506 })
1507 .await;
1508
1509 let frame_1_variables = vec![
1510 Variable {
1511 name: "variable1".into(),
1512 value: "value 1".into(),
1513 type_: None,
1514 presentation_hint: None,
1515 evaluate_name: None,
1516 variables_reference: 0,
1517 named_variables: None,
1518 indexed_variables: None,
1519 memory_reference: None,
1520 declaration_location_reference: None,
1521 value_location_reference: None,
1522 },
1523 Variable {
1524 name: "variable2".into(),
1525 value: "value 2".into(),
1526 type_: None,
1527 presentation_hint: None,
1528 evaluate_name: None,
1529 variables_reference: 0,
1530 named_variables: None,
1531 indexed_variables: None,
1532 memory_reference: None,
1533 declaration_location_reference: None,
1534 value_location_reference: None,
1535 },
1536 ];
1537
1538 client
1539 .on_request::<Variables, _>({
1540 let frame_1_variables = Arc::new(frame_1_variables.clone());
1541 move |_, args| {
1542 assert_eq!(2, args.variables_reference);
1543
1544 Ok(dap::VariablesResponse {
1545 variables: (*frame_1_variables).clone(),
1546 })
1547 }
1548 })
1549 .await;
1550
1551 let running_state = active_debug_session_panel(workspace, cx).update_in(cx, |item, _, _| {
1552 let state = item
1553 .mode()
1554 .as_running()
1555 .expect("Session should be running by this point")
1556 .clone();
1557
1558 state
1559 });
1560
1561 client
1562 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
1563 reason: dap::StoppedEventReason::Pause,
1564 description: None,
1565 thread_id: Some(1),
1566 preserve_focus_hint: None,
1567 text: None,
1568 all_threads_stopped: None,
1569 hit_breakpoint_ids: None,
1570 }))
1571 .await;
1572
1573 cx.run_until_parked();
1574
1575 // We shouldn't make any variable requests unless we're rendering the variable list
1576 running_state.update_in(cx, |running_state, window, cx| {
1577 let variable_list = running_state.variable_list().read(cx);
1578 let empty: Vec<dap::Variable> = vec![];
1579
1580 assert_eq!(empty, variable_list.variables());
1581 assert!(!made_scopes_request.load(Ordering::SeqCst));
1582
1583 cx.focus_self(window);
1584 });
1585 running_state.update_in(cx, |this, window, cx| {
1586 this.activate_variable_list(window, cx);
1587 });
1588 cx.run_until_parked();
1589
1590 running_state.update(cx, |running_state, cx| {
1591 let (stack_frame_list, stack_frame_id) =
1592 running_state.stack_frame_list().update(cx, |list, _| {
1593 (list.flatten_entries(), list.current_stack_frame_id())
1594 });
1595
1596 assert_eq!(Some(1), stack_frame_id);
1597 assert_eq!(stack_frames, stack_frame_list);
1598
1599 let variable_list = running_state.variable_list().read(cx);
1600
1601 assert_eq!(frame_1_variables, variable_list.variables());
1602 assert!(made_scopes_request.load(Ordering::SeqCst));
1603 });
1604
1605 let shutdown_session = project.update(cx, |project, cx| {
1606 project.dap_store().update(cx, |dap_store, cx| {
1607 dap_store.shutdown_session(session.read(cx).session_id(), cx)
1608 })
1609 });
1610
1611 shutdown_session.await.unwrap();
1612}
1613
1614#[gpui::test]
1615async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame(
1616 executor: BackgroundExecutor,
1617 cx: &mut TestAppContext,
1618) {
1619 init_test(cx);
1620
1621 let fs = FakeFs::new(executor.clone());
1622
1623 let test_file_content = r#"
1624 import { SOME_VALUE } './module.js';
1625
1626 console.log(SOME_VALUE);
1627 "#
1628 .unindent();
1629
1630 let module_file_content = r#"
1631 export SOME_VALUE = 'some value';
1632 "#
1633 .unindent();
1634
1635 fs.insert_tree(
1636 path!("/project"),
1637 json!({
1638 "src": {
1639 "test.js": test_file_content,
1640 "module.js": module_file_content,
1641 }
1642 }),
1643 )
1644 .await;
1645
1646 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
1647 let workspace = init_test_workspace(&project, cx).await;
1648 workspace
1649 .update(cx, |workspace, window, cx| {
1650 workspace.focus_panel::<DebugPanel>(window, cx);
1651 })
1652 .unwrap();
1653 let cx = &mut VisualTestContext::from_window(*workspace, cx);
1654
1655 let task = project.update(cx, |project, cx| {
1656 project.fake_debug_session(
1657 dap::DebugRequestType::Launch(LaunchConfig::default()),
1658 None,
1659 false,
1660 cx,
1661 )
1662 });
1663
1664 let session = task.await.unwrap();
1665 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
1666
1667 client
1668 .on_request::<dap::requests::Threads, _>(move |_, _| {
1669 Ok(dap::ThreadsResponse {
1670 threads: vec![dap::Thread {
1671 id: 1,
1672 name: "Thread 1".into(),
1673 }],
1674 })
1675 })
1676 .await;
1677
1678 client
1679 .on_request::<Initialize, _>(move |_, _| {
1680 Ok(dap::Capabilities {
1681 supports_step_back: Some(false),
1682 ..Default::default()
1683 })
1684 })
1685 .await;
1686
1687 client.on_request::<Launch, _>(move |_, _| Ok(())).await;
1688
1689 let stack_frames = vec![
1690 StackFrame {
1691 id: 1,
1692 name: "Stack Frame 1".into(),
1693 source: Some(dap::Source {
1694 name: Some("test.js".into()),
1695 path: Some(path!("/project/src/test.js").into()),
1696 source_reference: None,
1697 presentation_hint: None,
1698 origin: None,
1699 sources: None,
1700 adapter_data: None,
1701 checksums: None,
1702 }),
1703 line: 3,
1704 column: 1,
1705 end_line: None,
1706 end_column: None,
1707 can_restart: None,
1708 instruction_pointer_reference: None,
1709 module_id: None,
1710 presentation_hint: None,
1711 },
1712 StackFrame {
1713 id: 2,
1714 name: "Stack Frame 2".into(),
1715 source: Some(dap::Source {
1716 name: Some("module.js".into()),
1717 path: Some(path!("/project/src/module.js").into()),
1718 source_reference: None,
1719 presentation_hint: None,
1720 origin: None,
1721 sources: None,
1722 adapter_data: None,
1723 checksums: None,
1724 }),
1725 line: 1,
1726 column: 1,
1727 end_line: None,
1728 end_column: None,
1729 can_restart: None,
1730 instruction_pointer_reference: None,
1731 module_id: None,
1732 presentation_hint: None,
1733 },
1734 ];
1735
1736 client
1737 .on_request::<StackTrace, _>({
1738 let stack_frames = Arc::new(stack_frames.clone());
1739 move |_, args| {
1740 assert_eq!(1, args.thread_id);
1741
1742 Ok(dap::StackTraceResponse {
1743 stack_frames: (*stack_frames).clone(),
1744 total_frames: None,
1745 })
1746 }
1747 })
1748 .await;
1749
1750 let frame_1_scopes = vec![Scope {
1751 name: "Frame 1 Scope 1".into(),
1752 presentation_hint: None,
1753 variables_reference: 2,
1754 named_variables: None,
1755 indexed_variables: None,
1756 expensive: false,
1757 source: None,
1758 line: None,
1759 column: None,
1760 end_line: None,
1761 end_column: None,
1762 }];
1763
1764 // add handlers for fetching the second stack frame's scopes and variables
1765 // after the user clicked the stack frame
1766 let frame_2_scopes = vec![Scope {
1767 name: "Frame 2 Scope 1".into(),
1768 presentation_hint: None,
1769 variables_reference: 3,
1770 named_variables: None,
1771 indexed_variables: None,
1772 expensive: false,
1773 source: None,
1774 line: None,
1775 column: None,
1776 end_line: None,
1777 end_column: None,
1778 }];
1779
1780 let called_second_stack_frame = Arc::new(AtomicBool::new(false));
1781 let called_first_stack_frame = Arc::new(AtomicBool::new(false));
1782
1783 client
1784 .on_request::<Scopes, _>({
1785 let frame_1_scopes = Arc::new(frame_1_scopes.clone());
1786 let frame_2_scopes = Arc::new(frame_2_scopes.clone());
1787 let called_first_stack_frame = called_first_stack_frame.clone();
1788 let called_second_stack_frame = called_second_stack_frame.clone();
1789 move |_, args| match args.frame_id {
1790 1 => {
1791 called_first_stack_frame.store(true, Ordering::SeqCst);
1792 Ok(dap::ScopesResponse {
1793 scopes: (*frame_1_scopes).clone(),
1794 })
1795 }
1796 2 => {
1797 called_second_stack_frame.store(true, Ordering::SeqCst);
1798
1799 Ok(dap::ScopesResponse {
1800 scopes: (*frame_2_scopes).clone(),
1801 })
1802 }
1803 _ => panic!("Made a scopes request with an invalid frame id"),
1804 }
1805 })
1806 .await;
1807
1808 let frame_1_variables = vec![
1809 Variable {
1810 name: "variable1".into(),
1811 value: "value 1".into(),
1812 type_: None,
1813 presentation_hint: None,
1814 evaluate_name: None,
1815 variables_reference: 0,
1816 named_variables: None,
1817 indexed_variables: None,
1818 memory_reference: None,
1819 declaration_location_reference: None,
1820 value_location_reference: None,
1821 },
1822 Variable {
1823 name: "variable2".into(),
1824 value: "value 2".into(),
1825 type_: None,
1826 presentation_hint: None,
1827 evaluate_name: None,
1828 variables_reference: 0,
1829 named_variables: None,
1830 indexed_variables: None,
1831 memory_reference: None,
1832 declaration_location_reference: None,
1833 value_location_reference: None,
1834 },
1835 ];
1836
1837 let frame_2_variables = vec![
1838 Variable {
1839 name: "variable3".into(),
1840 value: "old value 1".into(),
1841 type_: None,
1842 presentation_hint: None,
1843 evaluate_name: None,
1844 variables_reference: 0,
1845 named_variables: None,
1846 indexed_variables: None,
1847 memory_reference: None,
1848 declaration_location_reference: None,
1849 value_location_reference: None,
1850 },
1851 Variable {
1852 name: "variable4".into(),
1853 value: "old value 2".into(),
1854 type_: None,
1855 presentation_hint: None,
1856 evaluate_name: None,
1857 variables_reference: 0,
1858 named_variables: None,
1859 indexed_variables: None,
1860 memory_reference: None,
1861 declaration_location_reference: None,
1862 value_location_reference: None,
1863 },
1864 ];
1865
1866 client
1867 .on_request::<Variables, _>({
1868 let frame_1_variables = Arc::new(frame_1_variables.clone());
1869 move |_, args| {
1870 assert_eq!(2, args.variables_reference);
1871
1872 Ok(dap::VariablesResponse {
1873 variables: (*frame_1_variables).clone(),
1874 })
1875 }
1876 })
1877 .await;
1878
1879 client
1880 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
1881 reason: dap::StoppedEventReason::Pause,
1882 description: None,
1883 thread_id: Some(1),
1884 preserve_focus_hint: None,
1885 text: None,
1886 all_threads_stopped: None,
1887 hit_breakpoint_ids: None,
1888 }))
1889 .await;
1890
1891 cx.run_until_parked();
1892
1893 let running_state =
1894 active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
1895 cx.focus_self(window);
1896 item.mode()
1897 .as_running()
1898 .expect("Session should be running by this point")
1899 .clone()
1900 });
1901 running_state.update_in(cx, |this, window, cx| {
1902 this.activate_variable_list(window, cx);
1903 });
1904 cx.run_until_parked();
1905
1906 running_state.update(cx, |running_state, cx| {
1907 let (stack_frame_list, stack_frame_id) =
1908 running_state.stack_frame_list().update(cx, |list, _| {
1909 (list.flatten_entries(), list.current_stack_frame_id())
1910 });
1911
1912 let variable_list = running_state.variable_list().read(cx);
1913 let variables = variable_list.variables();
1914
1915 assert_eq!(Some(1), stack_frame_id);
1916 assert_eq!(
1917 running_state
1918 .stack_frame_list()
1919 .read(cx)
1920 .current_stack_frame_id(),
1921 Some(1)
1922 );
1923
1924 assert!(
1925 called_first_stack_frame.load(std::sync::atomic::Ordering::SeqCst),
1926 "Request scopes shouldn't be called before it's needed"
1927 );
1928 assert!(
1929 !called_second_stack_frame.load(std::sync::atomic::Ordering::SeqCst),
1930 "Request scopes shouldn't be called before it's needed"
1931 );
1932
1933 assert_eq!(stack_frames, stack_frame_list);
1934 assert_eq!(frame_1_variables, variables);
1935 });
1936
1937 client
1938 .on_request::<Variables, _>({
1939 let frame_2_variables = Arc::new(frame_2_variables.clone());
1940 move |_, args| {
1941 assert_eq!(3, args.variables_reference);
1942
1943 Ok(dap::VariablesResponse {
1944 variables: (*frame_2_variables).clone(),
1945 })
1946 }
1947 })
1948 .await;
1949
1950 running_state
1951 .update_in(cx, |running_state, window, cx| {
1952 running_state
1953 .stack_frame_list()
1954 .update(cx, |stack_frame_list, cx| {
1955 stack_frame_list.select_stack_frame(&stack_frames[1], true, window, cx)
1956 })
1957 })
1958 .await
1959 .unwrap();
1960
1961 cx.run_until_parked();
1962
1963 running_state.update(cx, |running_state, cx| {
1964 let (stack_frame_list, stack_frame_id) =
1965 running_state.stack_frame_list().update(cx, |list, _| {
1966 (list.flatten_entries(), list.current_stack_frame_id())
1967 });
1968
1969 let variable_list = running_state.variable_list().read(cx);
1970 let variables = variable_list.variables();
1971
1972 assert_eq!(Some(2), stack_frame_id);
1973 assert!(
1974 called_second_stack_frame.load(std::sync::atomic::Ordering::SeqCst),
1975 "Request scopes shouldn't be called before it's needed"
1976 );
1977
1978 assert_eq!(stack_frames, stack_frame_list);
1979
1980 assert_eq!(variables, frame_2_variables,);
1981 });
1982
1983 let shutdown_session = project.update(cx, |project, cx| {
1984 project.dap_store().update(cx, |dap_store, cx| {
1985 dap_store.shutdown_session(session.read(cx).session_id(), cx)
1986 })
1987 });
1988
1989 shutdown_session.await.unwrap();
1990}