variable_list.rs

   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}