1 // Copyright 2017 The xi-editor Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #[macro_use]
16 extern crate serde_json;
17
18 extern crate xi_core_lib;
19 extern crate xi_rpc;
20
21 use std::io;
22
23 use xi_core_lib::test_helpers;
24 use xi_core_lib::XiCore;
25 use xi_rpc::test_utils::{make_reader, test_channel};
26 use xi_rpc::{ReadError, RpcLoop};
27
28 #[test]
29 /// Tests that the handler responds to a standard startup sequence as expected.
test_startup()30 fn test_startup() {
31 let mut state = XiCore::new();
32 let (tx, mut rx) = test_channel();
33 let mut rpc_looper = RpcLoop::new(tx);
34 let json = make_reader(
35 r#"{"method":"client_started","params":{}}
36 {"method":"set_theme","params":{"theme_name":"InspiredGitHub"}}"#,
37 );
38 assert!(rpc_looper.mainloop(|| json, &mut state).is_ok());
39 rx.expect_rpc("available_languages");
40 rx.expect_rpc("available_themes");
41 rx.expect_rpc("theme_changed");
42
43 let json = make_reader(r#"{"id":0,"method":"new_view","params":{}}"#);
44 assert!(rpc_looper.mainloop(|| json, &mut state).is_ok());
45 assert_eq!(rx.expect_response(), Ok(json!("view-id-1")));
46 rx.expect_rpc("available_plugins");
47 rx.expect_rpc("config_changed");
48 rx.expect_rpc("language_changed");
49 rx.expect_rpc("update");
50 rx.expect_rpc("scroll_to");
51 rx.expect_nothing();
52 }
53
54 #[test]
55 /// Tests that the handler creates and destroys views and buffers
test_state()56 fn test_state() {
57 let mut state = XiCore::new();
58
59 let write = io::sink();
60 let json = make_reader(
61 r#"{"method":"client_started","params":{}}
62 {"method":"set_theme","params":{"theme_name":"InspiredGitHub"}}
63 {"id":0,"method":"new_view","params":{}}"#,
64 );
65 let mut rpc_looper = RpcLoop::new(write);
66 rpc_looper.mainloop(|| json, &mut state).unwrap();
67
68 {
69 let state = state.inner();
70 assert_eq!(state._test_open_editors(), vec![test_helpers::new_buffer_id(2)]);
71 assert_eq!(state._test_open_views(), vec![test_helpers::new_view_id(1)]);
72 }
73
74 let json = make_reader(r#"{"method":"close_view","params":{"view_id":"view-id-1"}}"#);
75 rpc_looper.mainloop(|| json, &mut state).unwrap();
76 {
77 let state = state.inner();
78 assert_eq!(state._test_open_views(), Vec::new());
79 assert_eq!(state._test_open_editors(), Vec::new());
80 }
81
82 let json = make_reader(
83 r#"{"id":1,"method":"new_view","params":{}}
84 {"id":2,"method":"new_view","params":{}}
85 {"id":3,"method":"new_view","params":{}}"#,
86 );
87
88 rpc_looper.mainloop(|| json, &mut state).unwrap();
89 {
90 let state = state.inner();
91 assert_eq!(state._test_open_editors().len(), 3);
92 }
93 }
94
95 #[test]
96 /// Tests that the runloop exits with the correct error when receiving
97 /// malformed json.
test_malformed_json()98 fn test_malformed_json() {
99 let mut state = XiCore::new();
100 let write = io::sink();
101 let mut rpc_looper = RpcLoop::new(write);
102 // malformed json: method should be in quotes.
103 let read = make_reader(
104 r#"{"method":"client_started","params":{}}
105 {"id":0,method:"new_view","params":{}}"#,
106 );
107 match rpc_looper.mainloop(|| read, &mut state).err().expect("malformed json exits with error") {
108 ReadError::Json(_) => (), // expected
109 err => panic!("Unexpected error: {:?}", err),
110 }
111 // read should have ended after first item
112 {
113 let state = state.inner();
114 assert_eq!(state._test_open_editors().len(), 0);
115 }
116 }
117
118 #[test]
119 /// Sends all of the cursor movement-related commands, and verifies that
120 /// they are handled.
121 ///
122 ///
123 /// Note: this is a test of message parsing, not of editor behaviour.
test_movement_cmds()124 fn test_movement_cmds() {
125 let mut state = XiCore::new();
126 let write = io::sink();
127 let mut rpc_looper = RpcLoop::new(write);
128 // init a new view
129 let json = make_reader(
130 r#"{"method":"client_started","params":{}}
131 {"method":"set_theme","params":{"theme_name":"InspiredGitHub"}}
132 {"id":0,"method":"new_view","params":{}}"#,
133 );
134 assert!(rpc_looper.mainloop(|| json, &mut state).is_ok());
135
136 let json = make_reader(MOVEMENT_RPCS);
137 rpc_looper.mainloop(|| json, &mut state).unwrap();
138 }
139
140 #[test]
141 /// Sends all the commands which modify the buffer, and verifies that they
142 /// are handled.
test_text_commands()143 fn test_text_commands() {
144 let mut state = XiCore::new();
145 let write = io::sink();
146 let mut rpc_looper = RpcLoop::new(write);
147 // init a new view
148 let json = make_reader(
149 r#"{"method":"client_started","params":{}}
150 {"method":"set_theme","params":{"theme_name":"InspiredGitHub"}}
151 {"id":0,"method":"new_view","params":{}}"#,
152 );
153 assert!(rpc_looper.mainloop(|| json, &mut state).is_ok());
154
155 let json = make_reader(TEXT_EDIT_RPCS);
156 rpc_looper.mainloop(|| json, &mut state).unwrap();
157 }
158
159 #[test]
test_other_edit_commands()160 fn test_other_edit_commands() {
161 let mut state = XiCore::new();
162 let write = io::sink();
163 let mut rpc_looper = RpcLoop::new(write);
164 // init a new view
165 let json = make_reader(
166 r#"{"method":"client_started","params":{}}
167 {"method":"set_theme","params":{"theme_name":"InspiredGitHub"}}
168 {"id":0,"method":"new_view","params":{}}"#,
169 );
170 assert!(rpc_looper.mainloop(|| json, &mut state).is_ok());
171
172 let json = make_reader(OTHER_EDIT_RPCS);
173 rpc_looper.mainloop(|| json, &mut state).unwrap();
174 }
175
176 #[test]
test_settings_commands()177 fn test_settings_commands() {
178 let mut state = XiCore::new();
179 let (tx, mut rx) = test_channel();
180 let mut rpc_looper = RpcLoop::new(tx);
181 // init a new view
182 let json = make_reader(
183 r#"{"method":"client_started","params":{}}
184 {"method":"set_theme","params":{"theme_name":"InspiredGitHub"}}
185 {"id":0,"method":"new_view","params":{}}"#,
186 );
187 assert!(rpc_looper.mainloop(|| json, &mut state).is_ok());
188 rx.expect_rpc("available_languages");
189 rx.expect_rpc("available_themes");
190 rx.expect_rpc("theme_changed");
191 rx.expect_response().unwrap();
192 rx.expect_rpc("available_plugins");
193 rx.expect_rpc("config_changed");
194 rx.expect_rpc("language_changed");
195 rx.expect_rpc("update");
196 rx.expect_rpc("scroll_to");
197
198 let json = make_reader(r#"{"method":"get_config","id":1,"params":{"view_id":"view-id-1"}}"#);
199 rpc_looper.mainloop(|| json, &mut state).unwrap();
200 let resp = rx.expect_response().unwrap();
201 assert_eq!(resp["tab_size"], json!(4));
202
203 let json = make_reader(r#"{"method":"modify_user_config","params":{"domain":{"user_override":"view-id-1"},"changes":{"font_face": "Comic Sans"}}}
204 {"method":"modify_user_config","params":{"domain":{"syntax":"rust"},"changes":{"font_size":42}}}
205 {"method":"modify_user_config","params":{"domain":"general","changes":{"tab_size":13,"font_face":"Papyrus"}}}"#);
206 rpc_looper.mainloop(|| json, &mut state).unwrap();
207 // discard config_changed
208 rx.expect_rpc("config_changed");
209 rx.expect_rpc("config_changed");
210
211 let json = make_reader(r#"{"method":"get_config","id":2,"params":{"view_id":"view-id-1"}}"#);
212 rpc_looper.mainloop(|| json, &mut state).unwrap();
213 let resp = rx.expect_response().unwrap();
214 assert_eq!(resp["tab_size"], json!(13));
215 assert_eq!(resp["font_face"], json!("Comic Sans"));
216
217 // null value should clear entry from this config
218 let json = make_reader(r#"{"method":"modify_user_config","params":{"domain":{"user_override":"view-id-1"},"changes":{"font_face": null}}}"#);
219 rpc_looper.mainloop(|| json, &mut state).unwrap();
220 let resp = rx.expect_rpc("config_changed");
221 assert_eq!(resp.0["params"]["changes"]["font_face"], json!("Papyrus"));
222 }
223
224 //TODO: test saving rpc
225 //TODO: test plugin rpc
226
227 const MOVEMENT_RPCS: &str = r#"{"method":"edit","params":{"view_id":"view-id-1","method":"move_up","params":[]}}
228 {"method":"edit","params":{"view_id":"view-id-1","method":"move_down","params":[]}}
229 {"method":"edit","params":{"view_id":"view-id-1","method":"move_up_and_modify_selection","params":[]}}
230 {"method":"edit","params":{"view_id":"view-id-1","method":"move_down_and_modify_selection","params":[]}}
231 {"method":"edit","params":{"view_id":"view-id-1","method":"move_left","params":[]}}
232 {"method":"edit","params":{"view_id":"view-id-1","method":"move_backward","params":[]}}
233 {"method":"edit","params":{"view_id":"view-id-1","method":"move_right","params":[]}}
234 {"method":"edit","params":{"view_id":"view-id-1","method":"move_forward","params":[]}}
235 {"method":"edit","params":{"view_id":"view-id-1","method":"move_left_and_modify_selection","params":[]}}
236 {"method":"edit","params":{"view_id":"view-id-1","method":"move_right_and_modify_selection","params":[]}}
237 {"method":"edit","params":{"view_id":"view-id-1","method":"move_word_left","params":[]}}
238 {"method":"edit","params":{"view_id":"view-id-1","method":"move_word_right","params":[]}}
239 {"method":"edit","params":{"view_id":"view-id-1","method":"move_word_left_and_modify_selection","params":[]}}
240 {"method":"edit","params":{"view_id":"view-id-1","method":"move_word_right_and_modify_selection","params":[]}}
241 {"method":"edit","params":{"view_id":"view-id-1","method":"move_to_beginning_of_paragraph","params":[]}}
242 {"method":"edit","params":{"view_id":"view-id-1","method":"move_to_end_of_paragraph","params":[]}}
243 {"method":"edit","params":{"view_id":"view-id-1","method":"move_to_left_end_of_line","params":[]}}
244 {"method":"edit","params":{"view_id":"view-id-1","method":"move_to_left_end_of_line_and_modify_selection","params":[]}}
245 {"method":"edit","params":{"view_id":"view-id-1","method":"move_to_right_end_of_line","params":[]}}
246 {"method":"edit","params":{"view_id":"view-id-1","method":"move_to_right_end_of_line_and_modify_selection","params":[]}}
247 {"method":"edit","params":{"view_id":"view-id-1","method":"move_to_beginning_of_document","params":[]}}
248 {"method":"edit","params":{"view_id":"view-id-1","method":"move_to_beginning_of_document_and_modify_selection","params":[]}}
249 {"method":"edit","params":{"view_id":"view-id-1","method":"move_to_end_of_document","params":[]}}
250 {"method":"edit","params":{"view_id":"view-id-1","method":"move_to_end_of_document_and_modify_selection","params":[]}}
251 {"method":"edit","params":{"view_id":"view-id-1","method":"scroll_page_up","params":[]}}
252 {"method":"edit","params":{"view_id":"view-id-1","method":"scroll_page_down","params":[]}}
253 {"method":"edit","params":{"view_id":"view-id-1","method":"page_up_and_modify_selection","params":[]}}
254 {"method":"edit","params":{"view_id":"view-id-1","method":"page_down_and_modify_selection","params":[]}}
255 {"method":"edit","params":{"view_id":"view-id-1","method":"select_all","params":[]}}
256 {"method":"edit","params":{"view_id":"view-id-1","method":"add_selection_above","params":[]}}
257 {"method":"edit","params":{"view_id":"view-id-1","method":"add_selection_below","params":[]}}
258 {"method":"edit","params":{"view_id":"view-id-1","method":"collapse_selections","params":[]}}"#;
259
260 const TEXT_EDIT_RPCS: &str =
261 r#"{"method":"edit","params":{"view_id":"view-id-1","method":"insert","params":{"chars":"a"}}}
262 {"method":"edit","params":{"view_id":"view-id-1","method":"delete_backward","params":[]}}
263 {"method":"edit","params":{"view_id":"view-id-1","method":"delete_forward","params":[]}}
264 {"method":"edit","params":{"view_id":"view-id-1","method":"delete_word_forward","params":[]}}
265 {"method":"edit","params":{"view_id":"view-id-1","method":"delete_word_backward","params":[]}}
266 {"method":"edit","params":{"view_id":"view-id-1","method":"delete_to_end_of_paragraph","params":[]}}
267 {"method":"edit","params":{"view_id":"view-id-1","method":"insert_newline","params":[]}}
268 {"method":"edit","params":{"view_id":"view-id-1","method":"insert_tab","params":[]}}
269 {"method":"edit","params":{"view_id":"view-id-1","method":"yank","params":[]}}
270 {"method":"edit","params":{"view_id":"view-id-1","method":"undo","params":[]}}
271 {"method":"edit","params":{"view_id":"view-id-1","method":"redo","params":[]}}
272 {"method":"edit","params":{"view_id":"view-id-1","method":"transpose","params":[]}}
273 {"method":"edit","params":{"view_id":"view-id-1","method":"uppercase","params":[]}}
274 {"method":"edit","params":{"view_id":"view-id-1","method":"lowercase","params":[]}}
275 {"method":"edit","params":{"view_id":"view-id-1","method":"indent","params":[]}}
276 {"method":"edit","params":{"view_id":"view-id-1","method":"outdent","params":[]}}
277 {"method":"edit","params":{"view_id":"view-id-1","method":"duplicate_line","params":[]}}
278 {"method":"edit","params":{"view_id":"view-id-1","method":"replace_next","params":[]}}
279 {"method":"edit","params":{"view_id":"view-id-1","method":"replace_all","params":[]}}
280 {"id":2,"method":"edit","params":{"view_id":"view-id-1","method":"cut","params":[]}}"#;
281
282 const OTHER_EDIT_RPCS: &str = r#"{"method":"edit","params":{"view_id":"view-id-1","method":"scroll","params":[0,1]}}
283 {"method":"edit","params":{"view_id":"view-id-1","method":"goto_line","params":{"line":1}}}
284 {"method":"edit","params":{"view_id":"view-id-1","method":"request_lines","params":[0,1]}}
285 {"method":"edit","params":{"view_id":"view-id-1","method":"drag","params":[17,15,0]}}
286 {"method":"edit","params":{"view_id":"view-id-1","method":"gesture","params":{"line": 1, "col": 2, "ty": "toggle_sel"}}}
287 {"method":"edit","params":{"view_id":"view-id-1","method":"gesture","params":{"line": 1, "col": 2, "ty": "point_select"}}}
288 {"method":"edit","params":{"view_id":"view-id-1","method":"gesture","params":{"line": 1, "col": 2, "ty": "range_select"}}}
289 {"method":"edit","params":{"view_id":"view-id-1","method":"gesture","params":{"line": 1, "col": 2, "ty": "line_select"}}}
290 {"method":"edit","params":{"view_id":"view-id-1","method":"gesture","params":{"line": 1, "col": 2, "ty": "word_select"}}}
291 {"method":"edit","params":{"view_id":"view-id-1","method":"gesture","params":{"line": 1, "col": 2, "ty": "multi_line_select"}}}
292 {"method":"edit","params":{"view_id":"view-id-1","method":"gesture","params":{"line": 1, "col": 2, "ty": "multi_word_select"}}}
293 {"method":"edit","params":{"view_id":"view-id-1","method":"find","params":{"case_sensitive":false,"chars":"m"}}}
294 {"method":"edit","params":{"view_id":"view-id-1","method":"multi_find","params":{"queries": [{"case_sensitive":false,"chars":"m"}]}}}
295 {"method":"edit","params":{"view_id":"view-id-1","method":"find_next","params":{"wrap_around":true}}}
296 {"method":"edit","params":{"view_id":"view-id-1","method":"find_previous","params":{"wrap_around":true}}}
297 {"method":"edit","params":{"view_id":"view-id-1","method":"find_all","params":[]}}
298 {"method":"edit","params":{"view_id":"view-id-1","method":"highlight_find","params":{"visible":true}}}
299 {"method":"edit","params":{"view_id":"view-id-1","method":"selection_for_find","params":{"case_sensitive":true}}}
300 {"method":"edit","params":{"view_id":"view-id-1","method":"replace","params":{"chars":"a"}}}
301 {"method":"edit","params":{"view_id":"view-id-1","method":"selection_for_replace","params":[]}}
302 {"method":"edit","params":{"view_id":"view-id-1","method":"debug_rewrap","params":[]}}
303 {"method":"edit","params":{"view_id":"view-id-1","method":"debug_print_spans","params":[]}}
304 {"id":3,"method":"edit","params":{"view_id":"view-id-1","method":"copy","params":[]}}"#;
305