aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server/tests/heavy_tests
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-09-16 10:54:24 +0100
committerAleksey Kladov <[email protected]>2018-09-16 11:07:39 +0100
commitb5021411a84822cb3f1e3aeffad9550dd15bdeb6 (patch)
tree9dca564f8e51b298dced01c4ce669c756dce3142 /crates/ra_lsp_server/tests/heavy_tests
parentba0bfeee12e19da40b5eabc8d0408639af10e96f (diff)
rename all things
Diffstat (limited to 'crates/ra_lsp_server/tests/heavy_tests')
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/main.rs99
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/support.rs217
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]
2extern crate crossbeam_channel;
3extern crate tempdir;
4extern crate languageserver_types;
5extern crate serde;
6extern crate serde_json;
7extern crate gen_lsp_server;
8extern crate flexi_logger;
9extern crate ra_lsp_server;
10
11mod support;
12
13use ra_lsp_server::req::{Runnables, RunnablesParams};
14
15use support::project;
16
17
18const LOG: &'static str = "";
19
20#[test]
21fn test_runnables_no_project() {
22 let server = project(r"
23//- lib.rs
24#[test]
25fn 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]
49fn test_runnables_project() {
50 let server = project(r#"
51//- Cargo.toml
52[package]
53name = "foo"
54version = "0.0.0"
55
56//- src/lib.rs
57pub fn foo() {}
58
59//- tests/spam.rs
60#[test]
61fn 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 @@
1use std::{
2 fs,
3 cell::{Cell, RefCell},
4 path::PathBuf,
5 time::Duration,
6 sync::Once,
7};
8
9use tempdir::TempDir;
10use crossbeam_channel::{after, Receiver};
11use flexi_logger::Logger;
12use languageserver_types::{
13 Url,
14 TextDocumentIdentifier,
15 request::{Request, Shutdown},
16 notification::DidOpenTextDocument,
17 DidOpenTextDocumentParams,
18 TextDocumentItem,
19};
20use serde::Serialize;
21use serde_json::{Value, from_str, to_string_pretty};
22use gen_lsp_server::{RawMessage, RawRequest, RawNotification};
23
24use ra_lsp_server::{main_loop, req, thread_watcher::{ThreadWatcher, Worker}};
25
26pub 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
59pub 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
67impl 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, &params);
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
197impl 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
211fn 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}