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