
- **Enable a bunch of extra lints** - **First batch of fixes** - **More fixes** Release Notes: - N/A
2342 lines
70 KiB
Rust
2342 lines
70 KiB
Rust
use std::sync::{
|
|
Arc,
|
|
atomic::{AtomicBool, Ordering},
|
|
};
|
|
|
|
use crate::{
|
|
DebugPanel,
|
|
persistence::DebuggerPaneItem,
|
|
session::running::variable_list::{
|
|
AddWatch, CollapseSelectedEntry, ExpandSelectedEntry, RemoveWatch,
|
|
},
|
|
tests::{active_debug_session_panel, init_test, init_test_workspace, start_debug_session},
|
|
};
|
|
use collections::HashMap;
|
|
use dap::{
|
|
Scope, StackFrame, Variable,
|
|
requests::{Evaluate, Initialize, Launch, Scopes, StackTrace, Variables},
|
|
};
|
|
use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
|
|
use menu::{SelectFirst, SelectNext, SelectPrevious};
|
|
use project::{FakeFs, Project};
|
|
use serde_json::json;
|
|
use ui::SharedString;
|
|
use unindent::Unindent as _;
|
|
use util::path;
|
|
|
|
/// This only tests fetching one scope and 2 variables for a single stackframe
|
|
#[gpui::test]
|
|
async fn test_basic_fetch_initial_scope_and_variables(
|
|
executor: BackgroundExecutor,
|
|
cx: &mut TestAppContext,
|
|
) {
|
|
init_test(cx);
|
|
|
|
let fs = FakeFs::new(executor.clone());
|
|
|
|
let test_file_content = r#"
|
|
const variable1 = "Value 1";
|
|
const variable2 = "Value 2";
|
|
"#
|
|
.unindent();
|
|
|
|
fs.insert_tree(
|
|
path!("/project"),
|
|
json!({
|
|
"src": {
|
|
"test.js": test_file_content,
|
|
}
|
|
}),
|
|
)
|
|
.await;
|
|
|
|
let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
|
|
let workspace = init_test_workspace(&project, cx).await;
|
|
workspace
|
|
.update(cx, |workspace, window, cx| {
|
|
workspace.focus_panel::<DebugPanel>(window, cx);
|
|
})
|
|
.unwrap();
|
|
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
|
let session = start_debug_session(&workspace, cx, |_| {}).unwrap();
|
|
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
|
|
|
|
client.on_request::<dap::requests::Threads, _>(move |_, _| {
|
|
Ok(dap::ThreadsResponse {
|
|
threads: vec![dap::Thread {
|
|
id: 1,
|
|
name: "Thread 1".into(),
|
|
}],
|
|
})
|
|
});
|
|
|
|
let stack_frames = vec![StackFrame {
|
|
id: 1,
|
|
name: "Stack Frame 1".into(),
|
|
source: Some(dap::Source {
|
|
name: Some("test.js".into()),
|
|
path: Some(path!("/project/src/test.js").into()),
|
|
source_reference: None,
|
|
presentation_hint: None,
|
|
origin: None,
|
|
sources: None,
|
|
adapter_data: None,
|
|
checksums: None,
|
|
}),
|
|
line: 1,
|
|
column: 1,
|
|
end_line: None,
|
|
end_column: None,
|
|
can_restart: None,
|
|
instruction_pointer_reference: None,
|
|
module_id: None,
|
|
presentation_hint: None,
|
|
}];
|
|
|
|
client.on_request::<StackTrace, _>({
|
|
let stack_frames = Arc::new(stack_frames.clone());
|
|
move |_, args| {
|
|
assert_eq!(1, args.thread_id);
|
|
|
|
Ok(dap::StackTraceResponse {
|
|
stack_frames: (*stack_frames).clone(),
|
|
total_frames: None,
|
|
})
|
|
}
|
|
});
|
|
|
|
let scopes = vec![Scope {
|
|
name: "Scope 1".into(),
|
|
presentation_hint: None,
|
|
variables_reference: 2,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
expensive: false,
|
|
source: None,
|
|
line: None,
|
|
column: None,
|
|
end_line: None,
|
|
end_column: None,
|
|
}];
|
|
|
|
client.on_request::<Scopes, _>({
|
|
let scopes = Arc::new(scopes.clone());
|
|
move |_, args| {
|
|
assert_eq!(1, args.frame_id);
|
|
|
|
Ok(dap::ScopesResponse {
|
|
scopes: (*scopes).clone(),
|
|
})
|
|
}
|
|
});
|
|
|
|
let variables = vec![
|
|
Variable {
|
|
name: "variable1".into(),
|
|
value: "value 1".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
Variable {
|
|
name: "variable2".into(),
|
|
value: "value 2".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
];
|
|
|
|
client.on_request::<Variables, _>({
|
|
let variables = Arc::new(variables.clone());
|
|
move |_, args| {
|
|
assert_eq!(2, args.variables_reference);
|
|
|
|
Ok(dap::VariablesResponse {
|
|
variables: (*variables).clone(),
|
|
})
|
|
}
|
|
});
|
|
|
|
client
|
|
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
|
|
reason: dap::StoppedEventReason::Pause,
|
|
description: None,
|
|
thread_id: Some(1),
|
|
preserve_focus_hint: None,
|
|
text: None,
|
|
all_threads_stopped: None,
|
|
hit_breakpoint_ids: None,
|
|
}))
|
|
.await;
|
|
|
|
cx.run_until_parked();
|
|
|
|
let running_state =
|
|
active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
|
|
cx.focus_self(window);
|
|
item.running_state().clone()
|
|
});
|
|
cx.run_until_parked();
|
|
|
|
running_state.update(cx, |running_state, cx| {
|
|
let (stack_frame_list, stack_frame_id) =
|
|
running_state.stack_frame_list().update(cx, |list, _| {
|
|
(
|
|
list.flatten_entries(true, true),
|
|
list.opened_stack_frame_id(),
|
|
)
|
|
});
|
|
|
|
assert_eq!(stack_frames, stack_frame_list);
|
|
assert_eq!(Some(1), stack_frame_id);
|
|
|
|
running_state
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
assert_eq!(scopes, variable_list.scopes());
|
|
assert_eq!(
|
|
vec![variables[0].clone(), variables[1].clone(),],
|
|
variable_list.variables()
|
|
);
|
|
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" > variable1",
|
|
" > variable2",
|
|
]);
|
|
});
|
|
});
|
|
}
|
|
|
|
/// This tests fetching multiple scopes and variables for them with a single stackframe
|
|
#[gpui::test]
|
|
async fn test_fetch_variables_for_multiple_scopes(
|
|
executor: BackgroundExecutor,
|
|
cx: &mut TestAppContext,
|
|
) {
|
|
init_test(cx);
|
|
|
|
let fs = FakeFs::new(executor.clone());
|
|
|
|
let test_file_content = r#"
|
|
const variable1 = {
|
|
nested1: "Nested 1",
|
|
nested2: "Nested 2",
|
|
};
|
|
const variable2 = "Value 2";
|
|
const variable3 = "Value 3";
|
|
"#
|
|
.unindent();
|
|
|
|
fs.insert_tree(
|
|
path!("/project"),
|
|
json!({
|
|
"src": {
|
|
"test.js": test_file_content,
|
|
}
|
|
}),
|
|
)
|
|
.await;
|
|
|
|
let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
|
|
let workspace = init_test_workspace(&project, cx).await;
|
|
workspace
|
|
.update(cx, |workspace, window, cx| {
|
|
workspace.focus_panel::<DebugPanel>(window, cx);
|
|
})
|
|
.unwrap();
|
|
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
|
|
|
let session = start_debug_session(&workspace, cx, |_| {}).unwrap();
|
|
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
|
|
|
|
client.on_request::<dap::requests::Threads, _>(move |_, _| {
|
|
Ok(dap::ThreadsResponse {
|
|
threads: vec![dap::Thread {
|
|
id: 1,
|
|
name: "Thread 1".into(),
|
|
}],
|
|
})
|
|
});
|
|
|
|
client.on_request::<Initialize, _>(move |_, _| {
|
|
Ok(dap::Capabilities {
|
|
supports_step_back: Some(false),
|
|
..Default::default()
|
|
})
|
|
});
|
|
|
|
client.on_request::<Launch, _>(move |_, _| Ok(()));
|
|
|
|
let stack_frames = vec![StackFrame {
|
|
id: 1,
|
|
name: "Stack Frame 1".into(),
|
|
source: Some(dap::Source {
|
|
name: Some("test.js".into()),
|
|
path: Some(path!("/project/src/test.js").into()),
|
|
source_reference: None,
|
|
presentation_hint: None,
|
|
origin: None,
|
|
sources: None,
|
|
adapter_data: None,
|
|
checksums: None,
|
|
}),
|
|
line: 1,
|
|
column: 1,
|
|
end_line: None,
|
|
end_column: None,
|
|
can_restart: None,
|
|
instruction_pointer_reference: None,
|
|
module_id: None,
|
|
presentation_hint: None,
|
|
}];
|
|
|
|
client.on_request::<StackTrace, _>({
|
|
let stack_frames = Arc::new(stack_frames.clone());
|
|
move |_, args| {
|
|
assert_eq!(1, args.thread_id);
|
|
|
|
Ok(dap::StackTraceResponse {
|
|
stack_frames: (*stack_frames).clone(),
|
|
total_frames: None,
|
|
})
|
|
}
|
|
});
|
|
|
|
let scopes = vec![
|
|
Scope {
|
|
name: "Scope 1".into(),
|
|
presentation_hint: Some(dap::ScopePresentationHint::Locals),
|
|
variables_reference: 2,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
expensive: false,
|
|
source: None,
|
|
line: None,
|
|
column: None,
|
|
end_line: None,
|
|
end_column: None,
|
|
},
|
|
Scope {
|
|
name: "Scope 2".into(),
|
|
presentation_hint: None,
|
|
variables_reference: 3,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
expensive: false,
|
|
source: None,
|
|
line: None,
|
|
column: None,
|
|
end_line: None,
|
|
end_column: None,
|
|
},
|
|
];
|
|
|
|
client.on_request::<Scopes, _>({
|
|
let scopes = Arc::new(scopes.clone());
|
|
move |_, args| {
|
|
assert_eq!(1, args.frame_id);
|
|
|
|
Ok(dap::ScopesResponse {
|
|
scopes: (*scopes).clone(),
|
|
})
|
|
}
|
|
});
|
|
|
|
let mut variables = HashMap::default();
|
|
variables.insert(
|
|
2,
|
|
vec![
|
|
Variable {
|
|
name: "variable1".into(),
|
|
value: "{nested1: \"Nested 1\", nested2: \"Nested 2\"}".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
Variable {
|
|
name: "variable2".into(),
|
|
value: "Value 2".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
],
|
|
);
|
|
variables.insert(
|
|
3,
|
|
vec![Variable {
|
|
name: "variable3".into(),
|
|
value: "Value 3".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
}],
|
|
);
|
|
|
|
client.on_request::<Variables, _>({
|
|
let variables = Arc::new(variables.clone());
|
|
move |_, args| {
|
|
Ok(dap::VariablesResponse {
|
|
variables: variables.get(&args.variables_reference).unwrap().clone(),
|
|
})
|
|
}
|
|
});
|
|
|
|
client
|
|
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
|
|
reason: dap::StoppedEventReason::Pause,
|
|
description: None,
|
|
thread_id: Some(1),
|
|
preserve_focus_hint: None,
|
|
text: None,
|
|
all_threads_stopped: None,
|
|
hit_breakpoint_ids: None,
|
|
}))
|
|
.await;
|
|
|
|
cx.run_until_parked();
|
|
|
|
let running_state =
|
|
active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
|
|
cx.focus_self(window);
|
|
item.running_state().clone()
|
|
});
|
|
cx.run_until_parked();
|
|
|
|
running_state.update(cx, |running_state, cx| {
|
|
let (stack_frame_list, stack_frame_id) =
|
|
running_state.stack_frame_list().update(cx, |list, _| {
|
|
(
|
|
list.flatten_entries(true, true),
|
|
list.opened_stack_frame_id(),
|
|
)
|
|
});
|
|
|
|
assert_eq!(Some(1), stack_frame_id);
|
|
assert_eq!(stack_frames, stack_frame_list);
|
|
|
|
running_state
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
assert_eq!(2, variable_list.scopes().len());
|
|
assert_eq!(scopes, variable_list.scopes());
|
|
let variables_by_scope = variable_list.variables_per_scope();
|
|
|
|
// scope 1
|
|
assert_eq!(
|
|
vec![
|
|
variables.get(&2).unwrap()[0].clone(),
|
|
variables.get(&2).unwrap()[1].clone(),
|
|
],
|
|
variables_by_scope[0].1
|
|
);
|
|
|
|
// scope 2
|
|
let empty_vec: Vec<dap::Variable> = vec![];
|
|
assert_eq!(empty_vec, variables_by_scope[1].1);
|
|
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" > variable1",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
}
|
|
|
|
// tests that toggling a variable will fetch its children and shows it
|
|
#[gpui::test]
|
|
async fn test_keyboard_navigation(executor: BackgroundExecutor, cx: &mut TestAppContext) {
|
|
init_test(cx);
|
|
|
|
let fs = FakeFs::new(executor.clone());
|
|
|
|
let test_file_content = r#"
|
|
const variable1 = {
|
|
nested1: "Nested 1",
|
|
nested2: "Nested 2",
|
|
};
|
|
const variable2 = "Value 2";
|
|
const variable3 = "Value 3";
|
|
"#
|
|
.unindent();
|
|
|
|
fs.insert_tree(
|
|
path!("/project"),
|
|
json!({
|
|
"src": {
|
|
"test.js": test_file_content,
|
|
}
|
|
}),
|
|
)
|
|
.await;
|
|
|
|
let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
|
|
let workspace = init_test_workspace(&project, cx).await;
|
|
workspace
|
|
.update(cx, |workspace, window, cx| {
|
|
workspace.focus_panel::<DebugPanel>(window, cx);
|
|
})
|
|
.unwrap();
|
|
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
|
let session = start_debug_session(&workspace, cx, |_| {}).unwrap();
|
|
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
|
|
|
|
client.on_request::<dap::requests::Threads, _>(move |_, _| {
|
|
Ok(dap::ThreadsResponse {
|
|
threads: vec![dap::Thread {
|
|
id: 1,
|
|
name: "Thread 1".into(),
|
|
}],
|
|
})
|
|
});
|
|
|
|
client.on_request::<Initialize, _>(move |_, _| {
|
|
Ok(dap::Capabilities {
|
|
supports_step_back: Some(false),
|
|
..Default::default()
|
|
})
|
|
});
|
|
|
|
client.on_request::<Launch, _>(move |_, _| Ok(()));
|
|
|
|
let stack_frames = vec![StackFrame {
|
|
id: 1,
|
|
name: "Stack Frame 1".into(),
|
|
source: Some(dap::Source {
|
|
name: Some("test.js".into()),
|
|
path: Some(path!("/project/src/test.js").into()),
|
|
source_reference: None,
|
|
presentation_hint: None,
|
|
origin: None,
|
|
sources: None,
|
|
adapter_data: None,
|
|
checksums: None,
|
|
}),
|
|
line: 1,
|
|
column: 1,
|
|
end_line: None,
|
|
end_column: None,
|
|
can_restart: None,
|
|
instruction_pointer_reference: None,
|
|
module_id: None,
|
|
presentation_hint: None,
|
|
}];
|
|
|
|
client.on_request::<StackTrace, _>({
|
|
let stack_frames = Arc::new(stack_frames.clone());
|
|
move |_, args| {
|
|
assert_eq!(1, args.thread_id);
|
|
|
|
Ok(dap::StackTraceResponse {
|
|
stack_frames: (*stack_frames).clone(),
|
|
total_frames: None,
|
|
})
|
|
}
|
|
});
|
|
|
|
let scopes = vec![
|
|
Scope {
|
|
name: "Scope 1".into(),
|
|
presentation_hint: Some(dap::ScopePresentationHint::Locals),
|
|
variables_reference: 2,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
expensive: false,
|
|
source: None,
|
|
line: None,
|
|
column: None,
|
|
end_line: None,
|
|
end_column: None,
|
|
},
|
|
Scope {
|
|
name: "Scope 2".into(),
|
|
presentation_hint: None,
|
|
variables_reference: 4,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
expensive: false,
|
|
source: None,
|
|
line: None,
|
|
column: None,
|
|
end_line: None,
|
|
end_column: None,
|
|
},
|
|
];
|
|
|
|
client.on_request::<Scopes, _>({
|
|
let scopes = Arc::new(scopes.clone());
|
|
move |_, args| {
|
|
assert_eq!(1, args.frame_id);
|
|
|
|
Ok(dap::ScopesResponse {
|
|
scopes: (*scopes).clone(),
|
|
})
|
|
}
|
|
});
|
|
|
|
let scope1_variables = vec![
|
|
Variable {
|
|
name: "variable1".into(),
|
|
value: "{nested1: \"Nested 1\", nested2: \"Nested 2\"}".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 3,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
Variable {
|
|
name: "variable2".into(),
|
|
value: "Value 2".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
];
|
|
|
|
let nested_variables = vec![
|
|
Variable {
|
|
name: "nested1".into(),
|
|
value: "Nested 1".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
Variable {
|
|
name: "nested2".into(),
|
|
value: "Nested 2".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
];
|
|
|
|
let scope2_variables = vec![Variable {
|
|
name: "variable3".into(),
|
|
value: "Value 3".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
}];
|
|
|
|
client.on_request::<Variables, _>({
|
|
let scope1_variables = Arc::new(scope1_variables.clone());
|
|
let nested_variables = Arc::new(nested_variables.clone());
|
|
let scope2_variables = Arc::new(scope2_variables.clone());
|
|
move |_, args| match args.variables_reference {
|
|
4 => Ok(dap::VariablesResponse {
|
|
variables: (*scope2_variables).clone(),
|
|
}),
|
|
3 => Ok(dap::VariablesResponse {
|
|
variables: (*nested_variables).clone(),
|
|
}),
|
|
2 => Ok(dap::VariablesResponse {
|
|
variables: (*scope1_variables).clone(),
|
|
}),
|
|
id => unreachable!("unexpected variables reference {id}"),
|
|
}
|
|
});
|
|
|
|
client
|
|
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
|
|
reason: dap::StoppedEventReason::Pause,
|
|
description: None,
|
|
thread_id: Some(1),
|
|
preserve_focus_hint: None,
|
|
text: None,
|
|
all_threads_stopped: None,
|
|
hit_breakpoint_ids: None,
|
|
}))
|
|
.await;
|
|
|
|
cx.run_until_parked();
|
|
let running_state =
|
|
active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
|
|
cx.focus_self(window);
|
|
let running = item.running_state().clone();
|
|
|
|
let variable_list = running.update(cx, |state, cx| {
|
|
// have to do this because the variable list pane should be shown/active
|
|
// for testing keyboard navigation
|
|
state.activate_item(DebuggerPaneItem::Variables, window, cx);
|
|
|
|
state.variable_list().clone()
|
|
});
|
|
variable_list.update(cx, |_, cx| cx.focus_self(window));
|
|
running
|
|
});
|
|
cx.dispatch_action(SelectFirst);
|
|
cx.dispatch_action(SelectFirst);
|
|
cx.run_until_parked();
|
|
|
|
running_state.update(cx, |running_state, cx| {
|
|
running_state
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1 <=== selected",
|
|
" > variable1",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
cx.dispatch_action(SelectNext);
|
|
cx.run_until_parked();
|
|
|
|
running_state.update(cx, |running_state, cx| {
|
|
running_state
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" > variable1 <=== selected",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// expand the nested variables of variable 1
|
|
cx.dispatch_action(ExpandSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |running_state, cx| {
|
|
running_state
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1 <=== selected",
|
|
" > nested1",
|
|
" > nested2",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// select the first nested variable of variable 1
|
|
cx.dispatch_action(SelectNext);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1 <=== selected",
|
|
" > nested2",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// select the second nested variable of variable 1
|
|
cx.dispatch_action(SelectNext);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1",
|
|
" > nested2 <=== selected",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// select variable 2 of scope 1
|
|
cx.dispatch_action(SelectNext);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1",
|
|
" > nested2",
|
|
" > variable2 <=== selected",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// select scope 2
|
|
cx.dispatch_action(SelectNext);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1",
|
|
" > nested2",
|
|
" > variable2",
|
|
"> Scope 2 <=== selected",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// expand the nested variables of scope 2
|
|
cx.dispatch_action(ExpandSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1",
|
|
" > nested2",
|
|
" > variable2",
|
|
"v Scope 2 <=== selected",
|
|
" > variable3",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// select variable 3 of scope 2
|
|
cx.dispatch_action(SelectNext);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1",
|
|
" > nested2",
|
|
" > variable2",
|
|
"v Scope 2",
|
|
" > variable3 <=== selected",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// select scope 2
|
|
cx.dispatch_action(SelectPrevious);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1",
|
|
" > nested2",
|
|
" > variable2",
|
|
"v Scope 2 <=== selected",
|
|
" > variable3",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// collapse variables of scope 2
|
|
cx.dispatch_action(CollapseSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1",
|
|
" > nested2",
|
|
" > variable2",
|
|
"> Scope 2 <=== selected",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// select variable 2 of scope 1
|
|
cx.dispatch_action(SelectPrevious);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1",
|
|
" > nested2",
|
|
" > variable2 <=== selected",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// select nested2 of variable 1
|
|
cx.dispatch_action(SelectPrevious);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1",
|
|
" > nested2 <=== selected",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// select nested1 of variable 1
|
|
cx.dispatch_action(SelectPrevious);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1 <=== selected",
|
|
" > nested2",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// select variable 1 of scope 1
|
|
cx.dispatch_action(SelectPrevious);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1 <=== selected",
|
|
" > nested1",
|
|
" > nested2",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// collapse variables of variable 1
|
|
cx.dispatch_action(CollapseSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" > variable1 <=== selected",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// select scope 1
|
|
cx.dispatch_action(SelectPrevious);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |running_state, cx| {
|
|
running_state
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1 <=== selected",
|
|
" > variable1",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
// collapse variables of scope 1
|
|
cx.dispatch_action(CollapseSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec!["> Scope 1 <=== selected", "> Scope 2"]);
|
|
});
|
|
});
|
|
|
|
// select scope 2 backwards
|
|
cx.dispatch_action(SelectPrevious);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec!["> Scope 1", "> Scope 2 <=== selected"]);
|
|
});
|
|
});
|
|
|
|
// select scope 1 backwards
|
|
cx.dispatch_action(SelectNext);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec!["> Scope 1 <=== selected", "> Scope 2"]);
|
|
});
|
|
});
|
|
|
|
// test stepping through nested with ExpandSelectedEntry/CollapseSelectedEntry actions
|
|
|
|
cx.dispatch_action(ExpandSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1 <=== selected",
|
|
" > variable1",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
cx.dispatch_action(ExpandSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" > variable1 <=== selected",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
cx.dispatch_action(ExpandSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1 <=== selected",
|
|
" > nested1",
|
|
" > nested2",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
cx.dispatch_action(ExpandSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1 <=== selected",
|
|
" > nested2",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
cx.dispatch_action(ExpandSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1",
|
|
" > nested2 <=== selected",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
cx.dispatch_action(ExpandSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1",
|
|
" > nested2",
|
|
" > variable2 <=== selected",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
cx.dispatch_action(CollapseSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1",
|
|
" > nested2 <=== selected",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
cx.dispatch_action(CollapseSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1",
|
|
" > nested1 <=== selected",
|
|
" > nested2",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
cx.dispatch_action(CollapseSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" v variable1 <=== selected",
|
|
" > nested1",
|
|
" > nested2",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
cx.dispatch_action(CollapseSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1",
|
|
" > variable1 <=== selected",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
cx.dispatch_action(CollapseSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec![
|
|
"v Scope 1 <=== selected",
|
|
" > variable1",
|
|
" > variable2",
|
|
"> Scope 2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
cx.dispatch_action(CollapseSelectedEntry);
|
|
cx.run_until_parked();
|
|
running_state.update(cx, |debug_panel_item, cx| {
|
|
debug_panel_item
|
|
.variable_list()
|
|
.update(cx, |variable_list, _| {
|
|
variable_list.assert_visual_entries(vec!["> Scope 1 <=== selected", "> Scope 2"]);
|
|
});
|
|
});
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_variable_list_only_sends_requests_when_rendering(
|
|
executor: BackgroundExecutor,
|
|
cx: &mut TestAppContext,
|
|
) {
|
|
init_test(cx);
|
|
|
|
let fs = FakeFs::new(executor.clone());
|
|
|
|
let test_file_content = r#"
|
|
import { SOME_VALUE } './module.js';
|
|
|
|
console.log(SOME_VALUE);
|
|
"#
|
|
.unindent();
|
|
|
|
let module_file_content = r#"
|
|
export SOME_VALUE = 'some value';
|
|
"#
|
|
.unindent();
|
|
|
|
fs.insert_tree(
|
|
path!("/project"),
|
|
json!({
|
|
"src": {
|
|
"test.js": test_file_content,
|
|
"module.js": module_file_content,
|
|
}
|
|
}),
|
|
)
|
|
.await;
|
|
|
|
let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
|
|
let workspace = init_test_workspace(&project, cx).await;
|
|
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
|
|
|
let session = start_debug_session(&workspace, cx, |_| {}).unwrap();
|
|
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
|
|
|
|
client.on_request::<dap::requests::Threads, _>(move |_, _| {
|
|
Ok(dap::ThreadsResponse {
|
|
threads: vec![dap::Thread {
|
|
id: 1,
|
|
name: "Thread 1".into(),
|
|
}],
|
|
})
|
|
});
|
|
|
|
client.on_request::<Initialize, _>(move |_, _| {
|
|
Ok(dap::Capabilities {
|
|
supports_step_back: Some(false),
|
|
..Default::default()
|
|
})
|
|
});
|
|
|
|
client.on_request::<Launch, _>(move |_, _| Ok(()));
|
|
|
|
let stack_frames = vec![
|
|
StackFrame {
|
|
id: 1,
|
|
name: "Stack Frame 1".into(),
|
|
source: Some(dap::Source {
|
|
name: Some("test.js".into()),
|
|
path: Some(path!("/project/src/test.js").into()),
|
|
source_reference: None,
|
|
presentation_hint: None,
|
|
origin: None,
|
|
sources: None,
|
|
adapter_data: None,
|
|
checksums: None,
|
|
}),
|
|
line: 3,
|
|
column: 1,
|
|
end_line: None,
|
|
end_column: None,
|
|
can_restart: None,
|
|
instruction_pointer_reference: None,
|
|
module_id: None,
|
|
presentation_hint: None,
|
|
},
|
|
StackFrame {
|
|
id: 2,
|
|
name: "Stack Frame 2".into(),
|
|
source: Some(dap::Source {
|
|
name: Some("module.js".into()),
|
|
path: Some(path!("/project/src/module.js").into()),
|
|
source_reference: None,
|
|
presentation_hint: None,
|
|
origin: None,
|
|
sources: None,
|
|
adapter_data: None,
|
|
checksums: None,
|
|
}),
|
|
line: 1,
|
|
column: 1,
|
|
end_line: None,
|
|
end_column: None,
|
|
can_restart: None,
|
|
instruction_pointer_reference: None,
|
|
module_id: None,
|
|
presentation_hint: None,
|
|
},
|
|
];
|
|
|
|
client.on_request::<StackTrace, _>({
|
|
let stack_frames = Arc::new(stack_frames.clone());
|
|
move |_, args| {
|
|
assert_eq!(1, args.thread_id);
|
|
|
|
Ok(dap::StackTraceResponse {
|
|
stack_frames: (*stack_frames).clone(),
|
|
total_frames: None,
|
|
})
|
|
}
|
|
});
|
|
|
|
let frame_1_scopes = vec![Scope {
|
|
name: "Frame 1 Scope 1".into(),
|
|
presentation_hint: None,
|
|
variables_reference: 2,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
expensive: false,
|
|
source: None,
|
|
line: None,
|
|
column: None,
|
|
end_line: None,
|
|
end_column: None,
|
|
}];
|
|
|
|
let made_scopes_request = Arc::new(AtomicBool::new(false));
|
|
|
|
client.on_request::<Scopes, _>({
|
|
let frame_1_scopes = Arc::new(frame_1_scopes.clone());
|
|
let made_scopes_request = made_scopes_request.clone();
|
|
move |_, args| {
|
|
assert_eq!(1, args.frame_id);
|
|
assert!(
|
|
!made_scopes_request.load(Ordering::SeqCst),
|
|
"We should be caching the scope request"
|
|
);
|
|
|
|
made_scopes_request.store(true, Ordering::SeqCst);
|
|
|
|
Ok(dap::ScopesResponse {
|
|
scopes: (*frame_1_scopes).clone(),
|
|
})
|
|
}
|
|
});
|
|
|
|
let frame_1_variables = vec![
|
|
Variable {
|
|
name: "variable1".into(),
|
|
value: "value 1".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
Variable {
|
|
name: "variable2".into(),
|
|
value: "value 2".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
];
|
|
|
|
client.on_request::<Variables, _>({
|
|
let frame_1_variables = Arc::new(frame_1_variables.clone());
|
|
move |_, args| {
|
|
assert_eq!(2, args.variables_reference);
|
|
|
|
Ok(dap::VariablesResponse {
|
|
variables: (*frame_1_variables).clone(),
|
|
})
|
|
}
|
|
});
|
|
|
|
cx.run_until_parked();
|
|
|
|
let running_state = active_debug_session_panel(workspace, cx)
|
|
.update_in(cx, |item, _, _| item.running_state().clone());
|
|
|
|
client
|
|
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
|
|
reason: dap::StoppedEventReason::Pause,
|
|
description: None,
|
|
thread_id: Some(1),
|
|
preserve_focus_hint: None,
|
|
text: None,
|
|
all_threads_stopped: None,
|
|
hit_breakpoint_ids: None,
|
|
}))
|
|
.await;
|
|
|
|
cx.run_until_parked();
|
|
|
|
running_state.update(cx, |running_state, cx| {
|
|
let (stack_frame_list, stack_frame_id) =
|
|
running_state.stack_frame_list().update(cx, |list, _| {
|
|
(
|
|
list.flatten_entries(true, true),
|
|
list.opened_stack_frame_id(),
|
|
)
|
|
});
|
|
|
|
assert_eq!(Some(1), stack_frame_id);
|
|
assert_eq!(stack_frames, stack_frame_list);
|
|
|
|
let variable_list = running_state.variable_list().read(cx);
|
|
|
|
assert_eq!(frame_1_variables, variable_list.variables());
|
|
assert!(made_scopes_request.load(Ordering::SeqCst));
|
|
});
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame(
|
|
executor: BackgroundExecutor,
|
|
cx: &mut TestAppContext,
|
|
) {
|
|
init_test(cx);
|
|
|
|
let fs = FakeFs::new(executor.clone());
|
|
|
|
let test_file_content = r#"
|
|
import { SOME_VALUE } './module.js';
|
|
|
|
console.log(SOME_VALUE);
|
|
"#
|
|
.unindent();
|
|
|
|
let module_file_content = r#"
|
|
export SOME_VALUE = 'some value';
|
|
"#
|
|
.unindent();
|
|
|
|
fs.insert_tree(
|
|
path!("/project"),
|
|
json!({
|
|
"src": {
|
|
"test.js": test_file_content,
|
|
"module.js": module_file_content,
|
|
}
|
|
}),
|
|
)
|
|
.await;
|
|
|
|
let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
|
|
let workspace = init_test_workspace(&project, cx).await;
|
|
workspace
|
|
.update(cx, |workspace, window, cx| {
|
|
workspace.focus_panel::<DebugPanel>(window, cx);
|
|
})
|
|
.unwrap();
|
|
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
|
|
|
let session = start_debug_session(&workspace, cx, |_| {}).unwrap();
|
|
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
|
|
|
|
client.on_request::<dap::requests::Threads, _>(move |_, _| {
|
|
Ok(dap::ThreadsResponse {
|
|
threads: vec![dap::Thread {
|
|
id: 1,
|
|
name: "Thread 1".into(),
|
|
}],
|
|
})
|
|
});
|
|
|
|
client.on_request::<Initialize, _>(move |_, _| {
|
|
Ok(dap::Capabilities {
|
|
supports_step_back: Some(false),
|
|
..Default::default()
|
|
})
|
|
});
|
|
|
|
client.on_request::<Launch, _>(move |_, _| Ok(()));
|
|
|
|
let stack_frames = vec![
|
|
StackFrame {
|
|
id: 1,
|
|
name: "Stack Frame 1".into(),
|
|
source: Some(dap::Source {
|
|
name: Some("test.js".into()),
|
|
path: Some(path!("/project/src/test.js").into()),
|
|
source_reference: None,
|
|
presentation_hint: None,
|
|
origin: None,
|
|
sources: None,
|
|
adapter_data: None,
|
|
checksums: None,
|
|
}),
|
|
line: 3,
|
|
column: 1,
|
|
end_line: None,
|
|
end_column: None,
|
|
can_restart: None,
|
|
instruction_pointer_reference: None,
|
|
module_id: None,
|
|
presentation_hint: None,
|
|
},
|
|
StackFrame {
|
|
id: 2,
|
|
name: "Stack Frame 2".into(),
|
|
source: Some(dap::Source {
|
|
name: Some("module.js".into()),
|
|
path: Some(path!("/project/src/module.js").into()),
|
|
source_reference: None,
|
|
presentation_hint: None,
|
|
origin: None,
|
|
sources: None,
|
|
adapter_data: None,
|
|
checksums: None,
|
|
}),
|
|
line: 1,
|
|
column: 1,
|
|
end_line: None,
|
|
end_column: None,
|
|
can_restart: None,
|
|
instruction_pointer_reference: None,
|
|
module_id: None,
|
|
presentation_hint: None,
|
|
},
|
|
];
|
|
|
|
client.on_request::<StackTrace, _>({
|
|
let stack_frames = Arc::new(stack_frames.clone());
|
|
move |_, args| {
|
|
assert_eq!(1, args.thread_id);
|
|
|
|
Ok(dap::StackTraceResponse {
|
|
stack_frames: (*stack_frames).clone(),
|
|
total_frames: None,
|
|
})
|
|
}
|
|
});
|
|
|
|
let frame_1_scopes = vec![Scope {
|
|
name: "Frame 1 Scope 1".into(),
|
|
presentation_hint: None,
|
|
variables_reference: 2,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
expensive: false,
|
|
source: None,
|
|
line: None,
|
|
column: None,
|
|
end_line: None,
|
|
end_column: None,
|
|
}];
|
|
|
|
// add handlers for fetching the second stack frame's scopes and variables
|
|
// after the user clicked the stack frame
|
|
let frame_2_scopes = vec![Scope {
|
|
name: "Frame 2 Scope 1".into(),
|
|
presentation_hint: None,
|
|
variables_reference: 3,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
expensive: false,
|
|
source: None,
|
|
line: None,
|
|
column: None,
|
|
end_line: None,
|
|
end_column: None,
|
|
}];
|
|
|
|
let called_second_stack_frame = Arc::new(AtomicBool::new(false));
|
|
let called_first_stack_frame = Arc::new(AtomicBool::new(false));
|
|
|
|
client.on_request::<Scopes, _>({
|
|
let frame_1_scopes = Arc::new(frame_1_scopes.clone());
|
|
let frame_2_scopes = Arc::new(frame_2_scopes.clone());
|
|
let called_first_stack_frame = called_first_stack_frame.clone();
|
|
let called_second_stack_frame = called_second_stack_frame.clone();
|
|
move |_, args| match args.frame_id {
|
|
1 => {
|
|
called_first_stack_frame.store(true, Ordering::SeqCst);
|
|
Ok(dap::ScopesResponse {
|
|
scopes: (*frame_1_scopes).clone(),
|
|
})
|
|
}
|
|
2 => {
|
|
called_second_stack_frame.store(true, Ordering::SeqCst);
|
|
|
|
Ok(dap::ScopesResponse {
|
|
scopes: (*frame_2_scopes).clone(),
|
|
})
|
|
}
|
|
_ => panic!("Made a scopes request with an invalid frame id"),
|
|
}
|
|
});
|
|
|
|
let frame_1_variables = vec![
|
|
Variable {
|
|
name: "variable1".into(),
|
|
value: "value 1".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
Variable {
|
|
name: "variable2".into(),
|
|
value: "value 2".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
];
|
|
|
|
let frame_2_variables = vec![
|
|
Variable {
|
|
name: "variable3".into(),
|
|
value: "old value 1".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
Variable {
|
|
name: "variable4".into(),
|
|
value: "old value 2".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
];
|
|
|
|
client.on_request::<Variables, _>({
|
|
let frame_1_variables = Arc::new(frame_1_variables.clone());
|
|
move |_, args| {
|
|
assert_eq!(2, args.variables_reference);
|
|
|
|
Ok(dap::VariablesResponse {
|
|
variables: (*frame_1_variables).clone(),
|
|
})
|
|
}
|
|
});
|
|
|
|
client
|
|
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
|
|
reason: dap::StoppedEventReason::Pause,
|
|
description: None,
|
|
thread_id: Some(1),
|
|
preserve_focus_hint: None,
|
|
text: None,
|
|
all_threads_stopped: None,
|
|
hit_breakpoint_ids: None,
|
|
}))
|
|
.await;
|
|
|
|
cx.run_until_parked();
|
|
|
|
let running_state =
|
|
active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
|
|
cx.focus_self(window);
|
|
item.running_state().clone()
|
|
});
|
|
|
|
running_state.update(cx, |running_state, cx| {
|
|
let (stack_frame_list, stack_frame_id) =
|
|
running_state.stack_frame_list().update(cx, |list, _| {
|
|
(
|
|
list.flatten_entries(true, true),
|
|
list.opened_stack_frame_id(),
|
|
)
|
|
});
|
|
|
|
let variable_list = running_state.variable_list().read(cx);
|
|
let variables = variable_list.variables();
|
|
|
|
assert_eq!(Some(1), stack_frame_id);
|
|
assert_eq!(
|
|
running_state
|
|
.stack_frame_list()
|
|
.read(cx)
|
|
.opened_stack_frame_id(),
|
|
Some(1)
|
|
);
|
|
|
|
assert!(
|
|
called_first_stack_frame.load(std::sync::atomic::Ordering::SeqCst),
|
|
"Request scopes shouldn't be called before it's needed"
|
|
);
|
|
assert!(
|
|
!called_second_stack_frame.load(std::sync::atomic::Ordering::SeqCst),
|
|
"Request scopes shouldn't be called before it's needed"
|
|
);
|
|
|
|
assert_eq!(stack_frames, stack_frame_list);
|
|
assert_eq!(frame_1_variables, variables);
|
|
});
|
|
|
|
client.on_request::<Variables, _>({
|
|
let frame_2_variables = Arc::new(frame_2_variables.clone());
|
|
move |_, args| {
|
|
assert_eq!(3, args.variables_reference);
|
|
|
|
Ok(dap::VariablesResponse {
|
|
variables: (*frame_2_variables).clone(),
|
|
})
|
|
}
|
|
});
|
|
|
|
running_state
|
|
.update_in(cx, |running_state, window, cx| {
|
|
running_state
|
|
.stack_frame_list()
|
|
.update(cx, |stack_frame_list, cx| {
|
|
stack_frame_list.go_to_stack_frame(stack_frames[1].id, window, cx)
|
|
})
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
cx.run_until_parked();
|
|
|
|
running_state.update(cx, |running_state, cx| {
|
|
let (stack_frame_list, stack_frame_id) =
|
|
running_state.stack_frame_list().update(cx, |list, _| {
|
|
(
|
|
list.flatten_entries(true, true),
|
|
list.opened_stack_frame_id(),
|
|
)
|
|
});
|
|
|
|
let variable_list = running_state.variable_list().read(cx);
|
|
let variables = variable_list.variables();
|
|
|
|
assert_eq!(Some(2), stack_frame_id);
|
|
assert!(
|
|
called_second_stack_frame.load(std::sync::atomic::Ordering::SeqCst),
|
|
"Request scopes shouldn't be called before it's needed"
|
|
);
|
|
|
|
assert_eq!(stack_frames, stack_frame_list);
|
|
|
|
assert_eq!(variables, frame_2_variables,);
|
|
});
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_add_and_remove_watcher(executor: BackgroundExecutor, cx: &mut TestAppContext) {
|
|
init_test(cx);
|
|
|
|
let fs = FakeFs::new(executor.clone());
|
|
|
|
let test_file_content = r#"
|
|
const variable1 = "Value 1";
|
|
const variable2 = "Value 2";
|
|
"#
|
|
.unindent();
|
|
|
|
fs.insert_tree(
|
|
path!("/project"),
|
|
json!({
|
|
"src": {
|
|
"test.js": test_file_content,
|
|
}
|
|
}),
|
|
)
|
|
.await;
|
|
|
|
let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
|
|
let workspace = init_test_workspace(&project, cx).await;
|
|
workspace
|
|
.update(cx, |workspace, window, cx| {
|
|
workspace.focus_panel::<DebugPanel>(window, cx);
|
|
})
|
|
.unwrap();
|
|
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
|
let session = start_debug_session(&workspace, cx, |_| {}).unwrap();
|
|
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
|
|
|
|
client.on_request::<dap::requests::Threads, _>(move |_, _| {
|
|
Ok(dap::ThreadsResponse {
|
|
threads: vec![dap::Thread {
|
|
id: 1,
|
|
name: "Thread 1".into(),
|
|
}],
|
|
})
|
|
});
|
|
|
|
let stack_frames = vec![StackFrame {
|
|
id: 1,
|
|
name: "Stack Frame 1".into(),
|
|
source: Some(dap::Source {
|
|
name: Some("test.js".into()),
|
|
path: Some(path!("/project/src/test.js").into()),
|
|
source_reference: None,
|
|
presentation_hint: None,
|
|
origin: None,
|
|
sources: None,
|
|
adapter_data: None,
|
|
checksums: None,
|
|
}),
|
|
line: 1,
|
|
column: 1,
|
|
end_line: None,
|
|
end_column: None,
|
|
can_restart: None,
|
|
instruction_pointer_reference: None,
|
|
module_id: None,
|
|
presentation_hint: None,
|
|
}];
|
|
|
|
client.on_request::<StackTrace, _>({
|
|
let stack_frames = Arc::new(stack_frames.clone());
|
|
move |_, args| {
|
|
assert_eq!(1, args.thread_id);
|
|
|
|
Ok(dap::StackTraceResponse {
|
|
stack_frames: (*stack_frames).clone(),
|
|
total_frames: None,
|
|
})
|
|
}
|
|
});
|
|
|
|
let scopes = vec![Scope {
|
|
name: "Scope 1".into(),
|
|
presentation_hint: None,
|
|
variables_reference: 2,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
expensive: false,
|
|
source: None,
|
|
line: None,
|
|
column: None,
|
|
end_line: None,
|
|
end_column: None,
|
|
}];
|
|
|
|
client.on_request::<Scopes, _>({
|
|
let scopes = Arc::new(scopes.clone());
|
|
move |_, args| {
|
|
assert_eq!(1, args.frame_id);
|
|
|
|
Ok(dap::ScopesResponse {
|
|
scopes: (*scopes).clone(),
|
|
})
|
|
}
|
|
});
|
|
|
|
let variables = vec![
|
|
Variable {
|
|
name: "variable1".into(),
|
|
value: "value 1".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
Variable {
|
|
name: "variable2".into(),
|
|
value: "value 2".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
];
|
|
|
|
client.on_request::<Variables, _>({
|
|
let variables = Arc::new(variables.clone());
|
|
move |_, args| {
|
|
assert_eq!(2, args.variables_reference);
|
|
|
|
Ok(dap::VariablesResponse {
|
|
variables: (*variables).clone(),
|
|
})
|
|
}
|
|
});
|
|
|
|
client.on_request::<Evaluate, _>({
|
|
move |_, args| {
|
|
assert_eq!("variable1", args.expression);
|
|
|
|
Ok(dap::EvaluateResponse {
|
|
result: "value1".to_owned(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
variables_reference: 2,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
value_location_reference: None,
|
|
})
|
|
}
|
|
});
|
|
|
|
client
|
|
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
|
|
reason: dap::StoppedEventReason::Pause,
|
|
description: None,
|
|
thread_id: Some(1),
|
|
preserve_focus_hint: None,
|
|
text: None,
|
|
all_threads_stopped: None,
|
|
hit_breakpoint_ids: None,
|
|
}))
|
|
.await;
|
|
|
|
cx.run_until_parked();
|
|
|
|
let running_state =
|
|
active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
|
|
cx.focus_self(window);
|
|
let running = item.running_state().clone();
|
|
|
|
let variable_list = running.update(cx, |state, cx| {
|
|
// have to do this because the variable list pane should be shown/active
|
|
// for testing the variable list
|
|
state.activate_item(DebuggerPaneItem::Variables, window, cx);
|
|
|
|
state.variable_list().clone()
|
|
});
|
|
variable_list.update(cx, |_, cx| cx.focus_self(window));
|
|
running
|
|
});
|
|
cx.run_until_parked();
|
|
|
|
// select variable 1 from first scope
|
|
running_state.update(cx, |running_state, cx| {
|
|
running_state.variable_list().update(cx, |_, cx| {
|
|
cx.dispatch_action(&SelectFirst);
|
|
cx.dispatch_action(&SelectNext);
|
|
});
|
|
});
|
|
cx.run_until_parked();
|
|
|
|
running_state.update(cx, |running_state, cx| {
|
|
running_state.variable_list().update(cx, |_, cx| {
|
|
cx.dispatch_action(&AddWatch);
|
|
});
|
|
});
|
|
cx.run_until_parked();
|
|
|
|
// assert watcher for variable1 was added
|
|
running_state.update(cx, |running_state, cx| {
|
|
running_state.variable_list().update(cx, |list, _| {
|
|
list.assert_visual_entries(vec![
|
|
"> variable1",
|
|
"v Scope 1",
|
|
" > variable1 <=== selected",
|
|
" > variable2",
|
|
]);
|
|
});
|
|
});
|
|
|
|
session.update(cx, |session, _| {
|
|
let watcher = session
|
|
.watchers()
|
|
.get(&SharedString::from("variable1"))
|
|
.unwrap();
|
|
|
|
assert_eq!("value1", watcher.value.to_string());
|
|
assert_eq!("variable1", watcher.expression.to_string());
|
|
assert_eq!(2, watcher.variables_reference);
|
|
});
|
|
|
|
// select added watcher for variable1
|
|
running_state.update(cx, |running_state, cx| {
|
|
running_state.variable_list().update(cx, |_, cx| {
|
|
cx.dispatch_action(&SelectFirst);
|
|
});
|
|
});
|
|
cx.run_until_parked();
|
|
|
|
running_state.update(cx, |running_state, cx| {
|
|
running_state.variable_list().update(cx, |_, cx| {
|
|
cx.dispatch_action(&RemoveWatch);
|
|
});
|
|
});
|
|
cx.run_until_parked();
|
|
|
|
// assert watcher for variable1 was removed
|
|
running_state.update(cx, |running_state, cx| {
|
|
running_state.variable_list().update(cx, |list, _| {
|
|
list.assert_visual_entries(vec!["v Scope 1", " > variable1", " > variable2"]);
|
|
});
|
|
});
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_refresh_watchers(executor: BackgroundExecutor, cx: &mut TestAppContext) {
|
|
init_test(cx);
|
|
|
|
let fs = FakeFs::new(executor.clone());
|
|
|
|
let test_file_content = r#"
|
|
const variable1 = "Value 1";
|
|
const variable2 = "Value 2";
|
|
"#
|
|
.unindent();
|
|
|
|
fs.insert_tree(
|
|
path!("/project"),
|
|
json!({
|
|
"src": {
|
|
"test.js": test_file_content,
|
|
}
|
|
}),
|
|
)
|
|
.await;
|
|
|
|
let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
|
|
let workspace = init_test_workspace(&project, cx).await;
|
|
workspace
|
|
.update(cx, |workspace, window, cx| {
|
|
workspace.focus_panel::<DebugPanel>(window, cx);
|
|
})
|
|
.unwrap();
|
|
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
|
let session = start_debug_session(&workspace, cx, |_| {}).unwrap();
|
|
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
|
|
|
|
client.on_request::<dap::requests::Threads, _>(move |_, _| {
|
|
Ok(dap::ThreadsResponse {
|
|
threads: vec![dap::Thread {
|
|
id: 1,
|
|
name: "Thread 1".into(),
|
|
}],
|
|
})
|
|
});
|
|
|
|
let stack_frames = vec![StackFrame {
|
|
id: 1,
|
|
name: "Stack Frame 1".into(),
|
|
source: Some(dap::Source {
|
|
name: Some("test.js".into()),
|
|
path: Some(path!("/project/src/test.js").into()),
|
|
source_reference: None,
|
|
presentation_hint: None,
|
|
origin: None,
|
|
sources: None,
|
|
adapter_data: None,
|
|
checksums: None,
|
|
}),
|
|
line: 1,
|
|
column: 1,
|
|
end_line: None,
|
|
end_column: None,
|
|
can_restart: None,
|
|
instruction_pointer_reference: None,
|
|
module_id: None,
|
|
presentation_hint: None,
|
|
}];
|
|
|
|
client.on_request::<StackTrace, _>({
|
|
let stack_frames = Arc::new(stack_frames.clone());
|
|
move |_, args| {
|
|
assert_eq!(1, args.thread_id);
|
|
|
|
Ok(dap::StackTraceResponse {
|
|
stack_frames: (*stack_frames).clone(),
|
|
total_frames: None,
|
|
})
|
|
}
|
|
});
|
|
|
|
let scopes = vec![Scope {
|
|
name: "Scope 1".into(),
|
|
presentation_hint: None,
|
|
variables_reference: 2,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
expensive: false,
|
|
source: None,
|
|
line: None,
|
|
column: None,
|
|
end_line: None,
|
|
end_column: None,
|
|
}];
|
|
|
|
client.on_request::<Scopes, _>({
|
|
let scopes = Arc::new(scopes.clone());
|
|
move |_, args| {
|
|
assert_eq!(1, args.frame_id);
|
|
|
|
Ok(dap::ScopesResponse {
|
|
scopes: (*scopes).clone(),
|
|
})
|
|
}
|
|
});
|
|
|
|
let variables = vec![
|
|
Variable {
|
|
name: "variable1".into(),
|
|
value: "value 1".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
Variable {
|
|
name: "variable2".into(),
|
|
value: "value 2".into(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
evaluate_name: None,
|
|
variables_reference: 0,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
declaration_location_reference: None,
|
|
value_location_reference: None,
|
|
},
|
|
];
|
|
|
|
client.on_request::<Variables, _>({
|
|
let variables = Arc::new(variables.clone());
|
|
move |_, args| {
|
|
assert_eq!(2, args.variables_reference);
|
|
|
|
Ok(dap::VariablesResponse {
|
|
variables: (*variables).clone(),
|
|
})
|
|
}
|
|
});
|
|
|
|
client.on_request::<Evaluate, _>({
|
|
move |_, args| {
|
|
assert_eq!("variable1", args.expression);
|
|
|
|
Ok(dap::EvaluateResponse {
|
|
result: "value1".to_owned(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
variables_reference: 2,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
value_location_reference: None,
|
|
})
|
|
}
|
|
});
|
|
|
|
client
|
|
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
|
|
reason: dap::StoppedEventReason::Pause,
|
|
description: None,
|
|
thread_id: Some(1),
|
|
preserve_focus_hint: None,
|
|
text: None,
|
|
all_threads_stopped: None,
|
|
hit_breakpoint_ids: None,
|
|
}))
|
|
.await;
|
|
|
|
cx.run_until_parked();
|
|
|
|
let running_state =
|
|
active_debug_session_panel(workspace, cx).update_in(cx, |item, window, cx| {
|
|
cx.focus_self(window);
|
|
let running = item.running_state().clone();
|
|
|
|
let variable_list = running.update(cx, |state, cx| {
|
|
// have to do this because the variable list pane should be shown/active
|
|
// for testing the variable list
|
|
state.activate_item(DebuggerPaneItem::Variables, window, cx);
|
|
|
|
state.variable_list().clone()
|
|
});
|
|
variable_list.update(cx, |_, cx| cx.focus_self(window));
|
|
running
|
|
});
|
|
cx.run_until_parked();
|
|
|
|
// select variable 1 from first scope
|
|
running_state.update(cx, |running_state, cx| {
|
|
running_state.variable_list().update(cx, |_, cx| {
|
|
cx.dispatch_action(&SelectFirst);
|
|
cx.dispatch_action(&SelectNext);
|
|
});
|
|
});
|
|
cx.run_until_parked();
|
|
|
|
running_state.update(cx, |running_state, cx| {
|
|
running_state.variable_list().update(cx, |_, cx| {
|
|
cx.dispatch_action(&AddWatch);
|
|
});
|
|
});
|
|
cx.run_until_parked();
|
|
|
|
session.update(cx, |session, _| {
|
|
let watcher = session
|
|
.watchers()
|
|
.get(&SharedString::from("variable1"))
|
|
.unwrap();
|
|
|
|
assert_eq!("value1", watcher.value.to_string());
|
|
assert_eq!("variable1", watcher.expression.to_string());
|
|
assert_eq!(2, watcher.variables_reference);
|
|
});
|
|
|
|
client.on_request::<Evaluate, _>({
|
|
move |_, args| {
|
|
assert_eq!("variable1", args.expression);
|
|
|
|
Ok(dap::EvaluateResponse {
|
|
result: "value updated".to_owned(),
|
|
type_: None,
|
|
presentation_hint: None,
|
|
variables_reference: 3,
|
|
named_variables: None,
|
|
indexed_variables: None,
|
|
memory_reference: None,
|
|
value_location_reference: None,
|
|
})
|
|
}
|
|
});
|
|
|
|
client
|
|
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
|
|
reason: dap::StoppedEventReason::Pause,
|
|
description: None,
|
|
thread_id: Some(1),
|
|
preserve_focus_hint: None,
|
|
text: None,
|
|
all_threads_stopped: None,
|
|
hit_breakpoint_ids: None,
|
|
}))
|
|
.await;
|
|
|
|
cx.run_until_parked();
|
|
|
|
session.update(cx, |session, _| {
|
|
let watcher = session
|
|
.watchers()
|
|
.get(&SharedString::from("variable1"))
|
|
.unwrap();
|
|
|
|
assert_eq!("value updated", watcher.value.to_string());
|
|
assert_eq!("variable1", watcher.expression.to_string());
|
|
assert_eq!(3, watcher.variables_reference);
|
|
});
|
|
}
|