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