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