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