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/heavy_tests/support.rs | |
parent | ba0bfeee12e19da40b5eabc8d0408639af10e96f (diff) |
rename all things
Diffstat (limited to 'crates/ra_lsp_server/tests/heavy_tests/support.rs')
-rw-r--r-- | crates/ra_lsp_server/tests/heavy_tests/support.rs | 217 |
1 files changed, 217 insertions, 0 deletions
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 | } | ||