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