Skip to content

Commit

Permalink
Add basic test for RunInTerminal reverse request to spawn terminal
Browse files Browse the repository at this point in the history
This commit also adds a way to add a handler when a response is received from a specific reverse request.
  • Loading branch information
RemcoSmitsDev committed Dec 26, 2024
1 parent fba00c7 commit 7ced61d
Show file tree
Hide file tree
Showing 8 changed files with 265 additions and 63 deletions.
10 changes: 10 additions & 0 deletions crates/dap/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,16 @@ impl DebugAdapterClient {
.unwrap();
}

#[cfg(any(test, feature = "test-support"))]
pub async fn on_response<R: dap_types::requests::Request, F>(&self, handler: F)
where
F: 'static + Send + Fn(Response),
{
let transport = self.transport_delegate.transport();

transport.as_fake().on_response::<R, F>(handler).await;
}

#[cfg(any(test, feature = "test-support"))]
pub async fn fake_event(&self, event: dap_types::messages::Events) {
self.send_message(Message::Event(Box::new(event)))
Expand Down
96 changes: 68 additions & 28 deletions crates/dap/src/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -752,16 +752,23 @@ type RequestHandler = Box<
) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>>,
>;

#[cfg(any(test, feature = "test-support"))]
type ResponseHandler = Box<dyn Send + Fn(Response)>;

#[cfg(any(test, feature = "test-support"))]
pub struct FakeTransport {
// for sending fake response back from adapter side
request_handlers: Arc<Mutex<HashMap<&'static str, RequestHandler>>>,
// for reverse request responses
response_handlers: Arc<Mutex<HashMap<&'static str, ResponseHandler>>>,
}

#[cfg(any(test, feature = "test-support"))]
impl FakeTransport {
pub fn new() -> Self {
Self {
request_handlers: Arc::new(Mutex::new(HashMap::default())),
response_handlers: Arc::new(Mutex::new(HashMap::default())),
}
}

Expand Down Expand Up @@ -799,6 +806,16 @@ impl FakeTransport {
),
);
}

pub async fn on_response<R: dap_types::requests::Request, F>(&self, handler: F)
where
F: 'static + Send + Fn(Response),
{
self.response_handlers
.lock()
.await
.insert(R::COMMAND, Box::new(handler));
}
}

