Home | History | Annotate | Line # | Download | only in indent
t_errors.sh revision 1.32
      1 #! /bin/sh
      2 # $NetBSD: t_errors.sh,v 1.32 2023/06/04 22:20:04 rillig Exp $
      3 #
      4 # Copyright (c) 2021 The NetBSD Foundation, Inc.
      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 # 1. Redistributions of source code must retain the above copyright
     11 #    notice, this list of conditions and the following disclaimer.
     12 # 2. Redistributions in binary form must reproduce the above copyright
     13 #    notice, this list of conditions and the following disclaimer in the
     14 #    documentation and/or other materials provided with the distribution.
     15 #
     16 # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17 # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19 # PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26 # POSSIBILITY OF SUCH DAMAGE.
     27 
     28 # Tests for error handling in indent.
     29 
     30 indent=$(atf_config_get usr.bin.indent.test_indent /usr/bin/indent)
     31 
     32 expect_error()
     33 {
     34 	local msg
     35 
     36 	msg="$1"
     37 	shift
     38 
     39 	atf_check -s 'exit:1' \
     40 	    -e "inline:$msg\n" \
     41 	    "$indent" "$@"
     42 }
     43 
     44 atf_test_case 'option_unknown'
     45 option_unknown_body()
     46 {
     47 	expect_error \
     48 	    'indent: Command line: unknown option "-Z-unknown"' \
     49 	    -Z-unknown
     50 }
     51 
     52 atf_test_case 'option_bool_trailing_garbage'
     53 option_bool_trailing_garbage_body()
     54 {
     55 	expect_error \
     56 	    'indent: Command line: unknown option "-bacchus"' \
     57 	    -bacchus
     58 }
     59 
     60 atf_test_case 'option_int_missing_argument'
     61 option_int_missing_argument_body()
     62 {
     63 	expect_error \
     64 	    'indent: Command line: argument "x" to option "-ts" must be an integer' \
     65 	    -tsx
     66 }
     67 
     68 atf_test_case 'option_profile_not_found'
     69 option_profile_not_found_body()
     70 {
     71 	expect_error \
     72 	    'indent: profile ./nonexistent: No such file or directory' \
     73 	    -P./nonexistent
     74 }
     75 
     76 atf_test_case 'option_typedefs_not_found'
     77 option_typedefs_not_found_body()
     78 {
     79 	expect_error \
     80 	    'indent: cannot open file ./nonexistent' \
     81 	    -U./nonexistent
     82 }
     83 
     84 atf_test_case 'option_tabsize_negative'
     85 option_tabsize_negative_body()
     86 {
     87 	expect_error \
     88 	    'indent: Command line: argument "-1" to option "-ts" must be between 1 and 80' \
     89 	    -ts-1
     90 }
     91 
     92 atf_test_case 'option_tabsize_zero'
     93 option_tabsize_zero_body()
     94 {
     95 	expect_error \
     96 	    'indent: Command line: argument "0" to option "-ts" must be between 1 and 80' \
     97 	    -ts0
     98 }
     99 
    100 atf_test_case 'option_tabsize_large'
    101 option_tabsize_large_body()
    102 {
    103 	expect_error \
    104 	    'indent: Command line: argument "81" to option "-ts" must be between 1 and 80' \
    105 	    -ts81
    106 }
    107 
    108 atf_test_case 'option_tabsize_very_large'
    109 option_tabsize_very_large_body()
    110 {
    111 	# Integer overflow, on both ILP32 and LP64 platforms.
    112 	expect_error \
    113 	    'indent: Command line: argument "3000000000" to option "-ts" must be between 1 and 80' \
    114 	    -ts3000000000
    115 }
    116 
    117 atf_test_case 'option_indent_size_zero'
    118 option_indent_size_zero_body()
    119 {
    120 	expect_error \
    121 	    'indent: Command line: argument "0" to option "-i" must be between 1 and 80' \
    122 	    -i0
    123 }
    124 
    125 atf_test_case 'option_int_trailing_garbage'
    126 option_int_trailing_garbage_body()
    127 {
    128 	expect_error \
    129 	    'indent: Command line: argument "3garbage" to option "-i" must be an integer' \
    130 	    -i3garbage
    131 }
    132 
    133 atf_test_case 'option_cli_trailing_garbage'
    134 option_cli_trailing_garbage_body()
    135 {
    136 	expect_error \
    137 	    'indent: Command line: argument "3garbage" to option "-cli" must be numeric' \
    138 	    -cli3garbage
    139 }
    140 
    141 atf_test_case 'option_npro_trailing_garbage'
    142 option_npro_trailing_garbage_body()
    143 {
    144 	atf_check -s 'exit:1' \
    145 	    -e 'inline:indent: Command line: unknown option "-npro-garbage"\n' \
    146 	    "$indent" -npro-garbage
    147 }
    148 
    149 atf_test_case 'option_st_trailing_garbage'
    150 option_st_trailing_garbage_body()
    151 {
    152 	atf_check -s 'exit:1' \
    153 	    -e 'inline:indent: Command line: unknown option "-stdio"\n' \
    154 	    "$indent" -stdio
    155 }
    156 
    157 atf_test_case 'option_version_trailing_garbage'
    158 option_version_trailing_garbage_body()
    159 {
    160 	atf_check -s 'exit:1' \
    161 	    -e 'inline:indent: Command line: unknown option "--version-dump"\n' \
    162 	    "$indent" --version-dump
    163 }
    164 
    165 atf_test_case 'option_buffer_overflow'
    166 option_buffer_overflow_body()
    167 {
    168 	opt='12345678123456781234567812345678'	# 32
    169 	opt="$opt$opt$opt$opt$opt$opt$opt$opt"	# 256
    170 	opt="$opt$opt$opt$opt$opt$opt$opt$opt"	# 2048
    171 	opt="$opt$opt$opt$opt$opt$opt$opt$opt"	# 16384
    172 	printf '%s\n' "-$opt" > indent.pro
    173 
    174 	expect_error \
    175 	    'indent: buffer overflow in indent.pro, starting with '\''-123456781'\''' \
    176 	    -Pindent.pro
    177 }
    178 
    179 atf_test_case 'option_special_missing_param'
    180 option_special_missing_param_body()
    181 {
    182 	expect_error \
    183 	    'indent: Command line: option "-cli" requires an argument' \
    184 	    -cli
    185 
    186 	expect_error \
    187 	    'indent: Command line: option "-T" requires an argument' \
    188 	    -T
    189 
    190 	expect_error \
    191 	    'indent: Command line: option "-U" requires an argument' \
    192 	    -U
    193 }
    194 
    195 atf_test_case 'unterminated_comment_wrap'
    196 unterminated_comment_wrap_body()
    197 {
    198 	echo '/*' > comment.c
    199 
    200 	atf_check -s 'exit:1' \
    201 	    -o 'inline:/*\n *\n' \
    202 	    -e 'inline:error: Standard Input:2: Unterminated comment\n' \
    203 	    "$indent" -st < comment.c
    204 }
    205 
    206 atf_test_case 'unterminated_comment_nowrap'
    207 unterminated_comment_nowrap_body()
    208 {
    209 	echo '/*-' > comment.c
    210 
    211 	atf_check -s 'exit:1' \
    212 	    -o 'inline:/*-\n\n' \
    213 	    -e 'inline:error: Standard Input:2: Unterminated comment\n' \
    214 	    "$indent" -st < comment.c
    215 }
    216 
    217 atf_test_case 'unterminated_char_constant'
    218 unterminated_char_constant_body()
    219 {
    220 	echo "char ch = 'x" > char.c
    221 
    222 	atf_check -s 'exit:1' \
    223 	    -o "inline:char ch = 'x\n" \
    224 	    -e 'inline:error: Standard Input:1: Unterminated literal\n' \
    225 	    "$indent" -st -di0 < char.c
    226 }
    227 
    228 atf_test_case 'unterminated_string_literal'
    229 unterminated_string_literal_body()
    230 {
    231 	echo 'const char str[] = "x' > string.c
    232 
    233 	atf_check -s 'exit:1' \
    234 	    -o 'inline:const char str[] = "x\n' \
    235 	    -e 'inline:error: Standard Input:1: Unterminated literal\n' \
    236 	    "$indent" -st -di0 < string.c
    237 }
    238 
    239 atf_test_case 'in_place_wrong_backup'
    240 in_place_wrong_backup_body()
    241 {
    242 	cat <<-\EOF > code.c
    243 		int decl;
    244 	EOF
    245 	cp code.c code.c.orig
    246 
    247 	# Due to the strange backup suffix '/subdir', indent tries to create
    248 	# a file named 'code.c/subdir', but 'code.c' is already a regular
    249 	# file, not a directory.
    250 	atf_check -s 'exit:1' \
    251 	    -e 'inline:indent: code.c/subdir: Not a directory\n' \
    252 	    env SIMPLE_BACKUP_SUFFIX="/subdir" "$indent" code.c
    253 
    254 	# Since there was an early error, the original file is kept as is.
    255 	atf_check -o 'file:code.c.orig' \
    256 	    cat code.c
    257 }
    258 
    259 atf_test_case 'argument_input_enoent'
    260 argument_input_enoent_body()
    261 {
    262 	atf_check -s 'exit:1' \
    263 	    -e 'inline:indent: ./nonexistent.c: No such file or directory\n' \
    264 	    "$indent" ./nonexistent.c
    265 }
    266 
    267 atf_test_case 'argument_output_equals_input_name'
    268 argument_output_equals_input_name_body()
    269 {
    270 	echo '/* comment */' > code.c
    271 
    272 	atf_check -s 'exit:1' \
    273 	    -e 'inline:indent: input and output files must be different\n' \
    274 	    "$indent" code.c code.c
    275 }
    276 
    277 atf_test_case 'argument_output_equals_input_file'
    278 argument_output_equals_input_file_body()
    279 {
    280 	echo '/* comment */' > code.c
    281 
    282 	atf_check \
    283 	    "$indent" code.c ./code.c
    284 
    285 	# Oops, the file has become empty since the output is first emptied,
    286 	# before reading any of the input.
    287 	atf_check \
    288 	    cat code.c
    289 }
    290 
    291 atf_test_case 'argument_output_enoent'
    292 argument_output_enoent_body()
    293 {
    294 	expect_error \
    295 	    'indent: subdir/nonexistent.c: No such file or directory' \
    296 	    /dev/null subdir/nonexistent.c
    297 }
    298 
    299 atf_test_case 'argument_too_many'
    300 argument_too_many_body()
    301 {
    302 	echo '/* comment */' > arg1.c
    303 
    304 	expect_error \
    305 	    'indent: too many arguments: arg3.c' \
    306 	    arg1.c arg2.c arg3.c arg4.c
    307 }
    308 
    309 atf_test_case 'unexpected_end_of_file'
    310 unexpected_end_of_file_body()
    311 {
    312 	echo 'struct{' > code.c
    313 
    314 	expect_error \
    315 	    'error: code.c:1: Stuff missing from end of file' \
    316 	    code.c
    317 
    318 	atf_check \
    319 	    -o 'inline:struct {\n' \
    320 	    cat code.c
    321 }
    322 
    323 atf_test_case 'unexpected_closing_brace_top_level'
    324 unexpected_closing_brace_top_level_body()
    325 {
    326 	echo '}' > code.c
    327 
    328 	expect_error \
    329 	    'error: code.c:1: Statement nesting error' \
    330 	    code.c
    331 	atf_check \
    332 	    -o 'inline:}\n' \
    333 	    cat code.c
    334 }
    335 
    336 atf_test_case 'unexpected_closing_brace_decl'
    337 unexpected_closing_brace_decl_body()
    338 {
    339 	echo 'int i = 3};' > code.c
    340 
    341 	expect_error \
    342 	    'error: code.c:1: Statement nesting error' \
    343 	    code.c
    344 	# Despite the error message, the original file got overwritten with a
    345 	# best-effort rewrite of the code.
    346 	atf_check \
    347 	    -o 'inline:int		i = 3};\n' \
    348 	    cat code.c
    349 }
    350 
    351 atf_test_case 'preprocessing_overflow'
    352 preprocessing_overflow_body()
    353 {
    354 	cat <<-\EOF > code.c
    355 		#if 1
    356 		#if 2
    357 		#if 3
    358 		#if 4
    359 		#if 5
    360 		#if 6
    361 		#endif 6
    362 		#endif 5
    363 		#endif 4
    364 		#endif 3
    365 		#endif 2
    366 		#endif 1
    367 		#endif too much
    368 	EOF
    369 	cat <<-\EOF > stderr.exp
    370 		error: code.c:6: #if stack overflow
    371 		error: code.c:12: Unmatched #endif
    372 		error: code.c:13: Unmatched #endif
    373 	EOF
    374 
    375 	atf_check -s 'exit:1' \
    376 	    -e 'file:stderr.exp' \
    377 	    "$indent" code.c
    378 }
    379 
    380 atf_test_case 'preprocessing_unrecognized'
    381 preprocessing_unrecognized_body()
    382 {
    383 	cat <<-\EOF > code.c
    384 		#unknown
    385 		# 3 "file.c"
    386 		#elif 3
    387 		#else
    388 	EOF
    389 	cat <<-\EOF > stderr.exp
    390 		error: code.c:3: Unmatched #elif
    391 		error: code.c:4: Unmatched #else
    392 	EOF
    393 
    394 	atf_check -s 'exit:1' \
    395 	    -e 'file:stderr.exp' \
    396 	    "$indent" code.c
    397 }
    398 
    399 atf_test_case 'unbalanced_parentheses_1'
    400 unbalanced_parentheses_1_body()
    401 {
    402 	cat <<-\EOF > code.c
    403 		int var =
    404 		(
    405 		;
    406 		)
    407 		;
    408 	EOF
    409 	cat <<-\EOF > stderr.exp
    410 		error: code.c:3: Unbalanced parentheses
    411 		warning: code.c:4: Extra ')'
    412 	EOF
    413 
    414 	atf_check -s 'exit:1' -e 'file:stderr.exp' \
    415 	    "$indent" code.c
    416 }
    417 
    418 atf_test_case 'unbalanced_parentheses_2'
    419 unbalanced_parentheses_2_body()
    420 {
    421 	# '({...})' is the GCC extension "Statement expression".
    422 	cat <<-\EOF > code.c
    423 		int var =
    424 		(
    425 		{
    426 		1
    427 		}
    428 		)
    429 		;
    430 	EOF
    431 	cat <<-\EOF > stderr.exp
    432 		error: code.c:3: Unbalanced parentheses
    433 		warning: code.c:6: Extra ')'
    434 	EOF
    435 
    436 	atf_check -s 'exit:1' -e 'file:stderr.exp' \
    437 	    "$indent" code.c
    438 }
    439 
    440 atf_test_case 'unbalanced_parentheses_3'
    441 unbalanced_parentheses_3_body()
    442 {
    443 	# '({...})' is the GCC extension "Statement expression".
    444 	cat <<-\EOF > code.c
    445 		int var =
    446 		(
    447 		1
    448 		}
    449 		;
    450 	EOF
    451 	cat <<-\EOF > stderr.exp
    452 		error: code.c:4: Unbalanced parentheses
    453 		error: code.c:4: Statement nesting error
    454 	EOF
    455 
    456 	atf_check -s 'exit:1' -e 'file:stderr.exp' \
    457 	    "$indent" code.c
    458 }
    459 
    460 atf_test_case 'crash_comment_after_controlling_expression'
    461 crash_comment_after_controlling_expression_body()
    462 {
    463 	# Before 2023-05-11, indent crashed while
    464 	# trying to format the following artificial code.
    465 
    466 	printf '{if(expr\n)/*c*/;}\n' > code.c
    467 
    468 	cat <<\EOF > code.exp
    469 {
    470 	if (expr
    471 		) /* c */ ;
    472 }
    473 EOF
    474 
    475 	atf_check -o 'file:code.exp' \
    476 	    "$indent" code.c -st
    477 }
    478 
    479 atf_test_case 'comment_fits_in_one_line'
    480 comment_fits_in_one_line_body()
    481 {
    482 	# The comment is placed after 'if (0) ...'. Before NetBSD pr_comment.c
    483 	# 1.91 from 2021-10-30, this produced an assertion failure in
    484 	# fits_in_one_line.
    485 	cat <<EOF > code.c
    486 int f(void)
    487 {
    488 	if (0)
    489 		/* 0123456789012345678901 */;
    490 }
    491 EOF
    492 
    493 	# Indent tries hard to make the comment fit to the 34-character line
    494 	# length, but it is just not possible.
    495 	cat <<EOF > expected.out
    496 int
    497 f(void)
    498 {
    499 	if (0)
    500 		/*
    501 		 * 0123456789012345678901
    502 		 */ ;
    503 }
    504 EOF
    505 
    506 	atf_check -o 'file:expected.out' \
    507 	    "$indent" -l34 code.c -st
    508 }
    509 
    510 
    511 atf_test_case 'compound_literal'
    512 compound_literal_body()
    513 {
    514 	# Test handling of compound literals (C99 6.5.2.5), as well as casts.
    515 
    516 	cat <<EOF > code.c
    517 void
    518 function(void)
    519 {
    520 	origin =
    521 	((int)
    522 	((-1)*
    523 	(struct point){0,0}
    524 	)
    525 	);
    526 }
    527 EOF
    528 
    529 	sed '/^#/d' <<EOF > expected.out
    530 void
    531 function(void)
    532 {
    533 	origin =
    534 		    ((int)
    535 		     ((-1) *
    536 		      (struct point){0, 0}
    537 # FIXME: the ')' must be aligned with the corresponding '('.
    538 	)
    539 		    );
    540 }
    541 EOF
    542 	sed '/^#/d' <<EOF > expected.err
    543 # FIXME: The parentheses _are_ balanced, the '}' does not end the block.
    544 error: code.c:7: Unbalanced parentheses
    545 warning: code.c:8: Extra ')'
    546 warning: code.c:9: Extra ')'
    547 EOF
    548 
    549 	atf_check -s 'exit:1' -o 'file:expected.out' -e 'file:expected.err' \
    550 	    "$indent" -nfc1 -ci12 code.c -st
    551 }
    552 
    553 atf_init_test_cases()
    554 {
    555 	atf_add_test_case 'option_unknown'
    556 	atf_add_test_case 'option_bool_trailing_garbage'
    557 	atf_add_test_case 'option_int_missing_argument'
    558 	atf_add_test_case 'option_profile_not_found'
    559 	atf_add_test_case 'option_buffer_overflow'
    560 	atf_add_test_case 'option_typedefs_not_found'
    561 	atf_add_test_case 'option_special_missing_param'
    562 	atf_add_test_case 'option_tabsize_negative'
    563 	atf_add_test_case 'option_tabsize_zero'
    564 	atf_add_test_case 'option_tabsize_large'
    565 	atf_add_test_case 'option_tabsize_very_large'
    566 	atf_add_test_case 'option_int_trailing_garbage'
    567 	atf_add_test_case 'option_cli_trailing_garbage'
    568 	atf_add_test_case 'option_npro_trailing_garbage'
    569 	atf_add_test_case 'option_st_trailing_garbage'
    570 	atf_add_test_case 'option_version_trailing_garbage'
    571 	atf_add_test_case 'option_indent_size_zero'
    572 	atf_add_test_case 'unterminated_comment_wrap'
    573 	atf_add_test_case 'unterminated_comment_nowrap'
    574 	atf_add_test_case 'unterminated_char_constant'
    575 	atf_add_test_case 'unterminated_string_literal'
    576 	atf_add_test_case 'in_place_wrong_backup'
    577 	atf_add_test_case 'argument_input_enoent'
    578 	atf_add_test_case 'argument_output_equals_input_name'
    579 	atf_add_test_case 'argument_output_equals_input_file'
    580 	atf_add_test_case 'argument_output_enoent'
    581 	atf_add_test_case 'argument_too_many'
    582 	atf_add_test_case 'unexpected_end_of_file'
    583 	atf_add_test_case 'unexpected_closing_brace_top_level'
    584 	atf_add_test_case 'unexpected_closing_brace_decl'
    585 	atf_add_test_case 'preprocessing_overflow'
    586 	atf_add_test_case 'preprocessing_unrecognized'
    587 	atf_add_test_case 'unbalanced_parentheses_1'
    588 	atf_add_test_case 'unbalanced_parentheses_2'
    589 	atf_add_test_case 'unbalanced_parentheses_3'
    590 	atf_add_test_case 'crash_comment_after_controlling_expression'
    591 	atf_add_test_case 'comment_fits_in_one_line'
    592 	atf_add_test_case 'compound_literal'
    593 }
    594