test_filecompletion.c revision 1.2.2.1 1 /* $NetBSD: test_filecompletion.c,v 1.2.2.1 2018/05/21 04:35:55 pgoyette Exp $ */
2
3 /*-
4 * Copyright (c) 2017 Abhinav Upadhyay <abhinav (at) NetBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include "config.h"
33
34 #include <assert.h>
35 #include <err.h>
36 #include <stdio.h>
37 #include <histedit.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <wchar.h>
41
42 #include "filecomplete.h"
43 #include "el.h"
44
45 typedef struct {
46 const wchar_t *user_typed_text; /* The actual text typed by the user on the terminal */
47 const char *completion_function_input ; /*the text received by fn_filename_completion_function */
48 const char *expanded_text; /* the value to which completion_function_input should be expanded */
49 const wchar_t *escaped_output; /* expected escaped value of expanded_text */
50 } test_input;
51
52 static test_input inputs[] = {
53 {
54 /* simple test for escaping angular brackets */
55 L"ls ang",
56 "ang",
57 "ang<ular>test",
58 L"ls ang\\<ular\\>test "
59 },
60 {
61 /* test angular bracket inside double quotes: ls "dq_ang */
62 L"ls \"dq_ang",
63 "dq_ang",
64 "dq_ang<ular>test",
65 L"ls \"dq_ang<ular>test\" "
66 },
67 {
68 /* test angular bracket inside singlq quotes: ls "sq_ang */
69 L"ls 'sq_ang",
70 "sq_ang",
71 "sq_ang<ular>test",
72 L"ls 'sq_ang<ular>test' "
73 },
74 {
75 /* simple test for backslash */
76 L"ls back",
77 "back",
78 "backslash\\test",
79 L"ls backslash\\\\test "
80 },
81 {
82 /* backslash inside single quotes */
83 L"ls 'sback",
84 "sback",
85 "sbackslash\\test",
86 L"ls 'sbackslash\\test' "
87 },
88 {
89 /* backslash inside double quotes */
90 L"ls \"dback",
91 "dback",
92 "dbackslash\\test",
93 L"ls \"dbackslash\\\\test\" "
94 },
95 {
96 /* test braces */
97 L"ls br",
98 "br",
99 "braces{test}",
100 L"ls braces\\{test\\} "
101 },
102 {
103 /* test braces inside single quotes */
104 L"ls 'sbr",
105 "sbr",
106 "sbraces{test}",
107 L"ls 'sbraces{test}' "
108 },
109 {
110 /* test braces inside double quotes */
111 L"ls \"dbr",
112 "dbr",
113 "dbraces{test}",
114 L"ls \"dbraces{test}\" "
115 },
116 {
117 /* test dollar */
118 L"ls doll",
119 "doll",
120 "doll$artest",
121 L"ls doll\\$artest "
122 },
123 {
124 /* test dollar inside single quotes */
125 L"ls 'sdoll",
126 "sdoll",
127 "sdoll$artest",
128 L"ls 'sdoll$artest' "
129 },
130 {
131 /* test dollar inside double quotes */
132 L"ls \"ddoll",
133 "ddoll",
134 "ddoll$artest",
135 L"ls \"ddoll\\$artest\" "
136 },
137 {
138 /* test equals */
139 L"ls eq",
140 "eq",
141 "equals==test",
142 L"ls equals\\=\\=test "
143 },
144 {
145 /* test equals inside sinqle quotes */
146 L"ls 'seq",
147 "seq",
148 "sequals==test",
149 L"ls 'sequals==test' "
150 },
151 {
152 /* test equals inside double quotes */
153 L"ls \"deq",
154 "deq",
155 "dequals==test",
156 L"ls \"dequals==test\" "
157 },
158 {
159 /* test \n */
160 L"ls new",
161 "new",
162 "new\\nline",
163 L"ls new\\\\nline "
164 },
165 {
166 /* test \n inside single quotes */
167 L"ls 'snew",
168 "snew",
169 "snew\nline",
170 L"ls 'snew\nline' "
171 },
172 {
173 /* test \n inside double quotes */
174 L"ls \"dnew",
175 "dnew",
176 "dnew\nline",
177 L"ls \"dnew\nline\" "
178 },
179 {
180 /* test single space */
181 L"ls spac",
182 "spac",
183 "space test",
184 L"ls space\\ test "
185 },
186 {
187 /* test single space inside singlq quotes */
188 L"ls 's_spac",
189 "s_spac",
190 "s_space test",
191 L"ls 's_space test' "
192 },
193 {
194 /* test single space inside double quotes */
195 L"ls \"d_spac",
196 "d_spac",
197 "d_space test",
198 L"ls \"d_space test\" "
199 },
200 {
201 /* test multiple spaces */
202 L"ls multi",
203 "multi",
204 "multi space test",
205 L"ls multi\\ space\\ \\ test "
206 },
207 {
208 /* test multiple spaces inside single quotes */
209 L"ls 's_multi",
210 "s_multi",
211 "s_multi space test",
212 L"ls 's_multi space test' "
213 },
214 {
215 /* test multiple spaces inside double quotes */
216 L"ls \"d_multi",
217 "d_multi",
218 "d_multi space test",
219 L"ls \"d_multi space test\" "
220 },
221 {
222 /* test double quotes */
223 L"ls doub",
224 "doub",
225 "doub\"quotes",
226 L"ls doub\\\"quotes "
227 },
228 {
229 /* test double quotes inside single quotes */
230 L"ls 's_doub",
231 "s_doub",
232 "s_doub\"quotes",
233 L"ls 's_doub\"quotes' "
234 },
235 {
236 /* test double quotes inside double quotes */
237 L"ls \"d_doub",
238 "d_doub",
239 "d_doub\"quotes",
240 L"ls \"d_doub\\\"quotes\" "
241 },
242 {
243 /* test multiple double quotes */
244 L"ls mud",
245 "mud",
246 "mud\"qu\"otes\"",
247 L"ls mud\\\"qu\\\"otes\\\" "
248 },
249 {
250 /* test multiple double quotes inside single quotes */
251 L"ls 'smud",
252 "smud",
253 "smud\"qu\"otes\"",
254 L"ls 'smud\"qu\"otes\"' "
255 },
256 {
257 /* test multiple double quotes inside double quotes */
258 L"ls \"dmud",
259 "dmud",
260 "dmud\"qu\"otes\"",
261 L"ls \"dmud\\\"qu\\\"otes\\\"\" "
262 },
263 {
264 /* test one single quote */
265 L"ls sing",
266 "sing",
267 "single'quote",
268 L"ls single\\'quote "
269 },
270 {
271 /* test one single quote inside single quote */
272 L"ls 'ssing",
273 "ssing",
274 "ssingle'quote",
275 L"ls 'ssingle'\\''quote' "
276 },
277 {
278 /* test one single quote inside double quote */
279 L"ls \"dsing",
280 "dsing",
281 "dsingle'quote",
282 L"ls \"dsingle'quote\" "
283 },
284 {
285 /* test multiple single quotes */
286 L"ls mu_sing",
287 "mu_sing",
288 "mu_single''quotes''",
289 L"ls mu_single\\'\\'quotes\\'\\' "
290 },
291 {
292 /* test multiple single quotes inside single quote */
293 L"ls 'smu_sing",
294 "smu_sing",
295 "smu_single''quotes''",
296 L"ls 'smu_single'\\'''\\''quotes'\\\'''\\''' "
297 },
298 {
299 /* test multiple single quotes inside double quote */
300 L"ls \"dmu_sing",
301 "dmu_sing",
302 "dmu_single''quotes''",
303 L"ls \"dmu_single''quotes''\" "
304 },
305 {
306 /* test parenthesis */
307 L"ls paren",
308 "paren",
309 "paren(test)",
310 L"ls paren\\(test\\) "
311 },
312 {
313 /* test parenthesis inside single quote */
314 L"ls 'sparen",
315 "sparen",
316 "sparen(test)",
317 L"ls 'sparen(test)' "
318 },
319 {
320 /* test parenthesis inside double quote */
321 L"ls \"dparen",
322 "dparen",
323 "dparen(test)",
324 L"ls \"dparen(test)\" "
325 },
326 {
327 /* test pipe */
328 L"ls pip",
329 "pip",
330 "pipe|test",
331 L"ls pipe\\|test "
332 },
333 {
334 /* test pipe inside single quote */
335 L"ls 'spip",
336 "spip",
337 "spipe|test",
338 L"ls 'spipe|test' ",
339 },
340 {
341 /* test pipe inside double quote */
342 L"ls \"dpip",
343 "dpip",
344 "dpipe|test",
345 L"ls \"dpipe|test\" "
346 },
347 {
348 /* test tab */
349 L"ls ta",
350 "ta",
351 "tab\ttest",
352 L"ls tab\\\ttest "
353 },
354 {
355 /* test tab inside single quote */
356 L"ls 'sta",
357 "sta",
358 "stab\ttest",
359 L"ls 'stab\ttest' "
360 },
361 {
362 /* test tab inside double quote */
363 L"ls \"dta",
364 "dta",
365 "dtab\ttest",
366 L"ls \"dtab\ttest\" "
367 },
368 {
369 /* test back tick */
370 L"ls tic",
371 "tic",
372 "tick`test`",
373 L"ls tick\\`test\\` "
374 },
375 {
376 /* test back tick inside single quote */
377 L"ls 'stic",
378 "stic",
379 "stick`test`",
380 L"ls 'stick`test`' "
381 },
382 {
383 /* test back tick inside double quote */
384 L"ls \"dtic",
385 "dtic",
386 "dtick`test`",
387 L"ls \"dtick\\`test\\`\" "
388 },
389 {
390 /* test for @ */
391 L"ls at",
392 "at",
393 "atthe@rate",
394 L"ls atthe\\@rate "
395 },
396 {
397 /* test for @ inside single quote */
398 L"ls 'sat",
399 "sat",
400 "satthe@rate",
401 L"ls 'satthe@rate' "
402 },
403 {
404 /* test for @ inside double quote */
405 L"ls \"dat",
406 "dat",
407 "datthe@rate",
408 L"ls \"datthe@rate\" "
409 },
410 {
411 /* test ; */
412 L"ls semi",
413 "semi",
414 "semi;colon;test",
415 L"ls semi\\;colon\\;test "
416 },
417 {
418 /* test ; inside single quote */
419 L"ls 'ssemi",
420 "ssemi",
421 "ssemi;colon;test",
422 L"ls 'ssemi;colon;test' "
423 },
424 {
425 /* test ; inside double quote */
426 L"ls \"dsemi",
427 "dsemi",
428 "dsemi;colon;test",
429 L"ls \"dsemi;colon;test\" "
430 },
431 {
432 /* test & */
433 L"ls amp",
434 "amp",
435 "ampers&and",
436 L"ls ampers\\&and "
437 },
438 {
439 /* test & inside single quote */
440 L"ls 'samp",
441 "samp",
442 "sampers&and",
443 L"ls 'sampers&and' "
444 },
445 {
446 /* test & inside double quote */
447 L"ls \"damp",
448 "damp",
449 "dampers&and",
450 L"ls \"dampers&and\" "
451 },
452 {
453 /* test completion when cursor at \ */
454 L"ls foo\\",
455 "foo",
456 "foo bar",
457 L"ls foo\\ bar "
458 },
459 {
460 /* test completion when cursor at single quote */
461 L"ls foo'",
462 "foo",
463 "foo bar",
464 L"ls foo\\ bar "
465 },
466 {
467 /* test completion when cursor at double quote */
468 L"ls foo\"",
469 "foo",
470 "foo bar",
471 L"ls foo\\ bar "
472 }
473 };
474
475 static const wchar_t break_chars[] = L" \t\n\"\\'`@$><=;|&{(";
476
477 /*
478 * Custom completion function passed to fn_complete.
479 * The function returns hardcoded completion matches
480 * based on the test cases present in inputs[] (above)
481 */
482 static char *
483 mycomplet_func(const char *text, int index)
484 {
485 static char *last_input = NULL;
486 size_t i = 0;
487 if (last_input && strcmp(last_input, text) == 0) {
488 free(last_input);
489 last_input = NULL;
490 return NULL;
491 }
492 last_input = strdup(text);
493
494 for (i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) {
495 if (strcmp(text, inputs[i].completion_function_input) == 0)
496 return strdup(inputs[i].expanded_text);
497 }
498
499 return NULL;
500 }
501
502 int
503 main(int argc, char **argv)
504 {
505 EditLine *el = el_init(argv[0], stdin, stdout, stderr);
506 size_t i;
507 size_t input_len;
508 el_line_t line;
509 wchar_t *buffer = malloc(64 * sizeof(*buffer));
510 if (buffer == NULL)
511 err(EXIT_FAILURE, "malloc failed");
512
513 for (i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) {
514 memset(buffer, 0, 64 * sizeof(*buffer));
515 input_len = wcslen(inputs[i].user_typed_text);
516 wmemcpy(buffer, inputs[i].user_typed_text, input_len);
517 buffer[input_len] = 0;
518 line.buffer = buffer;
519 line.cursor = line.buffer + input_len ;
520 line.lastchar = line.cursor - 1;
521 line.limit = line.buffer + 64 * sizeof(*buffer);
522 el->el_line = line;
523 fn_complete(el, mycomplet_func, NULL, break_chars, NULL, NULL, 10, NULL, NULL, NULL, NULL);
524
525 /*
526 * fn_complete would have expanded and escaped the input in el->el_line.buffer.
527 * We need to assert that it matches with the expected value in our test data
528 */
529 printf("User input: %ls\t Expected output: %ls\t Generated output: %ls\n",
530 inputs[i].user_typed_text, inputs[i].escaped_output, el->el_line.buffer);
531 assert(wcscmp(el->el_line.buffer, inputs[i].escaped_output) == 0);
532 }
533 el_end(el);
534 return 0;
535
536 }
537