Skip to content

Commit e5d49fb

Browse files
psteinroeclaude
andcommitted
fix: address PR review feedback
- Make REQUEST_TIMEOUT configurable via open_with_timeout() instead of #[cfg(test)] hack - Check that `leaks` tool is installed before running probes - Replace custom json_escape with serde_json::json! macro - Use 2ms timeout in leak-check xtask (no point waiting on a dropped stream) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8f1044a commit e5d49fb

2 files changed

Lines changed: 45 additions & 45 deletions

File tree

crates/pgls_cli/src/service/mod.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,10 @@ pub struct SocketTransport {
9999
runtime: Runtime,
100100
write_send: Sender<(Vec<u8>, bool)>,
101101
pending_requests: PendingRequests,
102+
request_timeout: Duration,
102103
}
103104

104-
#[cfg(not(test))]
105-
const REQUEST_TIMEOUT: Duration = Duration::from_secs(15);
106-
#[cfg(test)]
107-
const REQUEST_TIMEOUT: Duration = Duration::from_millis(50);
105+
const DEFAULT_REQUEST_TIMEOUT: Duration = Duration::from_secs(15);
108106

109107
/// Stores a handle to the map of pending requests, and clears the map
110108
/// automatically when the handle is dropped
@@ -137,6 +135,19 @@ impl Drop for PendingRequests {
137135

138136
impl SocketTransport {
139137
pub fn open<R, W>(runtime: Runtime, socket_read: R, socket_write: W) -> Self
138+
where
139+
R: AsyncRead + Unpin + Send + 'static,
140+
W: AsyncWrite + Unpin + Send + 'static,
141+
{
142+
Self::open_with_timeout(runtime, socket_read, socket_write, DEFAULT_REQUEST_TIMEOUT)
143+
}
144+
145+
pub fn open_with_timeout<R, W>(
146+
runtime: Runtime,
147+
socket_read: R,
148+
socket_write: W,
149+
request_timeout: Duration,
150+
) -> Self
140151
where
141152
R: AsyncRead + Unpin + Send + 'static,
142153
W: AsyncWrite + Unpin + Send + 'static,
@@ -177,6 +188,7 @@ impl SocketTransport {
177188
runtime,
178189
write_send,
179190
pending_requests: pending_requests_2,
191+
request_timeout,
180192
}
181193
}
182194
}
@@ -227,7 +239,7 @@ impl WorkspaceTransport for SocketTransport {
227239
Err(_) => Err(TransportError::ChannelClosed),
228240
}
229241
}
230-
_ = sleep(REQUEST_TIMEOUT) => {
242+
_ = sleep(self.request_timeout) => {
231243
Err(TransportError::Timeout)
232244
}
233245
}
@@ -490,6 +502,7 @@ impl FromStr for TransportHeader {
490502
#[cfg(test)]
491503
mod tests {
492504
use std::fmt;
505+
use std::time::Duration;
493506

494507
use pgls_workspace::TransportError;
495508
use pgls_workspace::workspace::{TransportRequest, WorkspaceTransport};
@@ -523,7 +536,7 @@ mod tests {
523536
let (stream, peer) = duplex(1024);
524537
drop(peer);
525538
let (read, write) = split(stream);
526-
SocketTransport::open(runtime, read, write)
539+
SocketTransport::open_with_timeout(runtime, read, write, Duration::from_millis(50))
527540
}
528541

529542
#[test]

xtask/src/leak_check.rs

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use anyhow::{bail, Context};
88
use pgls_cli::SocketTransport;
99
#[cfg(unix)]
1010
use pgls_workspace::workspace::{TransportRequest, WorkspaceTransport};
11-
#[cfg(unix)]
1211
use serde_json::Value;
1312
#[cfg(unix)]
1413
use tokio::net::UnixStream;
@@ -25,6 +24,10 @@ impl flags::LeakCheck {
2524
bail!("`xtask leak-check` is currently implemented only for macOS (`leaks` tool).");
2625
}
2726

27+
if Command::new("leaks").arg("--help").output().is_err() {
28+
bail!("`leaks` not found — install Xcode Command Line Tools (`xcode-select --install`)");
29+
}
30+
2831
let iterations = self.iterations.unwrap_or(DEFAULT_ITERATIONS);
2932
let pause = Duration::from_millis(self.pause_ms.unwrap_or(DEFAULT_PAUSE_MS));
3033
let probe = self.probe.unwrap_or_else(|| "lsp".to_string());
@@ -126,7 +129,8 @@ fn run_cli_timeout_probe(iterations: usize) -> anyhow::Result<()> {
126129
drop(stream_b);
127130

128131
let (read, write) = stream_a.into_split();
129-
let transport = SocketTransport::open(runtime, read, write);
132+
let transport =
133+
SocketTransport::open_with_timeout(runtime, read, write, Duration::from_millis(2));
130134

131135
let mut channel_closed = 0usize;
132136
let mut timed_out = 0usize;
@@ -256,38 +260,32 @@ fn wait_for_socket(pid: u32) -> anyhow::Result<()> {
256260
fn run_lsp_churn(stdin: &mut ChildStdin, iterations: usize, pause: Duration) -> anyhow::Result<()> {
257261
let uri = "file:///tmp/pgls-leak-check.sql";
258262

259-
send_lsp_message(
263+
send_lsp_json(
260264
stdin,
261-
r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{},"rootUri":null}}"#,
265+
serde_json::json!({"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{},"rootUri":null}}),
262266
)?;
263-
send_lsp_message(
267+
send_lsp_json(
264268
stdin,
265-
r#"{"jsonrpc":"2.0","method":"initialized","params":{}}"#,
269+
serde_json::json!({"jsonrpc":"2.0","method":"initialized","params":{}}),
266270
)?;
267271

268272
for i in 0..iterations {
269-
let open_text = json_escape(&format!("select {i} as value;"));
270-
let changed_text = json_escape(&format!("select {i} as value, {} as extra;", i + 1));
273+
let open_text = format!("select {i} as value;");
274+
let changed_text = format!("select {i} as value, {} as extra;", i + 1);
271275

272-
send_lsp_message(
276+
send_lsp_json(
273277
stdin,
274-
&format!(
275-
r#"{{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{{"textDocument":{{"uri":"{uri}","languageId":"sql","version":1,"text":"{open_text}"}}}}}}"#
276-
),
278+
serde_json::json!({"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":uri,"languageId":"sql","version":1,"text":open_text}}}),
277279
)?;
278280

279-
send_lsp_message(
281+
send_lsp_json(
280282
stdin,
281-
&format!(
282-
r#"{{"jsonrpc":"2.0","method":"textDocument/didChange","params":{{"textDocument":{{"uri":"{uri}","version":2}},"contentChanges":[{{"text":"{changed_text}"}}]}}}}"#
283-
),
283+
serde_json::json!({"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":uri,"version":2},"contentChanges":[{"text":changed_text}]}}),
284284
)?;
285285

286-
send_lsp_message(
286+
send_lsp_json(
287287
stdin,
288-
&format!(
289-
r#"{{"jsonrpc":"2.0","method":"textDocument/didClose","params":{{"textDocument":{{"uri":"{uri}"}}}}}}"#
290-
),
288+
serde_json::json!({"jsonrpc":"2.0","method":"textDocument/didClose","params":{"textDocument":{"uri":uri}}}),
291289
)?;
292290

293291
thread::sleep(pause);
@@ -297,15 +295,19 @@ fn run_lsp_churn(stdin: &mut ChildStdin, iterations: usize, pause: Duration) ->
297295
}
298296

299297
fn send_shutdown_and_exit(stdin: &mut ChildStdin) -> anyhow::Result<()> {
300-
send_lsp_message(
298+
send_lsp_json(
299+
stdin,
300+
serde_json::json!({"jsonrpc":"2.0","id":2,"method":"shutdown","params":null}),
301+
)?;
302+
send_lsp_json(
301303
stdin,
302-
r#"{"jsonrpc":"2.0","id":2,"method":"shutdown","params":null}"#,
304+
serde_json::json!({"jsonrpc":"2.0","method":"exit","params":null}),
303305
)?;
304-
send_lsp_message(stdin, r#"{"jsonrpc":"2.0","method":"exit","params":null}"#)?;
305306
Ok(())
306307
}
307308

308-
fn send_lsp_message(stdin: &mut ChildStdin, payload: &str) -> anyhow::Result<()> {
309+
fn send_lsp_json(stdin: &mut ChildStdin, value: Value) -> anyhow::Result<()> {
310+
let payload = serde_json::to_string(&value).context("failed to serialize LSP message")?;
309311
let header = format!("Content-Length: {}\r\n\r\n", payload.len());
310312
stdin
311313
.write_all(header.as_bytes())
@@ -316,21 +318,6 @@ fn send_lsp_message(stdin: &mut ChildStdin, payload: &str) -> anyhow::Result<()>
316318
stdin.flush().context("failed to flush LSP payload")
317319
}
318320

319-
fn json_escape(s: &str) -> String {
320-
let mut out = String::with_capacity(s.len());
321-
for c in s.chars() {
322-
match c {
323-
'"' => out.push_str("\\\""),
324-
'\\' => out.push_str("\\\\"),
325-
'\n' => out.push_str("\\n"),
326-
'\r' => out.push_str("\\r"),
327-
'\t' => out.push_str("\\t"),
328-
_ => out.push(c),
329-
}
330-
}
331-
out
332-
}
333-
334321
fn run_leaks(pid: u32) -> anyhow::Result<String> {
335322
let output = Command::new("leaks")
336323
.arg(pid.to_string())

0 commit comments

Comments
 (0)