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