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