diff options
Diffstat (limited to 'crates/server/tests/heavy_tests/support.rs')
-rw-r--r-- | crates/server/tests/heavy_tests/support.rs | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/crates/server/tests/heavy_tests/support.rs b/crates/server/tests/heavy_tests/support.rs new file mode 100644 index 000000000..113ef4c54 --- /dev/null +++ b/crates/server/tests/heavy_tests/support.rs | |||
@@ -0,0 +1,169 @@ | |||
1 | use std::{ | ||
2 | fs, | ||
3 | thread, | ||
4 | cell::Cell, | ||
5 | path::PathBuf, | ||
6 | }; | ||
7 | |||
8 | use tempdir::TempDir; | ||
9 | use crossbeam_channel::{bounded, Sender, Receiver}; | ||
10 | use flexi_logger::Logger; | ||
11 | use languageserver_types::{ | ||
12 | Url, | ||
13 | TextDocumentIdentifier, | ||
14 | request::{Request, Shutdown}, | ||
15 | notification::DidOpenTextDocument, | ||
16 | DidOpenTextDocumentParams, | ||
17 | TextDocumentItem, | ||
18 | }; | ||
19 | use serde::Serialize; | ||
20 | use serde_json::{Value, from_str, to_string_pretty}; | ||
21 | use gen_lsp_server::{RawMessage, RawRequest, RawNotification}; | ||
22 | |||
23 | use m::{Result, main_loop}; | ||
24 | |||
25 | pub fn project(fixture: &str) -> Server { | ||
26 | Logger::with_env_or_str("").start().unwrap(); | ||
27 | |||
28 | let tmp_dir = TempDir::new("test-project") | ||
29 | .unwrap(); | ||
30 | let mut buf = String::new(); | ||
31 | let mut file_name = None; | ||
32 | let mut paths = vec![]; | ||
33 | macro_rules! flush { | ||
34 | () => { | ||
35 | if let Some(file_name) = file_name { | ||
36 | let path = tmp_dir.path().join(file_name); | ||
37 | fs::write(path.as_path(), buf.as_bytes()).unwrap(); | ||
38 | paths.push((path, buf.clone())); | ||
39 | } | ||
40 | } | ||
41 | }; | ||
42 | for line in fixture.lines() { | ||
43 | if line.starts_with("//-") { | ||
44 | flush!(); | ||
45 | buf.clear(); | ||
46 | file_name = Some(line["//-".len()..].trim()); | ||
47 | continue; | ||
48 | } | ||
49 | buf.push_str(line); | ||
50 | buf.push('\n'); | ||
51 | } | ||
52 | flush!(); | ||
53 | |||
54 | Server::new(tmp_dir, paths) | ||
55 | } | ||
56 | |||
57 | pub struct Server { | ||
58 | req_id: Cell<u64>, | ||
59 | dir: TempDir, | ||
60 | sender: Option<Sender<RawMessage>>, | ||
61 | receiver: Receiver<RawMessage>, | ||
62 | server: Option<thread::JoinHandle<Result<()>>>, | ||
63 | } | ||
64 | |||
65 | impl Server { | ||
66 | fn new(dir: TempDir, files: Vec<(PathBuf, String)>) -> Server { | ||
67 | let path = dir.path().to_path_buf(); | ||
68 | let (client_sender, mut server_receiver) = bounded(1); | ||
69 | let (mut server_sender, client_receiver) = bounded(1); | ||
70 | let server = thread::spawn(move || main_loop(path, &mut server_receiver, &mut server_sender)); | ||
71 | let res = Server { | ||
72 | req_id: Cell::new(1), | ||
73 | dir, | ||
74 | sender: Some(client_sender), | ||
75 | receiver: client_receiver, | ||
76 | server: Some(server), | ||
77 | }; | ||
78 | for (path, text) in files { | ||
79 | res.send_notification(RawNotification::new::<DidOpenTextDocument>( | ||
80 | DidOpenTextDocumentParams { | ||
81 | text_document: TextDocumentItem { | ||
82 | uri: Url::from_file_path(path).unwrap(), | ||
83 | language_id: "rust".to_string(), | ||
84 | version: 0, | ||
85 | text, | ||
86 | } | ||
87 | } | ||
88 | )) | ||
89 | } | ||
90 | res | ||
91 | } | ||
92 | |||
93 | pub fn doc_id(&self, rel_path: &str) -> TextDocumentIdentifier { | ||
94 | let path = self.dir.path().join(rel_path); | ||
95 | TextDocumentIdentifier { | ||
96 | uri: Url::from_file_path(path).unwrap(), | ||
97 | } | ||
98 | } | ||
99 | |||
100 | pub fn request<R>( | ||
101 | &self, | ||
102 | params: R::Params, | ||
103 | expected_resp: &str, | ||
104 | ) | ||
105 | where | ||
106 | R: Request, | ||
107 | R::Params: Serialize, | ||
108 | { | ||
109 | let id = self.req_id.get(); | ||
110 | self.req_id.set(id + 1); | ||
111 | let expected_resp: Value = from_str(expected_resp).unwrap(); | ||
112 | let actual = self.send_request::<R>(id, params); | ||
113 | assert_eq!( | ||
114 | expected_resp, actual, | ||
115 | "Expected:\n{}\n\ | ||
116 | Actual:\n{}\n", | ||
117 | to_string_pretty(&expected_resp).unwrap(), | ||
118 | to_string_pretty(&actual).unwrap(), | ||
119 | ); | ||
120 | } | ||
121 | |||
122 | fn send_request<R>(&self, id: u64, params: R::Params) -> Value | ||
123 | where | ||
124 | R: Request, | ||
125 | R::Params: Serialize, | ||
126 | { | ||
127 | let r = RawRequest::new::<R>(id, params); | ||
128 | self.sender.as_ref() | ||
129 | .unwrap() | ||
130 | .send(RawMessage::Request(r)); | ||
131 | |||
132 | while let Some(msg) = self.receiver.recv() { | ||
133 | match msg { | ||
134 | RawMessage::Request(req) => panic!("unexpected request: {:?}", req), | ||
135 | RawMessage::Notification(_) => (), | ||
136 | RawMessage::Response(res) => { | ||
137 | assert_eq!(res.id, id); | ||
138 | if let Some(err) = res.error { | ||
139 | panic!("error response: {:#?}", err); | ||
140 | } | ||
141 | return res.result.unwrap(); | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | panic!("no response"); | ||
146 | } | ||
147 | fn send_notification(&self, not: RawNotification) { | ||
148 | |||
149 | self.sender.as_ref() | ||
150 | .unwrap() | ||
151 | .send(RawMessage::Notification(not)); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | impl Drop for Server { | ||
156 | fn drop(&mut self) { | ||
157 | { | ||
158 | self.send_request::<Shutdown>(666, ()); | ||
159 | drop(self.sender.take().unwrap()); | ||
160 | while let Some(msg) = self.receiver.recv() { | ||
161 | drop(msg); | ||
162 | } | ||
163 | } | ||
164 | eprintln!("joining server"); | ||
165 | self.server.take() | ||
166 | .unwrap() | ||
167 | .join().unwrap().unwrap(); | ||
168 | } | ||
169 | } | ||