From f08170dbeb3490c5f332c87e9a1e27ffa5989dd3 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 14 Jan 2019 21:10:35 +0100 Subject: [PATCH 01/16] Allow to wait for diagnostics message --- tests/support/client.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/support/client.rs b/tests/support/client.rs index 0542a276745..298f6a7d073 100644 --- a/tests/support/client.rs +++ b/tests/support/client.rs @@ -205,6 +205,18 @@ impl RlsHandle { }); } + /// Blocks until a "textDocument/publishDiagnostics" message is received. + pub fn wait_for_diagnostics(&mut self) -> lsp_types::PublishDiagnosticsParams { + use lsp_types::notification::Notification; + + let msg = self.wait_for_message(|msg| { + msg["method"] == lsp_types::notification::PublishDiagnostics::METHOD + }); + + lsp_types::PublishDiagnosticsParams::deserialize(&msg["params"]) + .unwrap_or_else(|_| panic!("Can't deserialize params: {:?}", msg)) + } + /// Requests the RLS to shut down and waits (with a timeout) until the child /// process is terminated. pub fn shutdown(mut self) { From 25bd3cfc490bca902dc9ea3402a4d91520eec3b2 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 14 Jan 2019 21:11:04 +0100 Subject: [PATCH 02/16] Translate infer_bin --- tests/client.rs | 70 +++++++++++++----- tests/tests.rs | 184 +----------------------------------------------- 2 files changed, 54 insertions(+), 200 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index 4d42e7d7eed..261ed684f44 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -1,8 +1,59 @@ -use self::support::project_builder::project; +use std::path::Path; + +use crate::support::basic_bin_manifest; +use crate::support::project_builder::project; + +use lsp_types::{*, request::*}; #[allow(dead_code)] mod support; +fn initialize_params(root_path: &Path) -> InitializeParams { + lsp_types::InitializeParams { + process_id: None, + root_uri: None, + root_path: Some(root_path.display().to_string()), + initialization_options: None, + capabilities: lsp_types::ClientCapabilities { + workspace: None, + text_document: None, + experimental: None, + }, + trace: None, + workspace_folders: None, + } +} + +#[test] +fn client_test_infer_bin() { + let p = project("simple_workspace") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + struct UnusedBin; + fn main() { + println!("Hello world!"); + } + "#, + ) + .build(); + + let root_path = p.root(); + let mut rls = p.spawn_rls_async(); + + rls.request::(0, initialize_params(root_path)); + + let diag = rls.wait_for_diagnostics(); + assert!(diag.diagnostics[0].message.contains("struct is never constructed: `UnusedBin`")); + + rls.wait_for_indexing(); + assert!(rls.messages().iter().filter(|msg| msg["method"] != "window/progress").count() > 1); + + rls.shutdown(); +} + +/// Test includes window/progress regression testing #[test] fn client_test_simple_workspace() { let p = project("simple_workspace") @@ -84,22 +135,7 @@ fn client_test_simple_workspace() { let root_path = p.root(); let mut rls = p.spawn_rls_async(); - rls.request::( - 0, - lsp_types::InitializeParams { - process_id: None, - root_uri: None, - root_path: Some(root_path.display().to_string()), - initialization_options: None, - capabilities: lsp_types::ClientCapabilities { - workspace: None, - text_document: None, - experimental: None, - }, - trace: None, - workspace_folders: None, - }, - ); + rls.request::(0, initialize_params(root_path)); rls.wait_for_indexing(); diff --git a/tests/tests.rs b/tests/tests.rs index fe539491c5f..86ebb7738eb 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -8,193 +8,11 @@ use rls::lsp_data::request::Request as _; use self::support::harness::compare_json; use self::support::project_builder::{project, ProjectBuilder}; -use self::support::{basic_bin_manifest, fixtures_dir, rls_timeout, RlsStdout}; +use self::support::{fixtures_dir, rls_timeout, RlsStdout}; #[allow(dead_code)] mod support; -#[test] -fn cmd_test_infer_bin() { - let p = project("simple_workspace") - .file("Cargo.toml", &basic_bin_manifest("foo")) - .file( - "src/main.rs", - r#" - struct UnusedBin; - fn main() { - println!("Hello world!"); - } - "#, - ) - .build(); - - let root_path = p.root(); - let mut rls = p.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": {} - })), - ) - .unwrap(); - - let json: Vec<_> = rls - .wait_until_done_indexing(rls_timeout()) - .to_json_messages() - .filter(|json| json["method"] != "window/progress") - .collect(); - - assert!(json.len() > 1); - - assert!(json[0]["result"]["capabilities"].is_object()); - - assert_eq!(json[1]["method"], "textDocument/publishDiagnostics"); - assert_eq!(json[1]["params"]["diagnostics"][0]["code"], "dead_code"); - - rls.shutdown(rls_timeout()); -} - -/// Test includes window/progress regression testing -#[test] -fn cmd_test_simple_workspace() { - let p = project("simple_workspace") - .file( - "Cargo.toml", - r#" - [workspace] - members = [ - "member_lib", - "member_bin", - ] - "#, - ) - .file( - "Cargo.lock", - r#" - [root] - name = "member_lib" - version = "0.1.0" - - [[package]] - name = "member_bin" - version = "0.1.0" - dependencies = [ - "member_lib 0.1.0", - ] - "#, - ) - .file( - "member_bin/Cargo.toml", - r#" - [package] - name = "member_bin" - version = "0.1.0" - authors = ["Igor Matuszewski "] - - [dependencies] - member_lib = { path = "../member_lib" } - "#, - ) - .file( - "member_bin/src/main.rs", - r#" - extern crate member_lib; - - fn main() { - let a = member_lib::MemberLibStruct; - } - "#, - ) - .file( - "member_lib/Cargo.toml", - r#" - [package] - name = "member_lib" - version = "0.1.0" - authors = ["Igor Matuszewski "] - - [dependencies] - "#, - ) - .file( - "member_lib/src/lib.rs", - r#" - pub struct MemberLibStruct; - - struct Unused; - - #[cfg(test)] - mod tests { - #[test] - fn it_works() { - } - } - "#, - ) - .build(); - - let root_path = p.root(); - let mut rls = p.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": {} - })), - ) - .unwrap(); - - let json: Vec<_> = rls - .wait_until_done_indexing(rls_timeout()) - .to_json_messages() - .collect(); - assert!(json.len() >= 11); - - assert!(json[0]["result"]["capabilities"].is_object()); - - assert_eq!(json[1]["method"], "window/progress"); - assert_eq!(json[1]["params"]["title"], "Building"); - assert_eq!(json[1]["params"].get("message"), None); - - // order of member_lib/member_bin is undefined - for json in &json[2..6] { - assert_eq!(json["method"], "window/progress"); - assert_eq!(json["params"]["title"], "Building"); - assert!(json["params"]["message"] - .as_str() - .unwrap() - .starts_with("member_")); - } - - assert_eq!(json[6]["method"], "window/progress"); - assert_eq!(json[6]["params"]["done"], true); - assert_eq!(json[6]["params"]["title"], "Building"); - - assert_eq!(json[7]["method"], "window/progress"); - assert_eq!(json[7]["params"]["title"], "Indexing"); - - assert_eq!(json[8]["method"], "textDocument/publishDiagnostics"); - - assert_eq!(json[9]["method"], "textDocument/publishDiagnostics"); - - assert_eq!(json[10]["method"], "window/progress"); - assert_eq!(json[10]["params"]["done"], true); - assert_eq!(json[10]["params"]["title"], "Indexing"); - - let json = rls - .shutdown(rls_timeout()) - .to_json_messages() - .nth(11) - .expect("No shutdown response received"); - - assert_eq!(json["id"], 99999); -} - #[test] fn cmd_changing_workspace_lib_retains_bin_diagnostics() { let p = project("simple_workspace") From 6f62c32e52e2930c7fbf8a317a119e4b9af4f790 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 14 Jan 2019 23:03:03 +0100 Subject: [PATCH 03/16] Translate cmd_changing_workspace_lib_retains_diagnostics --- tests/client.rs | 143 ++++++++++++++++++++++++++++- tests/support/client.rs | 25 ++++- tests/tests.rs | 196 ---------------------------------------- 3 files changed, 161 insertions(+), 203 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index 261ed684f44..51977dd9d50 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -1,10 +1,11 @@ use std::path::Path; +use futures::future::Future; +use lsp_types::{*, request::*, notification::*}; + use crate::support::basic_bin_manifest; use crate::support::project_builder::project; -use lsp_types::{*, request::*}; - #[allow(dead_code)] mod support; @@ -150,3 +151,141 @@ fn client_test_simple_workspace() { rls.shutdown(); } + +#[test] +fn client_changing_workspace_lib_retains_diagnostics() { + let p = project("simple_workspace") + .file( + "Cargo.toml", + r#" + [workspace] + members = [ + "library", + "binary", + ] + "#, + ) + .file( + "library/Cargo.toml", + r#" + [package] + name = "library" + version = "0.1.0" + authors = ["Example "] + "#, + ) + .file( + "library/src/lib.rs", + r#" + pub fn fetch_u32() -> u32 { + let unused = (); + 42 + } + #[cfg(test)] + mod test { + #[test] + fn my_test() { + let test_val: u32 = super::fetch_u32(); + } + } + "#, + ) + .file( + "binary/Cargo.toml", + r#" + [package] + name = "binary" + version = "0.1.0" + authors = ["Igor Matuszewski "] + + [dependencies] + library = { path = "../library" } + "#, + ) + .file( + "binary/src/main.rs", + r#" + extern crate library; + + fn main() { + let val: u32 = library::fetch_u32(); + } + "#, + ) + .build(); + + let root_path = p.root(); + let mut rls = p.spawn_rls_async(); + + rls.request::(0, initialize_params(root_path)); + + let lib = rls.future_diagnostics("library/src/lib.rs"); + let bin = rls.future_diagnostics("binary/src/main.rs"); + let (lib, bin) = rls.runtime().block_on(lib.join(bin)).unwrap(); + + assert!(lib.diagnostics.iter().any(|m| m.message.contains("unused variable: `test_val`"))); + assert!(lib.diagnostics.iter().any(|m| m.message.contains("unused variable: `unused`"))); + assert!(bin.diagnostics[0].message.contains("unused variable: `val`")); + + rls.notify::(DidChangeTextDocumentParams { + content_changes: vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 1, + character: 38, + }, + end: Position { + line: 1, + character: 41 + } + }), + range_length: Some(3), + text: "u64".to_string(), + }], + text_document: VersionedTextDocumentIdentifier { + uri: Url::from_file_path(p.root().join("library/src/lib.rs")).unwrap(), + version: Some(0), + } + }); + + let lib = rls.future_diagnostics("library/src/lib.rs"); + let bin = rls.future_diagnostics("binary/src/main.rs"); + let (lib, bin) = rls.runtime().block_on(lib.join(bin)).unwrap(); + + // lib unit tests have compile errors + assert!(lib.diagnostics.iter().any(|m| m.message.contains("unused variable: `unused`"))); + assert!(lib.diagnostics.iter().any(|m| m.message.contains("expected u32, found u64"))); + // bin depending on lib picks up type mismatch + assert!(bin.diagnostics[0].message.contains("mismatched types\n\nexpected u32, found u64")); + + rls.notify::(DidChangeTextDocumentParams { + content_changes: vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 1, + character: 38, + }, + end: Position { + line: 1, + character: 41 + } + }), + range_length: Some(3), + text: "u32".to_string(), + }], + text_document: VersionedTextDocumentIdentifier { + uri: Url::from_file_path(p.root().join("library/src/lib.rs")).unwrap(), + version: Some(1), + } + }); + + let lib = rls.future_diagnostics("library/src/lib.rs"); + let bin = rls.future_diagnostics("binary/src/main.rs"); + let (lib, bin) = rls.runtime().block_on(lib.join(bin)).unwrap(); + + assert!(lib.diagnostics.iter().any(|m| m.message.contains("unused variable: `test_val`"))); + assert!(lib.diagnostics.iter().any(|m| m.message.contains("unused variable: `unused`"))); + assert!(bin.diagnostics[0].message.contains("unused variable: `val`")); + + rls.shutdown(); +} diff --git a/tests/support/client.rs b/tests/support/client.rs index 298f6a7d073..b2fa81194a5 100644 --- a/tests/support/client.rs +++ b/tests/support/client.rs @@ -19,6 +19,8 @@ use futures::stream::Stream; use futures::unsync::oneshot; use futures::Future; use lsp_codec::{LspDecoder, LspEncoder}; +use lsp_types::PublishDiagnosticsParams; +use lsp_types::notification::{Notification, PublishDiagnostics}; use serde::Deserialize; use serde_json::{json, Value}; use tokio::codec::{FramedRead, FramedWrite}; @@ -133,6 +135,10 @@ impl RlsHandle { self.messages.borrow() } + pub fn runtime(&mut self) -> &mut Runtime { + &mut self.runtime + } + /// Send a request to the RLS and block until we receive the message. /// Note that between sending and receiving the response *another* messages /// can be received. @@ -192,6 +198,19 @@ impl RlsHandle { rx } + // Returns a future diagnostic message for a given file path suffix. + #[rustfmt::skip] + pub fn future_diagnostics( + &mut self, + path: impl AsRef + 'static, + ) -> impl Future { + self.future_msg(move |msg| + msg["method"] == PublishDiagnostics::METHOD && + msg["params"]["uri"].as_str().unwrap().ends_with(path.as_ref()) + ) + .and_then(|msg| Ok(PublishDiagnosticsParams::deserialize(&msg["params"]).unwrap())) + } + /// Blocks until a message, for which predicate `f` returns true, is received. pub fn wait_for_message(&mut self, f: impl Fn(&Value) -> bool + 'static) -> Value { let fut = self.future_msg(f); @@ -207,11 +226,7 @@ impl RlsHandle { /// Blocks until a "textDocument/publishDiagnostics" message is received. pub fn wait_for_diagnostics(&mut self) -> lsp_types::PublishDiagnosticsParams { - use lsp_types::notification::Notification; - - let msg = self.wait_for_message(|msg| { - msg["method"] == lsp_types::notification::PublishDiagnostics::METHOD - }); + let msg = self.wait_for_message(|msg| msg["method"] == PublishDiagnostics::METHOD); lsp_types::PublishDiagnosticsParams::deserialize(&msg["params"]) .unwrap_or_else(|_| panic!("Can't deserialize params: {:?}", msg)) diff --git a/tests/tests.rs b/tests/tests.rs index 86ebb7738eb..d8bfe416c4f 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -13,202 +13,6 @@ use self::support::{fixtures_dir, rls_timeout, RlsStdout}; #[allow(dead_code)] mod support; -#[test] -fn cmd_changing_workspace_lib_retains_bin_diagnostics() { - let p = project("simple_workspace") - .file( - "Cargo.toml", - r#" - [workspace] - members = [ - "library", - "binary", - ] - "#, - ) - .file( - "library/Cargo.toml", - r#" - [package] - name = "library" - version = "0.1.0" - authors = ["Example "] - "#, - ) - .file( - "library/src/lib.rs", - r#" - pub fn fetch_u32() -> u32 { - let unused = (); - 42 - } - #[cfg(test)] - mod test { - #[test] - fn my_test() { - let test_val: u32 = super::fetch_u32(); - } - } - "#, - ) - .file( - "binary/Cargo.toml", - r#" - [package] - name = "binary" - version = "0.1.0" - authors = ["Igor Matuszewski "] - - [dependencies] - library = { path = "../library" } - "#, - ) - .file( - "binary/src/main.rs", - r#" - extern crate library; - - fn main() { - let val: u32 = library::fetch_u32(); - } - "#, - ) - .build(); - - let root_path = p.root(); - let mut rls = p.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": {} - })), - ) - .unwrap(); - - let to_publish_messages = |stdout: &RlsStdout| { - stdout - .to_json_messages() - .filter(|json| json["method"] == "textDocument/publishDiagnostics") - }; - let rfind_diagnostics_with_uri = |stdout, uri_end| { - to_publish_messages(stdout) - .rfind(|json| json["params"]["uri"].as_str().unwrap().ends_with(uri_end)) - .unwrap() - }; - - let stdout = rls.wait_until_done_indexing(rls_timeout()); - - let lib_diagnostic = rfind_diagnostics_with_uri(&stdout, "library/src/lib.rs"); - assert_eq!( - lib_diagnostic["params"]["diagnostics"][0]["code"], - "unused_variables" - ); - let bin_diagnostic = rfind_diagnostics_with_uri(&stdout, "binary/src/main.rs"); - assert_eq!( - bin_diagnostic["params"]["diagnostics"][0]["code"], - "unused_variables" - ); - - rls.notify( - "textDocument/didChange", - Some(json!({ - "contentChanges": [ - { - "range": { - "start": { - "line": 1, - "character": 38, - }, - "end": { - "line": 1, - "character": 41, - } - }, - "rangeLength": 3, - "text": "u64" - } - ], - "textDocument": { - "uri": format!("file://{}/library/src/lib.rs", root_path.display()), - "version": 0 - } - })), - ) - .unwrap(); - - let stdout = rls.wait_until_done_indexing_n(2, rls_timeout()); - - // lib unit tests have compile errors - let lib_diagnostic = rfind_diagnostics_with_uri(&stdout, "library/src/lib.rs"); - let error_diagnostic = lib_diagnostic["params"]["diagnostics"] - .as_array() - .unwrap() - .iter() - .find(|d| d["code"] == "E0308") - .expect("expected lib error diagnostic"); - assert!(error_diagnostic["message"] - .as_str() - .unwrap() - .contains("expected u32, found u64")); - - // bin depending on lib picks up type mismatch - let bin_diagnostic = rfind_diagnostics_with_uri(&stdout, "binary/src/main.rs"); - let error_diagnostic = bin_diagnostic["params"]["diagnostics"] - .as_array() - .unwrap() - .iter() - .find(|d| d["code"] == "E0308") - .expect("expected bin error diagnostic"); - assert!(error_diagnostic["message"] - .as_str() - .unwrap() - .contains("expected u32, found u64")); - - rls.notify( - "textDocument/didChange", - Some(json!({ - "contentChanges": [ - { - "range": { - "start": { - "line": 1, - "character": 38, - }, - "end": { - "line": 1, - "character": 41, - } - }, - "rangeLength": 3, - "text": "u32" - } - ], - "textDocument": { - "uri": format!("file://{}/library/src/lib.rs", root_path.display()), - "version": 1 - } - })), - ) - .unwrap(); - - let stdout = rls.wait_until_done_indexing_n(3, rls_timeout()); - let lib_diagnostic = rfind_diagnostics_with_uri(&stdout, "library/src/lib.rs"); - assert_eq!( - lib_diagnostic["params"]["diagnostics"][0]["code"], - "unused_variables" - ); - let bin_diagnostic = rfind_diagnostics_with_uri(&stdout, "binary/src/main.rs"); - assert_eq!( - bin_diagnostic["params"]["diagnostics"][0]["code"], - "unused_variables" - ); - - rls.shutdown(rls_timeout()); -} - #[test] fn cmd_implicit_workspace_pick_up_lib_changes() { let p = project("simple_workspace") From 2d7a97b639920b46c7aa9be75219ebc0deaa074f Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 14 Jan 2019 23:16:46 +0100 Subject: [PATCH 04/16] Translate implicit_workspace_pick_up_lib_changes --- tests/client.rs | 105 ++++++++++++++++++++++++++++++++ tests/tests.rs | 155 +----------------------------------------------- 2 files changed, 106 insertions(+), 154 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index 51977dd9d50..b94854e5785 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -289,3 +289,108 @@ fn client_changing_workspace_lib_retains_diagnostics() { rls.shutdown(); } + +#[test] +fn client_implicit_workspace_pick_up_lib_changes() { + let p = project("simple_workspace") + .file( + "Cargo.toml", + r#" + [package] + name = "binary" + version = "0.1.0" + authors = ["Example "] + + [dependencies] + inner = { path = "inner" } + "#, + ) + .file( + "src/main.rs", + r#" + extern crate inner; + + fn main() { + let val = inner::foo(); + } + "#, + ) + .file( + "inner/Cargo.toml", + r#" + [package] + name = "inner" + version = "0.1.0" + authors = ["Example "] + "#, + ) + .file( + "inner/src/lib.rs", + r#" + pub fn foo() -> u32 { 42 } + "#, + ) + .build(); + + let root_path = p.root(); + let mut rls = p.spawn_rls_async(); + + rls.request::(0, initialize_params(root_path)); + + let bin = rls.future_diagnostics("src/main.rs"); + let bin = rls.runtime().block_on(bin).unwrap(); + assert!(bin.diagnostics[0].message.contains("unused variable: `val`")); + + rls.notify::(DidChangeTextDocumentParams { + content_changes: vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 1, + character: 23, + }, + end: Position { + line: 1, + character: 26 + } + }), + range_length: Some(3), + text: "bar".to_string(), + }], + text_document: VersionedTextDocumentIdentifier { + uri: Url::from_file_path(p.root().join("inner/src/lib.rs")).unwrap(), + version: Some(0), + } + }); + + // bin depending on lib picks up type mismatch + let bin = rls.future_diagnostics("src/main.rs"); + let bin = rls.runtime().block_on(bin).unwrap(); + assert!(bin.diagnostics[0].message.contains("cannot find function `foo`")); + + rls.notify::(DidChangeTextDocumentParams { + content_changes: vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 1, + character: 23, + }, + end: Position { + line: 1, + character: 26 + } + }), + range_length: Some(3), + text: "foo".to_string(), + }], + text_document: VersionedTextDocumentIdentifier { + uri: Url::from_file_path(p.root().join("inner/src/lib.rs")).unwrap(), + version: Some(1), + } + }); + + let bin = rls.future_diagnostics("src/main.rs"); + let bin = rls.runtime().block_on(bin).unwrap(); + assert!(bin.diagnostics[0].message.contains("unused variable: `val`")); + + rls.shutdown(); +} diff --git a/tests/tests.rs b/tests/tests.rs index d8bfe416c4f..3c420825de8 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -8,164 +8,11 @@ use rls::lsp_data::request::Request as _; use self::support::harness::compare_json; use self::support::project_builder::{project, ProjectBuilder}; -use self::support::{fixtures_dir, rls_timeout, RlsStdout}; +use self::support::{fixtures_dir, rls_timeout}; #[allow(dead_code)] mod support; -#[test] -fn cmd_implicit_workspace_pick_up_lib_changes() { - let p = project("simple_workspace") - .file( - "Cargo.toml", - r#" - [package] - name = "binary" - version = "0.1.0" - authors = ["Example "] - - [dependencies] - inner = { path = "inner" } - "#, - ) - .file( - "src/main.rs", - r#" - extern crate inner; - - fn main() { - let val = inner::foo(); - } - "#, - ) - .file( - "inner/Cargo.toml", - r#" - [package] - name = "inner" - version = "0.1.0" - authors = ["Example "] - "#, - ) - .file( - "inner/src/lib.rs", - r#" - pub fn foo() -> u32 { 42 } - "#, - ) - .build(); - - let root_path = p.root(); - let mut rls = p.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": {} - })), - ) - .unwrap(); - - let to_publish_messages = |stdout: &RlsStdout| { - stdout - .to_json_messages() - .filter(|json| json["method"] == "textDocument/publishDiagnostics") - }; - let rfind_diagnostics_with_uri = |stdout, uri_end| { - to_publish_messages(stdout) - .rfind(|json| json["params"]["uri"].as_str().unwrap().ends_with(uri_end)) - .unwrap() - }; - - let stdout = rls.wait_until_done_indexing(rls_timeout()); - - let bin_diagnostic = rfind_diagnostics_with_uri(&stdout, "src/main.rs"); - assert_eq!( - bin_diagnostic["params"]["diagnostics"][0]["code"], - "unused_variables" - ); - - rls.notify( - "textDocument/didChange", - Some(json!({ - "contentChanges": [ - { - "range": { - "start": { - "line": 1, - "character": 23, - }, - "end": { - "line": 1, - "character": 26, - } - }, - "rangeLength": 3, - "text": "bar" - } - ], - "textDocument": { - "uri": format!("file://{}/inner/src/lib.rs", root_path.display()), - "version": 0 - } - })), - ) - .unwrap(); - - let stdout = rls.wait_until_done_indexing_n(2, rls_timeout()); - - // bin depending on lib picks up type mismatch - let bin_diagnostic = rfind_diagnostics_with_uri(&stdout, "src/main.rs"); - let error_diagnostic = bin_diagnostic["params"]["diagnostics"] - .as_array() - .unwrap() - .iter() - .find(|d| d["code"] == "E0425") - .expect("expected bin error diagnostic"); - assert!(error_diagnostic["message"] - .as_str() - .unwrap() - .contains("cannot find function `foo` in module `inner`")); - - rls.notify( - "textDocument/didChange", - Some(json!({ - "contentChanges": [ - { - "range": { - "start": { - "line": 1, - "character": 23, - }, - "end": { - "line": 1, - "character": 26, - } - }, - "rangeLength": 3, - "text": "foo" - } - ], - "textDocument": { - "uri": format!("file://{}/inner/src/lib.rs", root_path.display()), - "version": 1 - } - })), - ) - .unwrap(); - - let stdout = rls.wait_until_done_indexing_n(3, rls_timeout()); - let bin_diagnostic = rfind_diagnostics_with_uri(&stdout, "src/main.rs"); - assert_eq!( - bin_diagnostic["params"]["diagnostics"][0]["code"], - "unused_variables" - ); - - rls.shutdown(rls_timeout()); -} - #[test] fn cmd_test_complete_self_crate_name() { let p = project("ws_with_test_dir") From 92d962880a350546084f77580d97c42308a09a49 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 14 Jan 2019 23:51:41 +0100 Subject: [PATCH 05/16] Translate cmd_test_complete_self_crate_name --- tests/client.rs | 73 +++++++++++++++++++++++++++++++++++ tests/tests.rs | 100 ------------------------------------------------ 2 files changed, 73 insertions(+), 100 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index b94854e5785..380d5849157 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -394,3 +394,76 @@ fn client_implicit_workspace_pick_up_lib_changes() { rls.shutdown(); } + +#[test] +fn client_test_complete_self_crate_name() { + let p = project("ws_with_test_dir") + .file( + "Cargo.toml", + r#" + [workspace] + members = ["library"] + "#, + ) + .file( + "library/Cargo.toml", + r#" + [package] + name = "library" + version = "0.1.0" + authors = ["Example "] + "#, + ) + .file( + "library/src/lib.rs", + r#" + pub fn function() -> usize { 5 } + "#, + ) + .file( + "library/tests/test.rs", + r#" + extern crate library; + use library::~ + "#, + ) + .build(); + + let root_path = p.root(); + let mut rls = p.spawn_rls_async(); + + rls.request::(0, initialize_params(root_path)); + + let diag = rls.wait_for_diagnostics(); + assert!(diag.diagnostics[0].message.contains("expected identifier")); + + // Sometimes RLS is not ready immediately for completion + let mut detail = None; + for id in 100..103 { + let response = rls.request::(id, CompletionParams { + context: Some(CompletionContext { + trigger_character: Some(":".to_string()), + trigger_kind: CompletionTriggerKind::TriggerCharacter, + }), + position: Position::new(2, 32), + text_document: TextDocumentIdentifier { + uri: Url::from_file_path(p.root().join("library/tests/test.rs")).unwrap(), + } + }); + + let items = match response { + Some(CompletionResponse::Array(items)) => items, + Some(CompletionResponse::List(CompletionList { items, ..})) => items, + _ => Vec::new(), + }; + + if let Some(item) = items.get(0) { + detail = item.detail.clone(); + break; + } + } + // Make sure we get the completion at least once right + assert_eq!(detail.as_ref().unwrap(), "pub fn function() -> usize"); + + rls.shutdown(); +} diff --git a/tests/tests.rs b/tests/tests.rs index 3c420825de8..02aae7f44a0 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -13,106 +13,6 @@ use self::support::{fixtures_dir, rls_timeout}; #[allow(dead_code)] mod support; -#[test] -fn cmd_test_complete_self_crate_name() { - let p = project("ws_with_test_dir") - .file( - "Cargo.toml", - r#" - [workspace] - members = ["library"] - "#, - ) - .file( - "library/Cargo.toml", - r#" - [package] - name = "library" - version = "0.1.0" - authors = ["Example "] - "#, - ) - .file( - "library/src/lib.rs", - r#" - pub fn function() -> usize { 5 } - "#, - ) - .file( - "library/tests/test.rs", - r#" - extern crate library; - use library::~ - "#, - ) - .build(); - - let root_path = p.root(); - let mut rls = p.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": {} - })), - ) - .unwrap(); - - let stdout = rls.wait_until_done_indexing(rls_timeout()); - - let json: Vec<_> = stdout - .to_json_messages() - .filter(|json| json["method"] != "window/progress") - .collect(); - assert!(json.len() > 1); - - assert!(json[0]["result"]["capabilities"].is_object()); - - assert_eq!(json[1]["method"], "textDocument/publishDiagnostics"); - assert!(json[1]["params"]["diagnostics"][0]["message"] - .as_str() - .unwrap() - .contains("expected identifier")); - - let mut json = serde_json::Value::Null; - for i in 0..3 { - let request_id = 100 + i; - - rls.request( - request_id, - "textDocument/completion", - Some(json!({ - "context": { - "triggerCharacter": ":", - "triggerKind": 2 - }, - "position": { - "character": 32, - "line": 2 - }, - "textDocument": { - "uri": format!("file://{}/library/tests/test.rs", root_path.display()), - "version": 1 - } - })), - ) - .unwrap(); - - json = rls.wait_until_json_id(request_id, rls_timeout()); - - if !json["result"].as_array().unwrap().is_empty() { - // retry completion message, rls not ready? - std::thread::sleep(Duration::from_millis(50)); - continue; - } - }; - assert_eq!(json["result"][0]["detail"], "pub fn function() -> usize"); - - rls.shutdown(rls_timeout()); -} - #[test] fn test_completion_suggests_arguments_in_statements() { let p = project("ws_with_test_dir") From 0fc768d3b7878d76cc69c6679d3649555e0bf5ea Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 15 Jan 2019 00:07:58 +0100 Subject: [PATCH 06/16] Translate completion_suggests_arguments_in_statements --- tests/client.rs | 91 +++++++++++++++++++++++++++++++++++++++++++++++ tests/tests.rs | 94 ------------------------------------------------- 2 files changed, 91 insertions(+), 94 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index 380d5849157..4334593e1bf 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -467,3 +467,94 @@ fn client_test_complete_self_crate_name() { rls.shutdown(); } + +#[test] +fn client_completion_suggests_arguments_in_statements() { + let p = project("ws_with_test_dir") + .file( + "Cargo.toml", + r#" + [workspace] + members = ["library"] + "#, + ) + .file( + "library/Cargo.toml", + r#" + [package] + name = "library" + version = "0.1.0" + authors = ["Example "] + "#, + ) + .file( + "library/src/lib.rs", + r#" + pub fn function() -> usize { 5 } + "#, + ) + .file( + "library/tests/test.rs", + r#" + extern crate library; + fn magic() { + let a = library::f~ + } + "#, + ) + .build(); + + let root_path = p.root(); + let mut rls = p.spawn_rls_async(); + + rls.request::(0, lsp_types::InitializeParams { + process_id: None, + root_uri: None, + root_path: Some(root_path.display().to_string()), + initialization_options: None, + capabilities: lsp_types::ClientCapabilities { + workspace: None, + text_document: Some(TextDocumentClientCapabilities { + completion: Some(CompletionCapability { + completion_item: Some(CompletionItemCapability { + snippet_support: Some(true), + ..CompletionItemCapability::default() + }), + ..CompletionCapability::default() + }), + ..TextDocumentClientCapabilities::default() + }), + experimental: None, + }, + trace: None, + workspace_folders: None, + }); + + // Sometimes RLS is not ready immediately for completion + let mut insert_text = None; + for id in 100..103 { + let response = rls.request::(id, CompletionParams { + context: Some(CompletionContext { + trigger_character: Some("f".to_string()), + trigger_kind: CompletionTriggerKind::TriggerCharacter, + }), + position: Position::new(3, 41), + text_document: TextDocumentIdentifier { + uri: Url::from_file_path(p.root().join("library/tests/test.rs")).unwrap(), + } + }); + + let items = match response { + Some(CompletionResponse::Array(items)) => items, + Some(CompletionResponse::List(CompletionList { items, ..})) => items, + _ => Vec::new(), + }; + + if let Some(item) = items.get(0) { + insert_text = item.insert_text.clone(); + break; + } + } + // Make sure we get the completion at least once right + assert_eq!(insert_text.as_ref().unwrap(), "function()"); +} diff --git a/tests/tests.rs b/tests/tests.rs index 02aae7f44a0..b924545c0c4 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -13,100 +13,6 @@ use self::support::{fixtures_dir, rls_timeout}; #[allow(dead_code)] mod support; -#[test] -fn test_completion_suggests_arguments_in_statements() { - let p = project("ws_with_test_dir") - .file( - "Cargo.toml", - r#" - [workspace] - members = ["library"] - "#, - ) - .file( - "library/Cargo.toml", - r#" - [package] - name = "library" - version = "0.1.0" - authors = ["Example "] - "#, - ) - .file( - "library/src/lib.rs", - r#" - pub fn function() -> usize { 5 } - "#, - ) - .file( - "library/tests/test.rs", - r#" - extern crate library; - fn magic() { - let a = library::f~ - } - "#, - ) - .build(); - - let root_path = p.root(); - let mut rls = p.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": { - "textDocument": { - "completion": { - "completionItem": { - "snippetSupport": true - } - } - } - } - })), - ) - .unwrap(); - - let mut json = serde_json::Value::Null; - for i in 0..3 { - let request_id = 100 + i; - - rls.request( - request_id, - "textDocument/completion", - Some(json!({ - "context": { - "triggerCharacter": "f", - "triggerKind": 2 - }, - "position": { - "character": 41, - "line": 3 - }, - "textDocument": { - "uri": format!("file://{}/library/tests/test.rs", root_path.display()), - "version": 1 - } - })), - ) - .unwrap(); - - json = rls.wait_until_json_id(request_id, rls_timeout()); - - if json["result"].as_array().unwrap().is_empty() { - // retry completion message, rls not ready? - std::thread::sleep(Duration::from_millis(50)); - continue; - } - } - assert_eq!(json["result"][0]["insertText"], "function()"); - - rls.shutdown(rls_timeout()); -} - #[test] fn test_use_statement_completion_doesnt_suggest_arguments() { let p = project("ws_with_test_dir") From 58fe39c2bd0f78e02ef65abbede8b6bc0a3adfdf Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 15 Jan 2019 00:19:07 +0100 Subject: [PATCH 07/16] Translate test_use_statement_completion_doesnt_suggest_arguments --- tests/client.rs | 74 ++++++++++++++++++++++++++++++++++++++++++ tests/tests.rs | 86 ------------------------------------------------- 2 files changed, 74 insertions(+), 86 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index 4334593e1bf..0d813559c8d 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -530,6 +530,9 @@ fn client_completion_suggests_arguments_in_statements() { workspace_folders: None, }); + let diag = rls.wait_for_diagnostics(); + assert!(diag.diagnostics[0].message.contains("expected one of")); + // Sometimes RLS is not ready immediately for completion let mut insert_text = None; for id in 100..103 { @@ -558,3 +561,74 @@ fn client_completion_suggests_arguments_in_statements() { // Make sure we get the completion at least once right assert_eq!(insert_text.as_ref().unwrap(), "function()"); } + +#[test] +fn client_use_statement_completion_doesnt_suggest_arguments() { + let p = project("ws_with_test_dir") + .file( + "Cargo.toml", + r#" + [workspace] + members = ["library"] + "#, + ) + .file( + "library/Cargo.toml", + r#" + [package] + name = "library" + version = "0.1.0" + authors = ["Example "] + "#, + ) + .file( + "library/src/lib.rs", + r#" + pub fn function() -> usize { 5 } + "#, + ) + .file( + "library/tests/test.rs", + r#" + extern crate library; + use library::~; + "#, + ) + .build(); + + let root_path = p.root(); + let mut rls = p.spawn_rls_async(); + + rls.request::(0, initialize_params(root_path)); + + let diag = rls.wait_for_diagnostics(); + assert!(diag.diagnostics[0].message.contains("expected identifier")); + + // Sometimes RLS is not ready immediately for completion + let mut insert_text = None; + for id in 100..103 { + let response = rls.request::(id, CompletionParams { + context: Some(CompletionContext { + trigger_character: Some(":".to_string()), + trigger_kind: CompletionTriggerKind::TriggerCharacter, + }), + position: Position::new(2, 32), + text_document: TextDocumentIdentifier { + uri: Url::from_file_path(p.root().join("library/tests/test.rs")).unwrap(), + } + }); + + let items = match response { + Some(CompletionResponse::Array(items)) => items, + Some(CompletionResponse::List(CompletionList { items, ..})) => items, + _ => Vec::new(), + }; + + if let Some(item) = items.get(0) { + insert_text = item.insert_text.clone(); + break; + } + } + // Make sure we get the completion at least once right + assert_eq!(insert_text.as_ref().unwrap(), "function"); +} diff --git a/tests/tests.rs b/tests/tests.rs index b924545c0c4..8dc3ff48f83 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,7 +1,6 @@ use serde_json::{self, json, Value as JsonValue}; use std::io::Write; -use std::time::Duration; use rls::actions::requests; use rls::lsp_data::request::Request as _; @@ -13,91 +12,6 @@ use self::support::{fixtures_dir, rls_timeout}; #[allow(dead_code)] mod support; -#[test] -fn test_use_statement_completion_doesnt_suggest_arguments() { - let p = project("ws_with_test_dir") - .file( - "Cargo.toml", - r#" - [workspace] - members = ["library"] - "#, - ) - .file( - "library/Cargo.toml", - r#" - [package] - name = "library" - version = "0.1.0" - authors = ["Example "] - "#, - ) - .file( - "library/src/lib.rs", - r#" - pub fn function() -> usize { 5 } - "#, - ) - .file( - "library/tests/test.rs", - r#" - extern crate library; - use library::~; - "#, - ) - .build(); - - //32, 2 - let root_path = p.root(); - let mut rls = p.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": {} - })), - ) - .unwrap(); - - let mut json = serde_json::Value::Null; - for i in 0..3 { - let request_id = 100 + i; - - rls.request( - request_id, - "textDocument/completion", - Some(json!({ - "context": { - "triggerCharacter": ":", - "triggerKind": 2 - }, - "position": { - "character": 32, - "line": 2 - }, - "textDocument": { - "uri": format!("file://{}/library/tests/test.rs", root_path.display()), - "version": 1 - } - })), - ) - .unwrap(); - - json = rls.wait_until_json_id(request_id, rls_timeout()); - - if json["result"].as_array().unwrap().is_empty() { - // retry completion message, rls not ready? - std::thread::sleep(Duration::from_millis(50)); - continue; - } - } - assert_eq!(json["result"][0]["insertText"], "function"); - - rls.shutdown(rls_timeout()); -} - /// Test simulates typing in a dependency wrongly in a couple of ways before finally getting it /// right. Rls should provide Cargo.toml diagnostics. /// From 53a0c62a43cdb8e6d39a65cdde03c039a0c131bd Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Sun, 20 Jan 2019 19:08:34 +0100 Subject: [PATCH 08/16] Translate client_dependency_typo_and_fix --- tests/client.rs | 90 +++++++++++++++++++++++++++++++ tests/tests.rs | 139 ------------------------------------------------ 2 files changed, 90 insertions(+), 139 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index 0d813559c8d..3be78ae29b8 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -632,3 +632,93 @@ fn client_use_statement_completion_doesnt_suggest_arguments() { // Make sure we get the completion at least once right assert_eq!(insert_text.as_ref().unwrap(), "function"); } + +/// Test simulates typing in a dependency wrongly in a couple of ways before finally getting it +/// right. Rls should provide Cargo.toml diagnostics. +/// +/// ``` +/// [dependencies] +/// version-check = "0.5555" +/// ``` +/// +/// * Firstly "version-check" doesn't exist, it should be "version_check" +/// * Secondly version 0.5555 of "version_check" doesn't exist. +#[test] +fn client_dependency_typo_and_fix() { + let manifest_with_dependency = |dep: &str| { + format!( + r#" + [package] + name = "dependency_typo" + version = "0.1.0" + authors = ["alexheretic@gmail.com"] + + [dependencies] + {} + "#, + dep + ) + }; + + let p = project("dependency_typo") + .file( + "Cargo.toml", + &manifest_with_dependency(r#"version-check = "0.5555""#), + ) + .file( + "src/main.rs", + r#" + fn main() { + println!("Hello world!"); + } + "#, + ) + .build(); + let root_path = p.root(); + let mut rls = p.spawn_rls_async(); + + rls.request::(0, initialize_params(root_path)); + + let diag = rls.wait_for_diagnostics(); + assert_eq!(diag.diagnostics.len(), 1); + assert_eq!(diag.diagnostics[0].severity, Some(DiagnosticSeverity::Error)); + assert!(diag.diagnostics[0].message.contains("no matching package named `version-check`")); + + let change_manifest = |contents: &str| { + std::fs::write(root_path.join("Cargo.toml"), contents).unwrap(); + }; + + // fix naming typo, we now expect a version error diagnostic + change_manifest(&manifest_with_dependency(r#"version_check = "0.5555""#)); + rls.notify::(DidChangeWatchedFilesParams { + changes: vec![ + FileEvent { + uri: Url::from_file_path(p.root().join("Cargo.toml")).unwrap(), + typ: FileChangeType::Changed + } + ] + }); + + let diag = rls.wait_for_diagnostics(); + assert_eq!(diag.diagnostics.len(), 1); + assert_eq!(diag.diagnostics[0].severity, Some(DiagnosticSeverity::Error)); + assert!(diag.diagnostics[0].message.contains("^0.5555")); + + // Fix version issue so no error diagnostics occur. + // This is kinda slow as cargo will compile the dependency, though I + // chose version_check to minimise this as it is a very small dependency. + change_manifest(&manifest_with_dependency(r#"version_check = "0.1""#)); + rls.notify::(DidChangeWatchedFilesParams { + changes: vec![ + FileEvent { + uri: Url::from_file_path(p.root().join("Cargo.toml")).unwrap(), + typ: FileChangeType::Changed + } + ] + }); + + let diag = rls.wait_for_diagnostics(); + assert_eq!(diag.diagnostics.iter().find(|d| d.severity == Some(DiagnosticSeverity::Error)), None); + + rls.shutdown(); +} diff --git a/tests/tests.rs b/tests/tests.rs index 8dc3ff48f83..d21210f2b26 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -12,145 +12,6 @@ use self::support::{fixtures_dir, rls_timeout}; #[allow(dead_code)] mod support; -/// Test simulates typing in a dependency wrongly in a couple of ways before finally getting it -/// right. Rls should provide Cargo.toml diagnostics. -/// -/// ``` -/// [dependencies] -/// version-check = "0.5555" -/// ``` -/// -/// * Firstly "version-check" doesn't exist, it should be "version_check" -/// * Secondly version 0.5555 of "version_check" doesn't exist. -#[test] -fn cmd_dependency_typo_and_fix() { - let manifest_with_dependency = |dep: &str| { - format!( - r#" - [package] - name = "dependency_typo" - version = "0.1.0" - authors = ["alexheretic@gmail.com"] - - [dependencies] - {} - "#, - dep - ) - }; - - let project = project("dependency_typo") - .file( - "Cargo.toml", - &manifest_with_dependency(r#"version-check = "0.5555""#), - ) - .file( - "src/main.rs", - r#" - fn main() { - println!("Hello world!"); - } - "#, - ) - .build(); - let root_path = project.root(); - let mut rls = project.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": {} - })), - ) - .unwrap(); - - let publish = rls - .wait_until_done_indexing(rls_timeout()) - .to_json_messages() - .rfind(|m| m["method"] == "textDocument/publishDiagnostics") - .expect("No publishDiagnostics"); - - let diags = &publish["params"]["diagnostics"]; - assert_eq!(diags.as_array().unwrap().len(), 1); - assert!(diags[0]["message"] - .as_str() - .unwrap() - .contains("no matching package named `version-check`")); - assert_eq!(diags[0]["severity"], 1); - - let change_manifest = |contents: &str| { - let mut manifest = std::fs::OpenOptions::new() - .write(true) - .open(root_path.join("Cargo.toml")) - .unwrap(); - - manifest.set_len(0).unwrap(); - write!(manifest, "{}", contents,).unwrap(); - }; - - // fix naming typo, we now expect a version error diagnostic - change_manifest(&manifest_with_dependency(r#"version_check = "0.5555""#)); - rls.request( - 1, - "workspace/didChangeWatchedFiles", - Some(json!({ - "changes": [{ - "uri": format!("file://{}/Cargo.toml", root_path.display()), - "type": 2 - }], - })), - ) - .unwrap(); - - let publish = rls - .wait_until_done_indexing_n(2, rls_timeout()) - .to_json_messages() - .rfind(|m| m["method"] == "textDocument/publishDiagnostics") - .expect("No publishDiagnostics"); - - let diags = &publish["params"]["diagnostics"]; - assert_eq!(diags.as_array().unwrap().len(), 1); - assert!(diags[0]["message"].as_str().unwrap().contains("^0.5555")); - assert_eq!(diags[0]["severity"], 1); - - // Fix version issue so no error diagnostics occur. - // This is kinda slow as cargo will compile the dependency, though I - // chose version_check to minimise this as it is a very small dependency. - change_manifest(&manifest_with_dependency(r#"version_check = "0.1""#)); - rls.request( - 2, - "workspace/didChangeWatchedFiles", - Some(json!({ - "changes": [{ - "uri": format!("file://{}/Cargo.toml", root_path.display()), - "type": 2 - }], - })), - ) - .unwrap(); - - let publish = rls - .wait_until_done_indexing_n(3, rls_timeout()) - .to_json_messages() - .rfind(|m| m["method"] == "textDocument/publishDiagnostics") - .expect("No publishDiagnostics"); - - let diags = &publish["params"]["diagnostics"]; - - assert_eq!( - diags - .as_array() - .unwrap() - .iter() - .find(|d| d["severity"] == 1), - None - ); - - rls.shutdown(rls_timeout()); -} - /// Tests correct positioning of a toml parse error, use of `==` instead of `=`. #[test] fn cmd_invalid_toml_manifest() { From 06e18f745aa47186dc6a7c6e01a682b5d0612e1a Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Sun, 20 Jan 2019 19:17:27 +0100 Subject: [PATCH 09/16] Translate invalid_toml_manifest --- tests/client.rs | 41 ++++++++++++++++++++++++++++++++++ tests/tests.rs | 58 ------------------------------------------------- 2 files changed, 41 insertions(+), 58 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index 3be78ae29b8..847d62e07d7 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -722,3 +722,44 @@ fn client_dependency_typo_and_fix() { rls.shutdown(); } + +/// Tests correct positioning of a toml parse error, use of `==` instead of `=`. +#[test] +fn client_invalid_toml_manifest() { + let p = project("invalid_toml") + .file( + "Cargo.toml", + r#"[package] + name = "probably_valid" + version == "0.1.0" + authors = ["alexheretic@gmail.com"] + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + println!("Hello world!"); + } + "#, + ) + .build(); + let root_path = p.root(); + let mut rls = p.spawn_rls_async(); + + rls.request::(0, initialize_params(root_path)); + + let diag: PublishDiagnosticsParams = rls.wait_for_diagnostics(); + + assert!(diag.uri.as_str().ends_with("invalid_toml/Cargo.toml")); + assert_eq!(diag.diagnostics.len(), 1); + assert_eq!(diag.diagnostics[0].severity, Some(DiagnosticSeverity::Error)); + assert!(diag.diagnostics[0].message.contains("failed to parse manifest")); + + assert_eq!(diag.diagnostics[0].range, Range { + start: Position { line: 2, character: 21 }, + end: Position { line: 2, character: 22 }, + }); + + rls.shutdown(); +} diff --git a/tests/tests.rs b/tests/tests.rs index d21210f2b26..1dc739744f4 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -12,64 +12,6 @@ use self::support::{fixtures_dir, rls_timeout}; #[allow(dead_code)] mod support; -/// Tests correct positioning of a toml parse error, use of `==` instead of `=`. -#[test] -fn cmd_invalid_toml_manifest() { - let project = project("invalid_toml") - .file( - "Cargo.toml", - r#"[package] - name = "probably_valid" - version == "0.1.0" - authors = ["alexheretic@gmail.com"] - "#, - ) - .file( - "src/main.rs", - r#" - fn main() { - println!("Hello world!"); - } - "#, - ) - .build(); - let root_path = project.root(); - let mut rls = project.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": {} - })), - ) - .unwrap(); - - let publish = rls - .wait_until_done_indexing(rls_timeout()) - .to_json_messages() - .rfind(|m| m["method"] == "textDocument/publishDiagnostics") - .expect("No publishDiagnostics"); - - let uri = publish["params"]["uri"].as_str().expect("uri"); - assert!(uri.ends_with("invalid_toml/Cargo.toml")); - - let diags = &publish["params"]["diagnostics"]; - assert_eq!(diags.as_array().unwrap().len(), 1); - assert_eq!(diags[0]["severity"], 1); - assert!(diags[0]["message"] - .as_str() - .unwrap() - .contains("failed to parse manifest")); - assert_eq!( - diags[0]["range"], - json!({ "start": { "line": 2, "character": 21 }, "end": { "line": 2, "character": 22 }}) - ); - - rls.shutdown(rls_timeout()); -} - /// Tests correct file highlighting of workspace member manifest with invalid path dependency. #[test] fn cmd_invalid_member_toml_manifest() { From 10c94c6cd1c9ab51091dab492a67916326ed52b9 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Sun, 20 Jan 2019 19:19:50 +0100 Subject: [PATCH 10/16] Translate invalid_member_toml_manifest --- tests/client.rs | 59 ++++++++++++++++++++++++++++++++++++++ tests/tests.rs | 76 ------------------------------------------------- 2 files changed, 59 insertions(+), 76 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index 847d62e07d7..4e8d99890b7 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -763,3 +763,62 @@ fn client_invalid_toml_manifest() { rls.shutdown(); } + +/// Tests correct file highlighting of workspace member manifest with invalid path dependency. +#[test] +fn client_invalid_member_toml_manifest() { + let project = project("invalid_member_toml") + .file( + "Cargo.toml", + r#"[package] + name = "root_is_fine" + version = "0.1.0" + authors = ["alexheretic@gmail.com"] + + [dependencies] + member_a = { path = "member_a" } + + [workspace] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "member_a/Cargo.toml", + r#"[package] + name = "member_a" + version = "0.0.3" + authors = ["alexheretic@gmail.com"] + + [dependencies] + dodgy_member = { path = "dodgy_member" } + "#, + ) + .file("member_a/src/lib.rs", "fn ma() {}") + .file( + "member_a/dodgy_member/Cargo.toml", + r#"[package] + name = "dodgy_member" + version = "0.5.0" + authors = ["alexheretic@gmail.com"] + + [dependencies] + nosuch = { path = "not-exist" } + "#, + ) + .file("member_a/dodgy_member/src/lib.rs", "fn dm() {}") + .build(); + let root_path = project.root(); + let mut rls = project.spawn_rls_async(); + + rls.request::(0, initialize_params(root_path)); + + let diag: PublishDiagnosticsParams = rls.wait_for_diagnostics(); + + assert!(diag.uri.as_str().ends_with("invalid_member_toml/member_a/dodgy_member/Cargo.toml")); + + assert_eq!(diag.diagnostics.len(), 1); + assert_eq!(diag.diagnostics[0].severity, Some(DiagnosticSeverity::Error)); + assert!(diag.diagnostics[0].message.contains("failed to read")); + + rls.shutdown(); +} diff --git a/tests/tests.rs b/tests/tests.rs index 1dc739744f4..005e45d65a7 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -12,82 +12,6 @@ use self::support::{fixtures_dir, rls_timeout}; #[allow(dead_code)] mod support; -/// Tests correct file highlighting of workspace member manifest with invalid path dependency. -#[test] -fn cmd_invalid_member_toml_manifest() { - let project = project("invalid_member_toml") - .file( - "Cargo.toml", - r#"[package] - name = "root_is_fine" - version = "0.1.0" - authors = ["alexheretic@gmail.com"] - - [dependencies] - member_a = { path = "member_a" } - - [workspace] - "#, - ) - .file("src/main.rs", "fn main() {}") - .file( - "member_a/Cargo.toml", - r#"[package] - name = "member_a" - version = "0.0.3" - authors = ["alexheretic@gmail.com"] - - [dependencies] - dodgy_member = { path = "dodgy_member" } - "#, - ) - .file("member_a/src/lib.rs", "fn ma() {}") - .file( - "member_a/dodgy_member/Cargo.toml", - r#"[package] - name = "dodgy_member" - version = "0.5.0" - authors = ["alexheretic@gmail.com"] - - [dependencies] - nosuch = { path = "not-exist" } - "#, - ) - .file("member_a/dodgy_member/src/lib.rs", "fn dm() {}") - .build(); - let root_path = project.root(); - let mut rls = project.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": {} - })), - ) - .unwrap(); - - let publish = rls - .wait_until_done_indexing(rls_timeout()) - .to_json_messages() - .rfind(|m| m["method"] == "textDocument/publishDiagnostics") - .expect("No publishDiagnostics"); - - let uri = publish["params"]["uri"].as_str().expect("uri"); - assert!(uri.ends_with("invalid_member_toml/member_a/dodgy_member/Cargo.toml")); - - let diags = &publish["params"]["diagnostics"]; - assert_eq!(diags.as_array().unwrap().len(), 1); - assert_eq!(diags[0]["severity"], 1); - assert!(diags[0]["message"] - .as_str() - .unwrap() - .contains("failed to read")); - - rls.shutdown(rls_timeout()); -} - #[test] fn cmd_invalid_member_dependency_resolution() { let project = project("invalid_member_resolution") From 0831568c05722627c7f194c1b269f547d484d8e2 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Sun, 20 Jan 2019 19:21:36 +0100 Subject: [PATCH 11/16] Translate invalid_member_dependency_resolution --- tests/client.rs | 58 ++++++++++++++++++++++++++++++++++++++ tests/tests.rs | 75 ------------------------------------------------- 2 files changed, 58 insertions(+), 75 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index 4e8d99890b7..cb8244183d8 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -822,3 +822,61 @@ fn client_invalid_member_toml_manifest() { rls.shutdown(); } + +#[test] +fn client_invalid_member_dependency_resolution() { + let project = project("invalid_member_resolution") + .file( + "Cargo.toml", + r#"[package] + name = "root_is_fine" + version = "0.1.0" + authors = ["alexheretic@gmail.com"] + + [dependencies] + member_a = { path = "member_a" } + + [workspace] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "member_a/Cargo.toml", + r#"[package] + name = "member_a" + version = "0.0.5" + authors = ["alexheretic@gmail.com"] + + [dependencies] + dodgy_member = { path = "dodgy_member" } + "#, + ) + .file("member_a/src/lib.rs", "fn ma() {}") + .file( + "member_a/dodgy_member/Cargo.toml", + r#"[package] + name = "dodgy_member" + version = "0.6.0" + authors = ["alexheretic@gmail.com"] + + [dependencies] + nosuchdep123 = "1.2.4" + "#, + ) + .file("member_a/dodgy_member/src/lib.rs", "fn dm() {}") + .build(); + let root_path = project.root(); + let mut rls = project.spawn_rls_async(); + + rls.request::(0, initialize_params(root_path)); + + let diag: PublishDiagnosticsParams = rls.wait_for_diagnostics(); + + assert!(diag.uri.as_str().ends_with("invalid_member_resolution/member_a/dodgy_member/Cargo.toml")); + + assert_eq!(diag.diagnostics.len(), 1); + assert_eq!(diag.diagnostics[0].severity, Some(DiagnosticSeverity::Error)); + assert!(diag.diagnostics[0].message.contains("no matching package named `nosuchdep123`")); + + rls.shutdown(); +} diff --git a/tests/tests.rs b/tests/tests.rs index 005e45d65a7..8a848a61c55 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -12,81 +12,6 @@ use self::support::{fixtures_dir, rls_timeout}; #[allow(dead_code)] mod support; -#[test] -fn cmd_invalid_member_dependency_resolution() { - let project = project("invalid_member_resolution") - .file( - "Cargo.toml", - r#"[package] - name = "root_is_fine" - version = "0.1.0" - authors = ["alexheretic@gmail.com"] - - [dependencies] - member_a = { path = "member_a" } - - [workspace] - "#, - ) - .file("src/main.rs", "fn main() {}") - .file( - "member_a/Cargo.toml", - r#"[package] - name = "member_a" - version = "0.0.5" - authors = ["alexheretic@gmail.com"] - - [dependencies] - dodgy_member = { path = "dodgy_member" } - "#, - ) - .file("member_a/src/lib.rs", "fn ma() {}") - .file( - "member_a/dodgy_member/Cargo.toml", - r#"[package] - name = "dodgy_member" - version = "0.6.0" - authors = ["alexheretic@gmail.com"] - - [dependencies] - nosuchdep123 = "1.2.4" - "#, - ) - .file("member_a/dodgy_member/src/lib.rs", "fn dm() {}") - .build(); - let root_path = project.root(); - let mut rls = project.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": {} - })), - ) - .unwrap(); - - let publish = rls - .wait_until_done_indexing(rls_timeout()) - .to_json_messages() - .rfind(|m| m["method"] == "textDocument/publishDiagnostics") - .expect("No publishDiagnostics"); - - let uri = publish["params"]["uri"].as_str().expect("uri"); - assert!(uri.ends_with("invalid_member_resolution/member_a/dodgy_member/Cargo.toml")); - - let diags = &publish["params"]["diagnostics"]; - assert_eq!(diags.as_array().unwrap().len(), 1); - assert_eq!(diags[0]["severity"], 1); - assert!(diags[0]["message"] - .as_str() - .unwrap() - .contains("no matching package named `nosuchdep123`")); - - rls.shutdown(rls_timeout()); -} - #[test] fn cmd_handle_utf16_unit_text_edits() { let project = project("cmd_handle_utf16_unit_text_edits") From 189470fd4498fa320e88b636bc436df602f25524 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Sun, 20 Jan 2019 19:27:23 +0100 Subject: [PATCH 12/16] Translate handle_utf16_unit_text_edits --- tests/client.rs | 46 ++++++++++++++++++++++++++++++++++++++ tests/tests.rs | 59 ------------------------------------------------- 2 files changed, 46 insertions(+), 59 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index cb8244183d8..40f3f8e470e 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -880,3 +880,49 @@ fn client_invalid_member_dependency_resolution() { rls.shutdown(); } + +#[test] +fn client_handle_utf16_unit_text_edits() { + let p = project("client_handle_utf16_unit_text_edits") + .file( + "Cargo.toml", + r#"[package] + name = "client_handle_utf16_unit_text_edits" + version = "0.1.0" + authors = ["example@example.com"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("src/some.rs", "😢") + .build(); + let root_path = p.root(); + let mut rls = p.spawn_rls_async(); + + rls.request::(0, initialize_params(root_path)); + + rls.wait_for_indexing(); + + rls.notify::(DidChangeTextDocumentParams { + text_document: VersionedTextDocumentIdentifier { + uri: Url::from_file_path(p.root().join("src/some.rs")).unwrap(), + version: Some(0), + }, + // "😢" -> "" + content_changes: vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 2 + } + }), + range_length: Some(2), + text: "".to_string(), + }] + }); + + rls.shutdown(); +} diff --git a/tests/tests.rs b/tests/tests.rs index 8a848a61c55..876ddd4247c 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -12,65 +12,6 @@ use self::support::{fixtures_dir, rls_timeout}; #[allow(dead_code)] mod support; -#[test] -fn cmd_handle_utf16_unit_text_edits() { - let project = project("cmd_handle_utf16_unit_text_edits") - .file( - "Cargo.toml", - r#"[package] - name = "cmd_handle_utf16_unit_text_edits" - version = "0.1.0" - authors = ["example@example.com"] - "#, - ) - .file("src/main.rs", "fn main() {}") - .file("src/unrelated.rs", "😢") - .build(); - let root_path = project.root(); - let mut rls = project.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": {} - })), - ) - .unwrap(); - - rls.wait_until_done_indexing(rls_timeout()); - - rls.notify( - "textDocument/didChange", - Some(json!( - {"textDocument": { - "uri": format!("file://{}/src/unrelated.rs", root_path.display()), - "version": 1 - }, - // "😢" -> "" - "contentChanges": [ - { - "range": { - "start": { - "line":0, - "character":0 - }, - "end":{ - "line":0, - "character":2 - } - }, - "rangeLength":2, - "text":"" - } - ] - })) - ).unwrap(); - - rls.shutdown(rls_timeout()); -} - /// Ensures that wide characters do not prevent RLS from calculating correct /// 'whole file' LSP range. #[test] From 8afa313a34bb0c5b5c2a97e6c3242c0e752be8e2 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Sun, 20 Jan 2019 19:34:33 +0100 Subject: [PATCH 13/16] Translate format_utf16_range --- tests/client.rs | 44 ++++++++++++++++++++++++++++++++++ tests/tests.rs | 63 ------------------------------------------------- 2 files changed, 44 insertions(+), 63 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index 40f3f8e470e..7677672f9e4 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -926,3 +926,47 @@ fn client_handle_utf16_unit_text_edits() { rls.shutdown(); } + +/// Ensures that wide characters do not prevent RLS from calculating correct +/// 'whole file' LSP range. +#[test] +fn client_format_utf16_range() { + let p = project("client_format_utf16_range") + .file( + "Cargo.toml", + r#"[package] + name = "client_format_utf16_range" + version = "0.1.0" + authors = ["example@example.com"] + "#, + ) + .file("src/main.rs", "/* 😢😢😢😢😢😢😢 */ fn main() { }") + .build(); + let root_path = p.root(); + let mut rls = p.spawn_rls_async(); + + rls.request::(0, initialize_params(root_path)); + + rls.wait_for_indexing(); + + let result = rls.request::(66, DocumentFormattingParams { + text_document: TextDocumentIdentifier { + uri: Url::from_file_path(p.root().join("src/main.rs")).unwrap(), + }, + options: FormattingOptions { + tab_size: 4, + insert_spaces: true, + properties: Default::default(), + } + }); + + let new_text: Vec<_> = result.unwrap() + .iter() + .map(|edit| edit.new_text.as_str().replace('\r', "")) + .collect(); + // Actual formatting isn't important - what is, is that the buffer isn't + // malformed and code stays semantically equivalent. + assert_eq!(new_text, vec!["/* 😢😢😢😢😢😢😢 */\nfn main() {}\n"]); + + rls.shutdown(); +} diff --git a/tests/tests.rs b/tests/tests.rs index 876ddd4247c..37a629f5053 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -12,69 +12,6 @@ use self::support::{fixtures_dir, rls_timeout}; #[allow(dead_code)] mod support; -/// Ensures that wide characters do not prevent RLS from calculating correct -/// 'whole file' LSP range. -#[test] -fn cmd_format_utf16_range() { - let project = project("cmd_format_utf16_range") - .file( - "Cargo.toml", - r#"[package] - name = "cmd_format_utf16_range" - version = "0.1.0" - authors = ["example@example.com"] - "#, - ) - .file("src/main.rs", "/* 😢😢😢😢😢😢😢 */ fn main() { }") - .build(); - let root_path = project.root(); - let mut rls = project.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": {} - })), - ) - .unwrap(); - - rls.wait_until_done_indexing(rls_timeout()); - - let request_id = 66; - rls.request( - request_id, - "textDocument/formatting", - Some(json!( - { - "textDocument": { - "uri": format!("file://{}/src/main.rs", root_path.display()), - "version": 1 - }, - "options": { - "tabSize": 4, - "insertSpaces": true - } - })) - ).unwrap(); - - let json = rls.wait_until_json_id(request_id, rls_timeout()); - eprintln!("{:#?}", json); - - let result = json["result"].as_array().unwrap(); - let new_text: Vec<_> = result - .iter() - .map(|o| o["newText"].as_str().unwrap()) - .map(|text| text.replace('\r', "")) - .collect(); - // Actual formatting isn't important - what is, is that the buffer isn't - // malformed and code stays semantically equivalent. - assert_eq!(new_text, vec!["/* 😢😢😢😢😢😢😢 */\nfn main() {}\n"]); - - rls.shutdown(rls_timeout()); -} - #[test] fn cmd_lens_run() { let p = ProjectBuilder::try_from_fixture(fixtures_dir().join("lens_run")) From a4a1fb7760e12d981cccf07ff9457ff96c153113 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Sun, 20 Jan 2019 19:46:37 +0100 Subject: [PATCH 14/16] Translate client_lens_run and find_definitions --- tests/client.rs | 192 +++++++++++++++++++++++++++++++++++- tests/tests.rs | 256 ------------------------------------------------ 2 files changed, 190 insertions(+), 258 deletions(-) delete mode 100644 tests/tests.rs diff --git a/tests/client.rs b/tests/client.rs index 7677672f9e4..d5e0cb7667e 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -2,9 +2,10 @@ use std::path::Path; use futures::future::Future; use lsp_types::{*, request::*, notification::*}; +use serde_json::json; -use crate::support::basic_bin_manifest; -use crate::support::project_builder::project; +use crate::support::{basic_bin_manifest, fixtures_dir}; +use crate::support::project_builder::{project, ProjectBuilder}; #[allow(dead_code)] mod support; @@ -970,3 +971,190 @@ fn client_format_utf16_range() { rls.shutdown(); } + +#[test] +fn client_lens_run() { + let p = ProjectBuilder::try_from_fixture(fixtures_dir().join("lens_run")) + .unwrap() + .build(); + let root_path = p.root(); + let mut rls = p.spawn_rls_async(); + + rls.request::(0, lsp_types::InitializeParams { + process_id: None, + root_uri: None, + root_path: Some(root_path.display().to_string()), + initialization_options: Some(json!({ "cmdRun": true})), + capabilities: Default::default(), + trace: None, + workspace_folders: None, + }); + + rls.wait_for_indexing(); + assert!(rls.messages().iter().count() >= 7); + + let lens = rls.request::(1, CodeLensParams { + text_document: TextDocumentIdentifier { + uri: Url::from_file_path(p.root().join("src/main.rs")).unwrap(), + } + }); + + let expected = CodeLens { + command: Some(Command { + command: "rls.run".to_string(), + title: "Run test".to_string(), + arguments: Some(vec![ + json!({ + "args": [ "test", "--", "--nocapture", "test_foo" ], + "binary": "cargo", + "env": { "RUST_BACKTRACE": "short" } + }) + ]), + }), + data: None, + range: Range { + start: Position { line: 14, character: 3 }, + end: Position { line: 14, character: 11 } + } + }; + + assert_eq!(lens, Some(vec![expected])); + + rls.shutdown(); +} + + +#[test] +fn client_find_definitions() { + const SRC: &str = r#" + struct Foo { + } + + impl Foo { + fn new() { + } + } + + fn main() { + Foo::new(); + } + "#; + + let p = project("simple_workspace") + .file("Cargo.toml", &basic_bin_manifest("bar")) + .file("src/main.rs", SRC) + .build(); + + let root_path = p.root(); + let mut rls = p.spawn_rls_async(); + + rls.request::(0, lsp_types::InitializeParams { + process_id: None, + root_uri: None, + root_path: Some(root_path.display().to_string()), + initialization_options: Some(json!({"settings.rust.racer_completion": false})), + capabilities: Default::default(), + trace: None, + workspace_folders: None, + }); + + rls.wait_for_indexing(); + + let mut results = vec![]; + for (line_index, line) in SRC.lines().enumerate() { + for i in 0..line.len() { + let id = (line_index * 100 + i) as u64; + let result = rls.request::(id, TextDocumentPositionParams { + position: Position { line: line_index as u64, character: i as u64 }, + text_document: TextDocumentIdentifier { + uri: Url::from_file_path(p.root().join("src/main.rs")).unwrap(), + } + }); + + let ranges = result.into_iter().flat_map(|x| match x { + GotoDefinitionResponse::Scalar(loc) => vec![loc].into_iter(), + GotoDefinitionResponse::Array(locs) => locs.into_iter(), + _ => unreachable!(), + }).map(|x| x.range).collect(); + + if !results.is_empty() { + results.push((line_index, i, ranges)); + } + } + } + rls.shutdown(); + + // Foo + let foo_definition = Range { + start: Position { line: 1, character: 15 }, + end: Position { line: 1, character: 18 } + }; + + // Foo::new + let foo_new_definition = Range { + start: Position { line: 5, character: 15 }, + end: Position { line: 5, character: 18 } + }; + + // main + let main_definition = Range { + start: Position { line: 9, character: 11 }, + end: Position { line: 9, character: 15 } + }; + + let expected = [ + // struct Foo + (1, 15, vec![foo_definition.clone()]), + (1, 16, vec![foo_definition.clone()]), + (1, 17, vec![foo_definition.clone()]), + (1, 18, vec![foo_definition.clone()]), + // impl Foo + (4, 13, vec![foo_definition.clone()]), + (4, 14, vec![foo_definition.clone()]), + (4, 15, vec![foo_definition.clone()]), + (4, 16, vec![foo_definition.clone()]), + + // fn new + (5, 15, vec![foo_new_definition.clone()]), + (5, 16, vec![foo_new_definition.clone()]), + (5, 17, vec![foo_new_definition.clone()]), + (5, 18, vec![foo_new_definition.clone()]), + + // fn main + (9, 11, vec![main_definition.clone()]), + (9, 12, vec![main_definition.clone()]), + (9, 13, vec![main_definition.clone()]), + (9, 14, vec![main_definition.clone()]), + (9, 15, vec![main_definition.clone()]), + + // Foo::new() + (10, 12, vec![foo_definition.clone()]), + (10, 13, vec![foo_definition.clone()]), + (10, 14, vec![foo_definition.clone()]), + (10, 15, vec![foo_definition.clone()]), + (10, 17, vec![foo_new_definition.clone()]), + (10, 18, vec![foo_new_definition.clone()]), + (10, 19, vec![foo_new_definition.clone()]), + (10, 20, vec![foo_new_definition.clone()]), + ]; + + if results.len() != expected.len() { + panic!( + "Got different amount of completions than expected: {} vs. {}: {:#?}", + results.len(), + expected.len(), + results + ) + } + + for (i, (actual, expected)) in results.iter().zip(expected.iter()).enumerate() { + if actual != expected { + panic!( + "Found different definition at index {}. Got {:#?}, expected {:#?}", + i, + actual, + expected + ) + } + } +} diff --git a/tests/tests.rs b/tests/tests.rs deleted file mode 100644 index 37a629f5053..00000000000 --- a/tests/tests.rs +++ /dev/null @@ -1,256 +0,0 @@ -use serde_json::{self, json, Value as JsonValue}; - -use std::io::Write; - -use rls::actions::requests; -use rls::lsp_data::request::Request as _; - -use self::support::harness::compare_json; -use self::support::project_builder::{project, ProjectBuilder}; -use self::support::{fixtures_dir, rls_timeout}; - -#[allow(dead_code)] -mod support; - -#[test] -fn cmd_lens_run() { - let p = ProjectBuilder::try_from_fixture(fixtures_dir().join("lens_run")) - .unwrap() - .build(); - let root_path = p.root(); - let mut rls = p.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": {}, - "initializationOptions": { "cmdRun": true } - })), - ) - .unwrap(); - - let json: Vec<_> = rls - .wait_until_done_indexing(rls_timeout()) - .to_json_messages() - .collect(); - assert!(json.len() >= 7); - - let request_id = 1; - rls.request( - request_id, - requests::CodeLensRequest::METHOD, - Some(json!({ - "textDocument": { - "uri": format!("file://{}/src/main.rs", root_path.display()), - "version": 1 - } - })), - ) - .unwrap(); - - let json = rls.wait_until_json_id(request_id, rls_timeout()); - - compare_json( - &json["result"], - r#"[{ - "command": { - "command": "rls.run", - "title": "Run test", - "arguments": [{ - "args": [ "test", "--", "--nocapture", "test_foo" ], - "binary": "cargo", - "env": { "RUST_BACKTRACE": "short" } - }] - }, - "range": { - "start": { "character": 3, "line": 14 }, - "end": { "character": 11, "line": 14 } - } - }]"#, - ); - - rls.shutdown(rls_timeout()); -} - -#[test] -fn test_find_definitions() { - const SRC: &str = r#" - struct Foo { - } - - impl Foo { - fn new() { - } - } - - fn main() { - Foo::new(); - } - "#; - - let p = project("simple_workspace") - .file("Cargo.toml", &basic_bin_manifest("bar")) - .file("src/main.rs", SRC) - .build(); - - let root_path = p.root(); - let mut rls = p.spawn_rls(); - - rls.request( - 0, - "initialize", - Some(json!({ - "rootPath": root_path, - "capabilities": {}, - "initializationOptions": { - "settings": { - "rust": { - "racer_completion": false - } - } - } - })), - ) - .unwrap(); - - rls.wait_until_done_indexing(rls_timeout()); - - let uri = format!("file://{}/src/main.rs", root_path.display()); - - let mut results = vec![]; - let mut request_id = 1; - for (line_index, line) in SRC.lines().enumerate() { - for i in 0..line.len() { - rls.request( - request_id, - "textDocument/definition", - Some(json!({ - "position": { - "character": i, - "line": line_index - }, - "textDocument": { - "uri": uri, - "version": 1 - } - })), - ) - .unwrap(); - - let json = rls.wait_until_json_id(request_id, rls_timeout()); - let result = json["result"].as_array().unwrap(); - - request_id += 1; - - if result.is_empty() { - continue; - } - - results.push(( - line_index, - i, - result - .iter() - .map(|definition| definition["range"].clone()) - .collect::>(), - )); - } - } - - rls.shutdown(rls_timeout()); - - // Foo - let foo_definition: JsonValue = json!({ - "start": { - "line": 1, - "character": 15, - }, - "end": { - "line": 1, - "character": 18, - } - }); - - // Foo::new - let foo_new_definition: JsonValue = json!({ - "start": { - "line": 5, - "character": 15, - }, - "end": { - "line": 5, - "character": 18, - } - }); - - - // main - let main_definition: JsonValue = json!({ - "start": { - "line": 9, - "character": 11, - }, - "end": { - "line": 9, - "character": 15, - } - }); - - let expected = [ - // struct Foo - (1, 15, vec![foo_definition.clone()]), - (1, 16, vec![foo_definition.clone()]), - (1, 17, vec![foo_definition.clone()]), - (1, 18, vec![foo_definition.clone()]), - // impl Foo - (4, 13, vec![foo_definition.clone()]), - (4, 14, vec![foo_definition.clone()]), - (4, 15, vec![foo_definition.clone()]), - (4, 16, vec![foo_definition.clone()]), - - // fn new - (5, 15, vec![foo_new_definition.clone()]), - (5, 16, vec![foo_new_definition.clone()]), - (5, 17, vec![foo_new_definition.clone()]), - (5, 18, vec![foo_new_definition.clone()]), - - // fn main - (9, 11, vec![main_definition.clone()]), - (9, 12, vec![main_definition.clone()]), - (9, 13, vec![main_definition.clone()]), - (9, 14, vec![main_definition.clone()]), - (9, 15, vec![main_definition.clone()]), - - // Foo::new() - (10, 12, vec![foo_definition.clone()]), - (10, 13, vec![foo_definition.clone()]), - (10, 14, vec![foo_definition.clone()]), - (10, 15, vec![foo_definition.clone()]), - (10, 17, vec![foo_new_definition.clone()]), - (10, 18, vec![foo_new_definition.clone()]), - (10, 19, vec![foo_new_definition.clone()]), - (10, 20, vec![foo_new_definition.clone()]), - ]; - - if results.len() != expected.len() { - panic!( - "Got different amount of completions than expected: {} vs. {}: {:#?}", - results.len(), - expected.len(), - results - ) - } - - for (i, (actual, expected)) in results.iter().zip(expected.iter()).enumerate() { - if actual != expected { - panic!( - "Found different definition at index {}. Got {:#?}, expected {:#?}", - i, - actual, - expected - ) - } - } -} From c1d705ddc169928c66e435280dada6ac5e3bdcfb Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 22 Jan 2019 17:46:48 +0100 Subject: [PATCH 15/16] Ensure we always wait with a timeout --- tests/client.rs | 12 ++++++------ tests/support/client.rs | 12 ++++++++---- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index d5e0cb7667e..8be3e3c91ca 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -222,7 +222,7 @@ fn client_changing_workspace_lib_retains_diagnostics() { let lib = rls.future_diagnostics("library/src/lib.rs"); let bin = rls.future_diagnostics("binary/src/main.rs"); - let (lib, bin) = rls.runtime().block_on(lib.join(bin)).unwrap(); + let (lib, bin) = rls.block_on(lib.join(bin)).unwrap(); assert!(lib.diagnostics.iter().any(|m| m.message.contains("unused variable: `test_val`"))); assert!(lib.diagnostics.iter().any(|m| m.message.contains("unused variable: `unused`"))); @@ -251,7 +251,7 @@ fn client_changing_workspace_lib_retains_diagnostics() { let lib = rls.future_diagnostics("library/src/lib.rs"); let bin = rls.future_diagnostics("binary/src/main.rs"); - let (lib, bin) = rls.runtime().block_on(lib.join(bin)).unwrap(); + let (lib, bin) = rls.block_on(lib.join(bin)).unwrap(); // lib unit tests have compile errors assert!(lib.diagnostics.iter().any(|m| m.message.contains("unused variable: `unused`"))); @@ -282,7 +282,7 @@ fn client_changing_workspace_lib_retains_diagnostics() { let lib = rls.future_diagnostics("library/src/lib.rs"); let bin = rls.future_diagnostics("binary/src/main.rs"); - let (lib, bin) = rls.runtime().block_on(lib.join(bin)).unwrap(); + let (lib, bin) = rls.block_on(lib.join(bin)).unwrap(); assert!(lib.diagnostics.iter().any(|m| m.message.contains("unused variable: `test_val`"))); assert!(lib.diagnostics.iter().any(|m| m.message.contains("unused variable: `unused`"))); @@ -339,7 +339,7 @@ fn client_implicit_workspace_pick_up_lib_changes() { rls.request::(0, initialize_params(root_path)); let bin = rls.future_diagnostics("src/main.rs"); - let bin = rls.runtime().block_on(bin).unwrap(); + let bin = rls.block_on(bin).unwrap(); assert!(bin.diagnostics[0].message.contains("unused variable: `val`")); rls.notify::(DidChangeTextDocumentParams { @@ -365,7 +365,7 @@ fn client_implicit_workspace_pick_up_lib_changes() { // bin depending on lib picks up type mismatch let bin = rls.future_diagnostics("src/main.rs"); - let bin = rls.runtime().block_on(bin).unwrap(); + let bin = rls.block_on(bin).unwrap(); assert!(bin.diagnostics[0].message.contains("cannot find function `foo`")); rls.notify::(DidChangeTextDocumentParams { @@ -390,7 +390,7 @@ fn client_implicit_workspace_pick_up_lib_changes() { }); let bin = rls.future_diagnostics("src/main.rs"); - let bin = rls.runtime().block_on(bin).unwrap(); + let bin = rls.block_on(bin).unwrap(); assert!(bin.diagnostics[0].message.contains("unused variable: `val`")); rls.shutdown(); diff --git a/tests/support/client.rs b/tests/support/client.rs index b2fa81194a5..eafe99c51ec 100644 --- a/tests/support/client.rs +++ b/tests/support/client.rs @@ -135,8 +135,11 @@ impl RlsHandle { self.messages.borrow() } - pub fn runtime(&mut self) -> &mut Runtime { - &mut self.runtime + /// Block on returned, associated future with a timeout. + pub fn block_on(&mut self, f: F) -> Result> { + let future_with_timeout = f.timeout(rls_timeout()); + + self.runtime.block_on(future_with_timeout) } /// Send a request to the RLS and block until we receive the message. @@ -182,7 +185,7 @@ impl RlsHandle { let fut = writer.send(msg); - self.writer = Some(self.runtime.block_on(fut).unwrap()); + self.writer = Some(self.block_on(fut).unwrap()); } /// Enqueues a channel that is notified and consumed when a given predicate @@ -214,7 +217,8 @@ impl RlsHandle { /// Blocks until a message, for which predicate `f` returns true, is received. pub fn wait_for_message(&mut self, f: impl Fn(&Value) -> bool + 'static) -> Value { let fut = self.future_msg(f); - self.runtime.block_on(fut).unwrap() + + self.block_on(fut).unwrap() } /// Blocks until the processing (building + indexing) is done by the RLS. From 663e437ea53723f59a864f726d1622e0683d8b48 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 22 Jan 2019 20:21:28 +0100 Subject: [PATCH 16/16] Fix definitions test Pulls lsp-codec 0.1.1 due to message send buffer reservation fix. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- tests/client.rs | 12 +++++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6dbbfc4ba1e..661efbe6774 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -837,7 +837,7 @@ dependencies = [ [[package]] name = "lsp-codec" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1356,7 +1356,7 @@ dependencies = [ "jsonrpc-core 9.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lsp-codec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lsp-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types 0.54.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "ordslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2298,7 +2298,7 @@ dependencies = [ "checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" -"checksum lsp-codec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b29e3d1632fef13c1286b0b2f8545a7d894ae565a7fac013b90a17ee5bfbc91" +"checksum lsp-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3570f641b984e3e4613a1ca34db2ea72549bbc3c0316d33f5af91ab514dcbff" "checksum lsp-types 0.54.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a252cc2be87d9329dd91c505a951996b3263582ba304870960faaae77b642183" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e1dd4eaac298c32ce07eb6ed9242eda7d82955b9170b7d6db59b2e02cc63fcb8" diff --git a/Cargo.toml b/Cargo.toml index 7da7729f784..7f521bf4226 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ rustc-workspace-hack = "1.0.0" [dev-dependencies] difference = "2" tempfile = "3" -lsp-codec = "0.1" +lsp-codec = "0.1.1" tokio = "0.1" futures = "0.1" tokio-process = "0.2" diff --git a/tests/client.rs b/tests/client.rs index 8be3e3c91ca..321774af253 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -1052,7 +1052,13 @@ fn client_find_definitions() { process_id: None, root_uri: None, root_path: Some(root_path.display().to_string()), - initialization_options: Some(json!({"settings.rust.racer_completion": false})), + initialization_options: Some(json!({ + "settings": { + "rust": { + "racer_completion": false + } + } + })), capabilities: Default::default(), trace: None, workspace_folders: None, @@ -1071,13 +1077,13 @@ fn client_find_definitions() { } }); - let ranges = result.into_iter().flat_map(|x| match x { + let ranges: Vec<_> = result.into_iter().flat_map(|x| match x { GotoDefinitionResponse::Scalar(loc) => vec![loc].into_iter(), GotoDefinitionResponse::Array(locs) => locs.into_iter(), _ => unreachable!(), }).map(|x| x.range).collect(); - if !results.is_empty() { + if !ranges.is_empty() { results.push((line_index, i, ranges)); } }