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