summaryrefslogtreecommitdiff
path: root/cli/bench/lsp.rs
diff options
context:
space:
mode:
authorKitson Kelly <me@kitsonkelly.com>2021-05-18 06:45:13 +1000
committerGitHub <noreply@github.com>2021-05-18 06:45:13 +1000
commit27e7bb090e9d771c8f3e4ddba4dd956a4a56855b (patch)
tree82cb99c1406fc94850bc3d5d66f8db40300860de /cli/bench/lsp.rs
parentaecdbba2c25d08d771a4e4fbeb62cac60015a3bd (diff)
refactor: share test harness for lsp between bench and integration (#10659)
Diffstat (limited to 'cli/bench/lsp.rs')
-rw-r--r--cli/bench/lsp.rs266
1 files changed, 16 insertions, 250 deletions
diff --git a/cli/bench/lsp.rs b/cli/bench/lsp.rs
index ed474b9ea..cabcef1db 100644
--- a/cli/bench/lsp.rs
+++ b/cli/bench/lsp.rs
@@ -1,37 +1,21 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-use deno_core::error::generic_error;
use deno_core::error::AnyError;
-use deno_core::serde::de;
use deno_core::serde::Deserialize;
-use deno_core::serde::Serialize;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::serde_json::Value;
-use lazy_static::lazy_static;
-use regex::Regex;
use std::collections::HashMap;
-use std::io::BufRead;
-use std::io::Read;
-use std::io::Write;
use std::path::Path;
-use std::process::ChildStdin;
-use std::process::ChildStdout;
-use std::process::Command;
-use std::process::Stdio;
use std::time::Duration;
-use std::time::Instant;
+use test_util::lsp::LspClient;
+use test_util::lsp::LspResponseError;
static FIXTURE_DB_TS: &str = include_str!("fixtures/db.ts");
static FIXTURE_DB_MESSAGES: &[u8] = include_bytes!("fixtures/db_messages.json");
static FIXTURE_INIT_JSON: &[u8] =
include_bytes!("fixtures/initialize_params.json");
-lazy_static! {
- static ref CONTENT_TYPE_REG: Regex =
- Regex::new(r"(?i)^content-length:\s+(\d+)").unwrap();
-}
-
#[derive(Debug, Deserialize)]
enum FixtureType {
#[serde(rename = "action")]
@@ -53,224 +37,6 @@ struct FixtureMessage {
params: Value,
}
-#[derive(Debug, Deserialize, Serialize)]
-struct LspResponseError {
- code: i32,
- message: String,
- data: Option<Value>,
-}
-
-#[derive(Debug)]
-enum LspMessage {
- Notification(String, Option<Value>),
- Request(u64, String, Option<Value>),
- Response(u64, Option<Value>, Option<LspResponseError>),
-}
-
-impl<'a> From<&'a [u8]> for LspMessage {
- fn from(s: &'a [u8]) -> Self {
- let value: Value = serde_json::from_slice(s).unwrap();
- let obj = value.as_object().unwrap();
- if obj.contains_key("id") && obj.contains_key("method") {
- let id = obj.get("id").unwrap().as_u64().unwrap();
- let method = obj.get("method").unwrap().as_str().unwrap().to_string();
- Self::Request(id, method, obj.get("params").cloned())
- } else if obj.contains_key("id") {
- let id = obj.get("id").unwrap().as_u64().unwrap();
- let maybe_error: Option<LspResponseError> = obj
- .get("error")
- .map(|v| serde_json::from_value(v.clone()).unwrap());
- Self::Response(id, obj.get("result").cloned(), maybe_error)
- } else {
- assert!(obj.contains_key("method"));
- let method = obj.get("method").unwrap().as_str().unwrap().to_string();
- Self::Notification(method, obj.get("params").cloned())
- }
- }
-}
-
-struct LspClient {
- reader: std::io::BufReader<ChildStdout>,
- child: std::process::Child,
- request_id: u64,
- start: Instant,
- writer: std::io::BufWriter<ChildStdin>,
-}
-
-fn read_message<R>(reader: &mut R) -> Result<Vec<u8>, AnyError>
-where
- R: Read + BufRead,
-{
- let mut content_length = 0_usize;
- loop {
- let mut buf = String::new();
- reader.read_line(&mut buf)?;
- if let Some(captures) = CONTENT_TYPE_REG.captures(&buf) {
- let content_length_match = captures
- .get(1)
- .ok_or_else(|| generic_error("missing capture"))?;
- content_length = content_length_match.as_str().parse::<usize>()?;
- }
- if &buf == "\r\n" {
- break;
- }
- }
-
- let mut msg_buf = vec![0_u8; content_length];
- reader.read_exact(&mut msg_buf)?;
- Ok(msg_buf)
-}
-
-impl Drop for LspClient {
- fn drop(&mut self) {
- match self.child.try_wait() {
- Ok(None) => {
- self.child.kill().unwrap();
- let _ = self.child.wait();
- }
- Ok(Some(status)) => panic!("deno lsp exited unexpectedly {}", status),
- Err(e) => panic!("pebble error: {}", e),
- }
- }
-}
-
-impl LspClient {
- fn new(deno_exe: &Path) -> Result<Self, AnyError> {
- let mut child = Command::new(deno_exe)
- .arg("lsp")
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .stderr(Stdio::null())
- .spawn()?;
-
- let stdout = child.stdout.take().unwrap();
- let reader = std::io::BufReader::new(stdout);
-
- let stdin = child.stdin.take().unwrap();
- let writer = std::io::BufWriter::new(stdin);
-
- Ok(Self {
- child,
- reader,
- request_id: 1,
- start: Instant::now(),
- writer,
- })
- }
-
- fn duration(&self) -> Duration {
- self.start.elapsed()
- }
-
- fn read(&mut self) -> Result<LspMessage, AnyError> {
- let msg_buf = read_message(&mut self.reader)?;
- let msg = LspMessage::from(msg_buf.as_slice());
- Ok(msg)
- }
-
- fn read_notification<R>(&mut self) -> Result<(String, Option<R>), AnyError>
- where
- R: de::DeserializeOwned,
- {
- loop {
- if let LspMessage::Notification(method, maybe_params) = self.read()? {
- if let Some(p) = maybe_params {
- let params = serde_json::from_value(p)?;
- return Ok((method, Some(params)));
- } else {
- return Ok((method, None));
- }
- }
- }
- }
-
- #[allow(unused)]
- fn read_request<R>(&mut self) -> Result<(u64, String, Option<R>), AnyError>
- where
- R: de::DeserializeOwned,
- {
- loop {
- if let LspMessage::Request(id, method, maybe_params) = self.read()? {
- if let Some(p) = maybe_params {
- let params = serde_json::from_value(p)?;
- return Ok((id, method, Some(params)));
- } else {
- return Ok((id, method, None));
- }
- }
- }
- }
-
- fn write(&mut self, value: Value) -> Result<(), AnyError> {
- let value_str = value.to_string();
- let msg = format!(
- "Content-Length: {}\r\n\r\n{}",
- value_str.as_bytes().len(),
- value_str
- );
- self.writer.write_all(msg.as_bytes())?;
- self.writer.flush()?;
- Ok(())
- }
-
- fn write_request<S, V>(
- &mut self,
- method: S,
- params: V,
- ) -> Result<(Option<Value>, Option<LspResponseError>), AnyError>
- where
- S: AsRef<str>,
- V: Serialize,
- {
- let value = json!({
- "jsonrpc": "2.0",
- "id": self.request_id,
- "method": method.as_ref(),
- "params": params,
- });
- self.write(value)?;
-
- loop {
- if let LspMessage::Response(id, result, error) = self.read()? {
- assert_eq!(id, self.request_id);
- self.request_id += 1;
- return Ok((result, error));
- }
- }
- }
-
- #[allow(unused)]
- fn write_response<V>(&mut self, id: u64, result: V) -> Result<(), AnyError>
- where
- V: Serialize,
- {
- let value = json!({
- "jsonrpc": "2.0",
- "id": id,
- "result": result
- });
- self.write(value)
- }
-
- fn write_notification<S, V>(
- &mut self,
- method: S,
- params: V,
- ) -> Result<(), AnyError>
- where
- S: AsRef<str>,
- V: Serialize,
- {
- let value = json!({
- "jsonrpc": "2.0",
- "method": method.as_ref(),
- "params": params,
- });
- self.write(value)?;
- Ok(())
- }
-}
-
/// A benchmark that opens a 8000+ line TypeScript document, adds a function to
/// the end of the document and does a level of hovering and gets quick fix
/// code actions.
@@ -320,19 +86,29 @@ fn bench_big_file_edits(deno_exe: &Path) -> Result<Duration, AnyError> {
for msg in messages {
match msg.fixture_type {
FixtureType::Action => {
- client.write_request("textDocument/codeAction", msg.params)?;
+ client.write_request::<_, _, Value>(
+ "textDocument/codeAction",
+ msg.params,
+ )?;
}
FixtureType::Change => {
client.write_notification("textDocument/didChange", msg.params)?;
}
FixtureType::Completion => {
- client.write_request("textDocument/completion", msg.params)?;
+ client.write_request::<_, _, Value>(
+ "textDocument/completion",
+ msg.params,
+ )?;
}
FixtureType::Highlight => {
- client.write_request("textDocument/documentHighlight", msg.params)?;
+ client.write_request::<_, _, Value>(
+ "textDocument/documentHighlight",
+ msg.params,
+ )?;
}
FixtureType::Hover => {
- client.write_request("textDocument/hover", msg.params)?;
+ client
+ .write_request::<_, _, Value>("textDocument/hover", msg.params)?;
}
}
}
@@ -427,13 +203,3 @@ pub(crate) fn benchmarks(
Ok(exec_times)
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn test_read_message() {
- let msg = b"content-length: 11\r\n\r\nhello world";
- let reader = std::io::Cursor::new(msg);
- assert_eq!(read_message(reader).unwrap(), b"hello world");
- }
-}