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