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