diff options
author | Aleksey Kladov <[email protected]> | 2018-09-16 10:54:24 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-09-16 11:07:39 +0100 |
commit | b5021411a84822cb3f1e3aeffad9550dd15bdeb6 (patch) | |
tree | 9dca564f8e51b298dced01c4ce669c756dce3142 /crates/ra_lsp_server/tests | |
parent | ba0bfeee12e19da40b5eabc8d0408639af10e96f (diff) |
rename all things
Diffstat (limited to 'crates/ra_lsp_server/tests')
-rw-r--r-- | crates/ra_lsp_server/tests/heavy_tests/main.rs | 99 | ||||
-rw-r--r-- | crates/ra_lsp_server/tests/heavy_tests/support.rs | 217 |
2 files changed, 316 insertions, 0 deletions
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs new file mode 100644 index 000000000..dced45f55 --- /dev/null +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs | |||
@@ -0,0 +1,99 @@ | |||
1 | #[macro_use] | ||
2 | extern crate crossbeam_channel; | ||
3 | extern crate tempdir; | ||
4 | extern crate languageserver_types; | ||
5 | extern crate serde; | ||
6 | extern crate serde_json; | ||
7 | extern crate gen_lsp_server; | ||
8 | extern crate flexi_logger; | ||
9 | extern crate ra_lsp_server; | ||
10 | |||
11 | mod support; | ||
12 | |||
13 | use ra_lsp_server::req::{Runnables, RunnablesParams}; | ||
14 | |||
15 | use support::project; | ||
16 | |||
17 | |||
18 | const LOG: &'static str = ""; | ||
19 | |||
20 | #[test] | ||
21 | fn test_runnables_no_project() { | ||
22 | let server = project(r" | ||
23 | //- lib.rs | ||
24 | #[test] | ||
25 | fn foo() { | ||
26 | } | ||
27 | "); | ||
28 | server.request::<Runnables>( | ||
29 | RunnablesParams { | ||
30 | text_document: server.doc_id("lib.rs"), | ||
31 | position: None, | ||
32 | }, | ||
33 | r#"[ | ||
34 | { | ||
35 | "args": [ "test", "--", "foo", "--nocapture" ], | ||
36 | "bin": "cargo", | ||
37 | "env": { "RUST_BACKTRACE": "short" }, | ||
38 | "label": "test foo", | ||
39 | "range": { | ||
40 | "end": { "character": 1, "line": 2 }, | ||
41 | "start": { "character": 0, "line": 0 } | ||
42 | } | ||
43 | } | ||
44 | ]"# | ||
45 | ); | ||
46 | } | ||
47 | |||
48 | #[test] | ||
49 | fn test_runnables_project() { | ||
50 | let server = project(r#" | ||
51 | //- Cargo.toml | ||
52 | [package] | ||
53 | name = "foo" | ||
54 | version = "0.0.0" | ||
55 | |||
56 | //- src/lib.rs | ||
57 | pub fn foo() {} | ||
58 | |||
59 | //- tests/spam.rs | ||
60 | #[test] | ||
61 | fn test_eggs() {} | ||
62 | "#); | ||
63 | server.wait_for_feedback("workspace loaded"); | ||
64 | server.request::<Runnables>( | ||
65 | RunnablesParams { | ||
66 | text_document: server.doc_id("tests/spam.rs"), | ||
67 | position: None, | ||
68 | }, | ||
69 | r#"[ | ||
70 | { | ||
71 | "args": [ "test", "--package", "foo", "--test", "spam", "--", "test_eggs", "--nocapture" ], | ||
72 | "bin": "cargo", | ||
73 | "env": { "RUST_BACKTRACE": "short" }, | ||
74 | "label": "test test_eggs", | ||
75 | "range": { | ||
76 | "end": { "character": 17, "line": 1 }, | ||
77 | "start": { "character": 0, "line": 0 } | ||
78 | } | ||
79 | } | ||
80 | ]"# | ||
81 | ); | ||
82 | } | ||
83 | |||
84 | // #[test] | ||
85 | // fn test_deps() { | ||
86 | // let server = project(r#" | ||
87 | // //- Cargo.toml | ||
88 | // [package] | ||
89 | // name = "foo" | ||
90 | // version = "0.0.0" | ||
91 | // [dependencies] | ||
92 | // regex = "=1.0.4" | ||
93 | |||
94 | // //- src/lib.rs | ||
95 | // extern crate regex; | ||
96 | // "#); | ||
97 | // server.wait_for_feedback("workspace loaded"); | ||
98 | // server.wait_for_feedback_n("library loaded", 9); | ||
99 | // } | ||
diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs new file mode 100644 index 000000000..8fe2aa816 --- /dev/null +++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs | |||
@@ -0,0 +1,217 @@ | |||
1 | use std::{ | ||
2 | fs, | ||
3 | cell::{Cell, RefCell}, | ||
4 | path::PathBuf, | ||
5 | time::Duration, | ||
6 | sync::Once, | ||
7 | }; | ||
8 | |||
9 | use tempdir::TempDir; | ||
10 | use crossbeam_channel::{after, Receiver}; | ||
11 | use flexi_logger::Logger; | ||
12 | use languageserver_types::{ | ||
13 | Url, | ||
14 | TextDocumentIdentifier, | ||
15 | request::{Request, Shutdown}, | ||
16 | notification::DidOpenTextDocument, | ||
17 | DidOpenTextDocumentParams, | ||
18 | TextDocumentItem, | ||
19 | }; | ||
20 | use serde::Serialize; | ||
21 | use serde_json::{Value, from_str, to_string_pretty}; | ||
22 | use gen_lsp_server::{RawMessage, RawRequest, RawNotification}; | ||
23 | |||
24 | use ra_lsp_server::{main_loop, req, thread_watcher::{ThreadWatcher, Worker}}; | ||
25 | |||
26 | pub fn project(fixture: &str) -> Server { | ||
27 | static INIT: Once = Once::new(); | ||
28 | INIT.call_once(|| Logger::with_env_or_str(::LOG).start().unwrap()); | ||
29 | |||
30 | let tmp_dir = TempDir::new("test-project") | ||
31 | .unwrap(); | ||
32 | let mut buf = String::new(); | ||
33 | let mut file_name = None; | ||
34 | let mut paths = vec![]; | ||
35 | macro_rules! flush { | ||
36 | () => { | ||
37 | if let Some(file_name) = file_name { | ||
38 | let path = tmp_dir.path().join(file_name); | ||
39 | fs::create_dir_all(path.parent().unwrap()).unwrap(); | ||
40 | fs::write(path.as_path(), buf.as_bytes()).unwrap(); | ||
41 | paths.push((path, buf.clone())); | ||
42 | } | ||
43 | } | ||
44 | }; | ||
45 | for line in fixture.lines() { | ||
46 | if line.starts_with("//-") { | ||
47 | flush!(); | ||
48 | buf.clear(); | ||
49 | file_name = Some(line["//-".len()..].trim()); | ||
50 | continue; | ||
51 | } | ||
52 | buf.push_str(line); | ||
53 | buf.push('\n'); | ||
54 | } | ||
55 | flush!(); | ||
56 | Server::new(tmp_dir, paths) | ||
57 | } | ||
58 | |||
59 | pub struct Server { | ||
60 | req_id: Cell<u64>, | ||
61 | messages: RefCell<Vec<RawMessage>>, | ||
62 | dir: TempDir, | ||
63 | worker: Option<Worker<RawMessage, RawMessage>>, | ||
64 | watcher: Option<ThreadWatcher>, | ||
65 | } | ||
66 | |||
67 | impl Server { | ||
68 | fn new(dir: TempDir, files: Vec<(PathBuf, String)>) -> Server { | ||
69 | let path = dir.path().to_path_buf(); | ||
70 | let (worker, watcher) = Worker::<RawMessage, RawMessage>::spawn( | ||
71 | "test server", | ||
72 | 128, | ||
73 | move |mut msg_receiver, mut msg_sender| { | ||
74 | main_loop(true, path, &mut msg_receiver, &mut msg_sender) | ||
75 | .unwrap() | ||
76 | } | ||
77 | ); | ||
78 | let res = Server { | ||
79 | req_id: Cell::new(1), | ||
80 | dir, | ||
81 | messages: Default::default(), | ||
82 | worker: Some(worker), | ||
83 | watcher: Some(watcher), | ||
84 | }; | ||
85 | |||
86 | for (path, text) in files { | ||
87 | res.send_notification(RawNotification::new::<DidOpenTextDocument>( | ||
88 | &DidOpenTextDocumentParams { | ||
89 | text_document: TextDocumentItem { | ||
90 | uri: Url::from_file_path(path).unwrap(), | ||
91 | language_id: "rust".to_string(), | ||
92 | version: 0, | ||
93 | text, | ||
94 | } | ||
95 | } | ||
96 | )) | ||
97 | } | ||
98 | res | ||
99 | } | ||
100 | |||
101 | pub fn doc_id(&self, rel_path: &str) -> TextDocumentIdentifier { | ||
102 | let path = self.dir.path().join(rel_path); | ||
103 | TextDocumentIdentifier { | ||
104 | uri: Url::from_file_path(path).unwrap(), | ||
105 | } | ||
106 | } | ||
107 | |||
108 | pub fn request<R>( | ||
109 | &self, | ||
110 | params: R::Params, | ||
111 | expected_resp: &str, | ||
112 | ) | ||
113 | where | ||
114 | R: Request, | ||
115 | R::Params: Serialize, | ||
116 | { | ||
117 | let id = self.req_id.get(); | ||
118 | self.req_id.set(id + 1); | ||
119 | let expected_resp: Value = from_str(expected_resp).unwrap(); | ||
120 | let actual = self.send_request::<R>(id, params); | ||
121 | assert_eq!( | ||
122 | expected_resp, actual, | ||
123 | "Expected:\n{}\n\ | ||
124 | Actual:\n{}\n", | ||
125 | to_string_pretty(&expected_resp).unwrap(), | ||
126 | to_string_pretty(&actual).unwrap(), | ||
127 | ); | ||
128 | } | ||
129 | |||
130 | fn send_request<R>(&self, id: u64, params: R::Params) -> Value | ||
131 | where | ||
132 | R: Request, | ||
133 | R::Params: Serialize, | ||
134 | { | ||
135 | let r = RawRequest::new::<R>(id, ¶ms); | ||
136 | self.send_request_(r) | ||
137 | } | ||
138 | fn send_request_(&self, r: RawRequest) -> Value | ||
139 | { | ||
140 | let id = r.id; | ||
141 | self.worker.as_ref() | ||
142 | .unwrap() | ||
143 | .send(RawMessage::Request(r)); | ||
144 | while let Some(msg) = self.recv() { | ||
145 | match msg { | ||
146 | RawMessage::Request(req) => panic!("unexpected request: {:?}", req), | ||
147 | RawMessage::Notification(_) => (), | ||
148 | RawMessage::Response(res) => { | ||
149 | assert_eq!(res.id, id); | ||
150 | if let Some(err) = res.error { | ||
151 | panic!("error response: {:#?}", err); | ||
152 | } | ||
153 | return res.result.unwrap(); | ||
154 | } | ||
155 | } | ||
156 | } | ||
157 | panic!("no response"); | ||
158 | } | ||
159 | pub fn wait_for_feedback(&self, feedback: &str) { | ||
160 | self.wait_for_feedback_n(feedback, 1) | ||
161 | } | ||
162 | pub fn wait_for_feedback_n(&self, feedback: &str, n: usize) { | ||
163 | let f = |msg: &RawMessage| match msg { | ||
164 | RawMessage::Notification(n) if n.method == "internalFeedback" => { | ||
165 | return n.clone().cast::<req::InternalFeedback>() | ||
166 | .unwrap() == feedback | ||
167 | } | ||
168 | _ => false, | ||
169 | }; | ||
170 | let mut total = 0; | ||
171 | for msg in self.messages.borrow().iter() { | ||
172 | if f(msg) { | ||
173 | total += 1 | ||
174 | } | ||
175 | } | ||
176 | while total < n { | ||
177 | let msg = self.recv().expect("no response"); | ||
178 | if f(&msg) { | ||
179 | total += 1; | ||
180 | } | ||
181 | } | ||
182 | } | ||
183 | fn recv(&self) -> Option<RawMessage> { | ||
184 | recv_timeout(&self.worker.as_ref().unwrap().out) | ||
185 | .map(|msg| { | ||
186 | self.messages.borrow_mut().push(msg.clone()); | ||
187 | msg | ||
188 | }) | ||
189 | } | ||
190 | fn send_notification(&self, not: RawNotification) { | ||
191 | self.worker.as_ref() | ||
192 | .unwrap() | ||
193 | .send(RawMessage::Notification(not)); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | impl Drop for Server { | ||
198 | fn drop(&mut self) { | ||
199 | self.send_request::<Shutdown>(666, ()); | ||
200 | let receiver = self.worker.take().unwrap().stop(); | ||
201 | while let Some(msg) = recv_timeout(&receiver) { | ||
202 | drop(msg); | ||
203 | } | ||
204 | self.watcher.take() | ||
205 | .unwrap() | ||
206 | .stop() | ||
207 | .unwrap(); | ||
208 | } | ||
209 | } | ||
210 | |||
211 | fn recv_timeout(receiver: &Receiver<RawMessage>) -> Option<RawMessage> { | ||
212 | let timeout = Duration::from_secs(5); | ||
213 | select! { | ||
214 | recv(receiver, msg) => msg, | ||
215 | recv(after(timeout)) => panic!("timed out"), | ||
216 | } | ||
217 | } | ||