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