variable_list.rs

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