#[cfg(any(test, feature = "test-support"))]
Expand All @@ -815,7 +832,8 @@ impl Transport for FakeTransport {
let (stdin_writer, stdin_reader) = async_pipe::pipe();
let (stdout_writer, stdout_reader) = async_pipe::pipe();

let handlers = self.request_handlers.clone();
let request_handlers = self.request_handlers.clone();
let response_handlers = self.response_handlers.clone();
let stdout_writer = Arc::new(Mutex::new(stdout_writer));

cx.background_executor()
Expand All @@ -833,13 +851,48 @@ impl Transport for FakeTransport {
break anyhow!(error);
}
Ok(message) => {
if let Message::Request(request) = message {
// redirect reverse requests to stdout writer/reader
if request.command == RunInTerminal::COMMAND
|| request.command == StartDebugging::COMMAND
{
match message {
Message::Request(request) => {
// redirect reverse requests to stdout writer/reader
if request.command == RunInTerminal::COMMAND
|| request.command == StartDebugging::COMMAND
{
let message =
serde_json::to_string(&Message::Request(request))
.unwrap();

let mut writer = stdout_writer.lock().await;
writer
.write_all(
TransportDelegate::build_rpc_message(message)
.as_bytes(),
)
.await
.unwrap();
writer.flush().await.unwrap();
} else {
if let Some(handle) = request_handlers
.lock()
.await
.get_mut(request.command.as_str())
{
handle(
request.seq,
request.arguments.unwrap_or(json!({})),
stdout_writer.clone(),
)
.await;
} else {
log::error!(
"No request handler for {}",
request.command
);
}
}
}
Message::Event(event) => {
let message =
serde_json::to_string(&Message::Request(request)).unwrap();
serde_json::to_string(&Message::Event(event)).unwrap();

let mut writer = stdout_writer.lock().await;
writer
Expand All @@ -850,31 +903,18 @@ impl Transport for FakeTransport {
.await
.unwrap();
writer.flush().await.unwrap();
} else {
if let Some(handle) =
handlers.lock().await.get_mut(request.command.as_str())
}
Message::Response(response) => {
if let Some(handle) = response_handlers
.lock()
.await
.get(response.command.as_str())
{
handle(
request.seq,
request.arguments.unwrap_or(json!({})),
stdout_writer.clone(),
)
.await;
handle(response);
} else {
log::debug!("No handler for {}", request.command);
log::error!("No response handler for {}", response.command);
}
}
} else if let Message::Event(event) = message {
let message = serde_json::to_string(&Message::Event(event)).unwrap();

let mut writer = stdout_writer.lock().await;
writer
.write_all(TransportDelegate::build_rpc_message(message).as_bytes())
.await
.unwrap();
writer.flush().await.unwrap();
} else {
unreachable!("You can only send a request and an event that is redirected to the output reader")
}
}
}
Expand Down
12 changes: 11 additions & 1 deletion crates/debugger_ui/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use gpui::{Model, TestAppContext, WindowHandle};
use project::Project;
use settings::SettingsStore;
use terminal_view::terminal_panel::TerminalPanel;
use workspace::Workspace;

use crate::debugger_panel::DebugPanel;

mod console;
mod debugger_panel;
mod run_in_terminal;
mod stack_frame_list;
mod variable_list;

Expand All @@ -18,6 +20,7 @@ pub fn init_test(cx: &mut gpui::TestAppContext) {
cx.update(|cx| {
let settings = SettingsStore::test(cx);
cx.set_global(settings);
terminal_view::init(cx);
theme::init(theme::LoadThemes::JustBase, cx);
command_palette_hooks::init(cx);
language::init(cx);
Expand All @@ -28,7 +31,7 @@ pub fn init_test(cx: &mut gpui::TestAppContext) {
});
}

pub async fn add_debugger_panel(
pub async fn init_test_workspace(
project: &Model<Project>,
cx: &mut TestAppContext,
) -> WindowHandle<Workspace> {
Expand All @@ -40,9 +43,16 @@ pub async fn add_debugger_panel(
.await
.expect("Failed to load debug panel");

let terminal_panel = window
.update(cx, |_, cx| cx.spawn(TerminalPanel::load))
.unwrap()
.await
.expect("Failed to load terminal panel");

window
.update(cx, |workspace, cx| {
workspace.add_panel(debugger_panel, cx);
workspace.add_panel(terminal_panel, cx);
})
.unwrap();
window
Expand Down
8 changes: 4 additions & 4 deletions crates/debugger_ui/src/tests/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
};
use tests::{add_debugger_panel, init_test};
use tests::{init_test, init_test_workspace};
use unindent::Unindent as _;
use variable_list::{VariableContainer, VariableListEntry};

Expand Down Expand Up @@ -45,7 +45,7 @@ async fn test_evaluate_expression(executor: BackgroundExecutor, cx: &mut TestApp
.await;

let project = Project::test(fs, ["/project".as_ref()], cx).await;
let workspace = add_debugger_panel(&project, cx).await;
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);

let task = project.update(cx, |project, cx| {
Expand Down Expand Up @@ -453,11 +453,11 @@ async fn test_evaluate_expression(executor: BackgroundExecutor, cx: &mut TestApp
"Expected evaluate request to be called"
);

let shutdown_client = project.update(cx, |project, cx| {
let shutdown_session = project.update(cx, |project, cx| {
project.dap_store().update(cx, |dap_store, cx| {
dap_store.shutdown_session(&session.read(cx).id(), cx)
})
});

shutdown_client.await.unwrap();
shutdown_session.await.unwrap();
}
26 changes: 13 additions & 13 deletions crates/debugger_ui/src/tests/debugger_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::*;
use dap::requests::{Disconnect, Initialize, Launch, StackTrace};
use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
use project::{FakeFs, Project};
use tests::{add_debugger_panel, init_test};
use tests::{init_test, init_test_workspace};
use workspace::dock::Panel;

#[gpui::test]
Expand All @@ -12,7 +12,7 @@ async fn test_basic_show_debug_panel(executor: BackgroundExecutor, cx: &mut Test
let fs = FakeFs::new(executor.clone());

let project = Project::test(fs, [], cx).await;
let workspace = add_debugger_panel(&project, cx).await;
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);

let task = project.update(cx, |project, cx| {
Expand Down Expand Up @@ -98,13 +98,13 @@ async fn test_basic_show_debug_panel(executor: BackgroundExecutor, cx: &mut Test
})
.unwrap();

let shutdown_client = project.update(cx, |project, cx| {
let shutdown_session = project.update(cx, |project, cx| {
project.dap_store().update(cx, |dap_store, cx| {
dap_store.shutdown_session(&session.read(cx).id(), cx)
})
});

shutdown_client.await.unwrap();
shutdown_session.await.unwrap();

// assert we don't have a debug panel item anymore because the client shutdown
workspace
Expand All @@ -129,7 +129,7 @@ async fn test_we_can_only_have_one_panel_per_debug_thread(
let fs = FakeFs::new(executor.clone());

let project = Project::test(fs, [], cx).await;
let workspace = add_debugger_panel(&project, cx).await;
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);

let task = project.update(cx, |project, cx| {
Expand Down Expand Up @@ -246,13 +246,13 @@ async fn test_we_can_only_have_one_panel_per_debug_thread(
})
.unwrap();

let shutdown_client = project.update(cx, |project, cx| {
let shutdown_session = project.update(cx, |project, cx| {
project.dap_store().update(cx, |dap_store, cx| {
dap_store.shutdown_session(&session.read(cx).id(), cx)
})
});

shutdown_client.await.unwrap();
shutdown_session.await.unwrap();

// assert we don't have a debug panel item anymore because the client shutdown
workspace
Expand All @@ -277,7 +277,7 @@ async fn test_client_can_open_multiple_thread_panels(
let fs = FakeFs::new(executor.clone());

let project = Project::test(fs, [], cx).await;
let workspace = add_debugger_panel(&project, cx).await;
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);

let task = project.update(cx, |project, cx| {
Expand Down Expand Up @@ -394,13 +394,13 @@ async fn test_client_can_open_multiple_thread_panels(
})
.unwrap();

let shutdown_client = project.update(cx, |project, cx| {
let shutdown_session = project.update(cx, |project, cx| {
project.dap_store().update(cx, |dap_store, cx| {
dap_store.shutdown_session(&session.read(cx).id(), cx)
})
});

shutdown_client.await.unwrap();
shutdown_session.await.unwrap();

// assert we don't have a debug panel item anymore because the client shutdown
workspace
Expand All @@ -422,7 +422,7 @@ async fn test_handle_output_event(executor: BackgroundExecutor, cx: &mut TestApp
let fs = FakeFs::new(executor.clone());

let project = Project::test(fs, [], cx).await;
let workspace = add_debugger_panel(&project, cx).await;
let workspace = init_test_workspace(&project, cx).await;
let cx = &mut VisualTestContext::from_window(*workspace, cx);

let task = project.update(cx, |project, cx| {
Expand Down Expand Up @@ -553,13 +553,13 @@ async fn test_handle_output_event(executor: BackgroundExecutor, cx: &mut TestApp
})
.unwrap();

let shutdown_client = project.update(cx, |project, cx| {
let shutdown_session = project.update(cx, |project, cx| {
project.dap_store().update(cx, |dap_store, cx| {
dap_store.shutdown_session(&session.read(cx).id(), cx)
})
});

shutdown_client.await.unwrap();
shutdown_session.await.unwrap();

// assert output queue is empty
workspace
Expand Down
Loading

0 comments on commit 7ced61d

Please sign in to comment.