diff options
author | Kitson Kelly <me@kitsonkelly.com> | 2021-05-18 06:45:13 +1000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-18 06:45:13 +1000 |
commit | 27e7bb090e9d771c8f3e4ddba4dd956a4a56855b (patch) | |
tree | 82cb99c1406fc94850bc3d5d66f8db40300860de /cli/bench/lsp.rs | |
parent | aecdbba2c25d08d771a4e4fbeb62cac60015a3bd (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.rs | 266 |
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"); - } -} |