Home | History | Annotate | Line # | Download | only in TEST
      1 /*	$NetBSD: test_filecompletion.c,v 1.5 2019/09/08 05:50:58 abhinav 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[2]; /* 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", NULL},
     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", NULL},
     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", NULL},
     72 		L"ls 'sq_ang<ular>test'"
     73 	},
     74 	{
     75 		/* simple test for backslash */
     76 		L"ls back",
     77 		"back",
     78 		{"backslash\\test", NULL},
     79 		L"ls backslash\\\\test "
     80 	},
     81 	{
     82 		/* backslash inside single quotes */
     83 		L"ls 'sback",
     84 		"sback",
     85 		{"sbackslash\\test", NULL},
     86 		L"ls 'sbackslash\\test'"
     87 	},
     88 	{
     89 		/* backslash inside double quotes */
     90 		L"ls \"dback",
     91 		"dback",
     92 		{"dbackslash\\test", NULL},
     93 		L"ls \"dbackslash\\\\test\""
     94 	},
     95 	{
     96 		/* test braces */
     97 		L"ls br",
     98 		"br",
     99 		{"braces{test}", NULL},
    100 		L"ls braces\\{test\\} "
    101 	},
    102 	{
    103 		/* test braces inside single quotes */
    104 		L"ls 'sbr",
    105 		"sbr",
    106 		{"sbraces{test}", NULL},
    107 		L"ls 'sbraces{test}'"
    108 	},
    109 	{
    110 		/* test braces inside double quotes */
    111 		L"ls \"dbr",
    112 		"dbr",
    113 		{"dbraces{test}", NULL},
    114 		L"ls \"dbraces{test}\""
    115 	},
    116 	{
    117 		/* test dollar */
    118 		L"ls doll",
    119 		"doll",
    120 		{"doll$artest", NULL},
    121 		L"ls doll\\$artest "
    122 	},
    123 	{
    124 		/* test dollar inside single quotes */
    125 		L"ls 'sdoll",
    126 		"sdoll",
    127 		{"sdoll$artest", NULL},
    128 		L"ls 'sdoll$artest'"
    129 	},
    130 	{
    131 		/* test dollar inside double quotes */
    132 		L"ls \"ddoll",
    133 		"ddoll",
    134 		{"ddoll$artest", NULL},
    135 		L"ls \"ddoll\\$artest\""
    136 	},
    137 	{
    138 		/* test equals */
    139 		L"ls eq",
    140 		"eq",
    141 		{"equals==test", NULL},
    142 		L"ls equals\\=\\=test "
    143 	},
    144 	{
    145 		/* test equals inside sinqle quotes */
    146 		L"ls 'seq",
    147 		"seq",
    148 		{"sequals==test", NULL},
    149 		L"ls 'sequals==test'"
    150 	},
    151 	{
    152 		/* test equals inside double quotes */
    153 		L"ls \"deq",
    154 		"deq",
    155 		{"dequals==test", NULL},
    156 		L"ls \"dequals==test\""
    157 	},
    158 	{
    159 		/* test \n */
    160 		L"ls new",
    161 		"new",
    162 		{"new\\nline", NULL},
    163 		L"ls new\\\\nline "
    164 	},
    165 	{
    166 		/* test \n inside single quotes */
    167 		L"ls 'snew",
    168 		"snew",
    169 		{"snew\nline", NULL},
    170 		L"ls 'snew\nline'"
    171 	},
    172 	{
    173 		/* test \n inside double quotes */
    174 		L"ls \"dnew",
    175 		"dnew",
    176 		{"dnew\nline", NULL},
    177 		L"ls \"dnew\nline\""
    178 	},
    179 	{
    180 		/* test single space */
    181 		L"ls spac",
    182 		"spac",
    183 		{"space test", NULL},
    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", NULL},
    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", NULL},
    198 		L"ls \"d_space test\""
    199 	},
    200 	{
    201 		/* test multiple spaces */
    202 		L"ls multi",
    203 		"multi",
    204 		{"multi space  test", NULL},
    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", NULL},
    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", NULL},
    219 		L"ls \"d_multi space  test\""
    220 	},
    221 	{
    222 		/* test double quotes */
    223 		L"ls doub",
    224 		"doub",
    225 		{"doub\"quotes", NULL},
    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", NULL},
    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", NULL},
    240 		L"ls \"d_doub\\\"quotes\""
    241 	},
    242 	{
    243 		/* test multiple double quotes */
    244 		L"ls mud",
    245 		"mud",
    246 		{"mud\"qu\"otes\"", NULL},
    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\"", NULL},
    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\"", NULL},
    261 		L"ls \"dmud\\\"qu\\\"otes\\\"\""
    262 	},
    263 	{
    264 		/* test one single quote */
    265 		L"ls sing",
    266 		"sing",
    267 		{"single'quote", NULL},
    268 		L"ls single\\'quote "
    269 	},
    270 	{
    271 		/* test one single quote inside single quote */
    272 		L"ls 'ssing",
    273 		"ssing",
    274 		{"ssingle'quote", NULL},
    275 		L"ls 'ssingle'\\''quote'"
    276 	},
    277 	{
    278 		/* test one single quote inside double quote */
    279 		L"ls \"dsing",
    280 		"dsing",
    281 		{"dsingle'quote", NULL},
    282 		L"ls \"dsingle'quote\""
    283 	},
    284 	{
    285 		/* test multiple single quotes */
    286 		L"ls mu_sing",
    287 		"mu_sing",
    288 		{"mu_single''quotes''", NULL},
    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''", NULL},
    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''", NULL},
    303 		L"ls \"dmu_single''quotes''\""
    304 	},
    305 	{
    306 		/* test parenthesis */
    307 		L"ls paren",
    308 		"paren",
    309 		{"paren(test)", NULL},
    310 		L"ls paren\\(test\\) "
    311 	},
    312 	{
    313 		/* test parenthesis inside single quote */
    314 		L"ls 'sparen",
    315 		"sparen",
    316 		{"sparen(test)", NULL},
    317 		L"ls 'sparen(test)'"
    318 	},
    319 	{
    320 		/* test parenthesis inside double quote */
    321 		L"ls \"dparen",
    322 		"dparen",
    323 		{"dparen(test)", NULL},
    324 		L"ls \"dparen(test)\""
    325 	},
    326 	{
    327 		/* test pipe */
    328 		L"ls pip",
    329 		"pip",
    330 		{"pipe|test", NULL},
    331 		L"ls pipe\\|test "
    332 	},
    333 	{
    334 		/* test pipe inside single quote */
    335 		L"ls 'spip",
    336 		"spip",
    337 		{"spipe|test", NULL},
    338 		L"ls 'spipe|test'",
    339 	},
    340 	{
    341 		/* test pipe inside double quote */
    342 		L"ls \"dpip",
    343 		"dpip",
    344 		{"dpipe|test", NULL},
    345 		L"ls \"dpipe|test\""
    346 	},
    347 	{
    348 		/* test tab */
    349 		L"ls ta",
    350 		"ta",
    351 		{"tab\ttest", NULL},
    352 		L"ls tab\\\ttest "
    353 	},
    354 	{
    355 		/* test tab inside single quote */
    356 		L"ls 'sta",
    357 		"sta",
    358 		{"stab\ttest", NULL},
    359 		L"ls 'stab\ttest'"
    360 	},
    361 	{
    362 		/* test tab inside double quote */
    363 		L"ls \"dta",
    364 		"dta",
    365 		{"dtab\ttest", NULL},
    366 		L"ls \"dtab\ttest\""
    367 	},
    368 	{
    369 		/* test back tick */
    370 		L"ls tic",
    371 		"tic",
    372 		{"tick`test`", NULL},
    373 		L"ls tick\\`test\\` "
    374 	},
    375 	{
    376 		/* test back tick inside single quote */
    377 		L"ls 'stic",
    378 		"stic",
    379 		{"stick`test`", NULL},
    380 		L"ls 'stick`test`'"
    381 	},
    382 	{
    383 		/* test back tick inside double quote */
    384 		L"ls \"dtic",
    385 		"dtic",
    386 		{"dtick`test`", NULL},
    387 		L"ls \"dtick\\`test\\`\""
    388 	},
    389 	{
    390 		/* test for @ */
    391 		L"ls at",
    392 		"at",
    393 		{"atthe@rate", NULL},
    394 		L"ls atthe\\@rate "
    395 	},
    396 	{
    397 		/* test for @ inside single quote */
    398 		L"ls 'sat",
    399 		"sat",
    400 		{"satthe@rate", NULL},
    401 		L"ls 'satthe@rate'"
    402 	},
    403 	{
    404 		/* test for @ inside double quote */
    405 		L"ls \"dat",
    406 		"dat",
    407 		{"datthe@rate", NULL},
    408 		L"ls \"datthe@rate\""
    409 	},
    410 	{
    411 		/* test ; */
    412 		L"ls semi",
    413 		"semi",
    414 		{"semi;colon;test", NULL},
    415 		L"ls semi\\;colon\\;test "
    416 	},
    417 	{
    418 		/* test ; inside single quote */
    419 		L"ls 'ssemi",
    420 		"ssemi",
    421 		{"ssemi;colon;test", NULL},
    422 		L"ls 'ssemi;colon;test'"
    423 	},
    424 	{
    425 		/* test ; inside double quote */
    426 		L"ls \"dsemi",
    427 		"dsemi",
    428 		{"dsemi;colon;test", NULL},
    429 		L"ls \"dsemi;colon;test\""
    430 	},
    431 	{
    432 		/* test & */
    433 		L"ls amp",
    434 		"amp",
    435 		{"ampers&and", NULL},
    436 		L"ls ampers\\&and "
    437 	},
    438 	{
    439 		/* test & inside single quote */
    440 		L"ls 'samp",
    441 		"samp",
    442 		{"sampers&and", NULL},
    443 		L"ls 'sampers&and'"
    444 	},
    445 	{
    446 		/* test & inside double quote */
    447 		L"ls \"damp",
    448 		"damp",
    449 		{"dampers&and", NULL},
    450 		L"ls \"dampers&and\""
    451 	},
    452 	{
    453 		/* test completion when cursor at \ */
    454 		L"ls foo\\",
    455 		"foo",
    456 		{"foo bar", NULL},
    457 		L"ls foo\\ bar "
    458 	},
    459 	{
    460 		/* test completion when cursor at single quote */
    461 		L"ls foo'",
    462 		"foo'",
    463 		{"foo bar", NULL},
    464 		L"ls foo\\ bar "
    465 	},
    466 	{
    467 		/* test completion when cursor at double quote */
    468 		L"ls foo\"",
    469 		"foo\"",
    470 		{"foo bar", NULL},
    471 		L"ls foo\\ bar "
    472 	},
    473 	{
    474 		/* test multiple completion matches */
    475 		L"ls fo",
    476 		"fo",
    477 		{"foo bar", "foo baz"},
    478 		L"ls foo\\ ba"
    479 	},
    480 	{
    481 		L"ls ba",
    482 		"ba",
    483 		{"bar <bar>", "bar <baz>"},
    484 		L"ls bar\\ \\<ba"
    485 	}
    486 };
    487 
    488 static const wchar_t break_chars[] = L" \t\n\"\\'`@$><=;|&{(";
    489 
    490 /*
    491  * Custom completion function passed to fn_complet, NULLe.
    492  * The function returns hardcoded completion matches
    493  * based on the test cases present in inputs[] (above)
    494  */
    495 static char *
    496 mycomplet_func(const char *text, int index)
    497 {
    498 	static int last_index = 0;
    499 	size_t i = 0;
    500 	if (last_index == 2) {
    501 		last_index = 0;
    502 		return NULL;
    503 	}
    504 
    505 	for (i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) {
    506 		if (strcmp(text, inputs[i].completion_function_input) == 0) {
    507 			if (inputs[i].expanded_text[last_index] != NULL)
    508 				return strdup(inputs[i].expanded_text[last_index++]);
    509 			else {
    510 				last_index = 0;
    511 				return NULL;
    512 			}
    513 		}
    514 	}
    515 
    516 	return NULL;
    517 }
    518 
    519 int
    520 main(int argc, char **argv)
    521 {
    522 	EditLine *el = el_init(argv[0], stdin, stdout, stderr);
    523 	size_t i;
    524 	size_t input_len;
    525 	el_line_t line;
    526 	wchar_t *buffer = malloc(64 * sizeof(*buffer));
    527 	if (buffer == NULL)
    528 		err(EXIT_FAILURE, "malloc failed");
    529 
    530 	for (i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) {
    531 		memset(buffer, 0, 64 * sizeof(*buffer));
    532 		input_len = wcslen(inputs[i].user_typed_text);
    533 		wmemcpy(buffer, inputs[i].user_typed_text, input_len);
    534 		buffer[input_len] = 0;
    535 		line.buffer = buffer;
    536 		line.cursor = line.buffer + input_len ;
    537 		line.lastchar = line.cursor - 1;
    538 		line.limit = line.buffer + 64 * sizeof(*buffer);
    539 		el->el_line = line;
    540 		fn_complete(el, mycomplet_func, NULL, break_chars, NULL, NULL, 10, NULL, NULL, NULL, NULL);
    541 
    542 		/*
    543 		 * fn_complete would have expanded and escaped the input in el->el_line.buffer.
    544 		 * We need to assert that it matches with the expected value in our test data
    545 		 */
    546 		printf("User input: %ls\t Expected output: %ls\t Generated output: %ls\n",
    547 				inputs[i].user_typed_text, inputs[i].escaped_output, el->el_line.buffer);
    548 		assert(wcscmp(el->el_line.buffer, inputs[i].escaped_output) == 0);
    549 	}
    550 	el_end(el);
    551 	return 0;
    552 
    553 }
    554