t_errors.sh revision 1.27 1 #! /bin/sh
2 # $NetBSD: t_errors.sh,v 1.27 2023/05/13 16:40:18 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 # Integer overflow, on both ILP32 and LP64 platforms.
104 expect_error \
105 'indent: Command line: argument "81" to option "-ts" must be between 1 and 80' \
106 -ts81
107 }
108
109 atf_test_case 'option_tabsize_very_large'
110 option_tabsize_very_large_body()
111 {
112 # Integer overflow, on both ILP32 and LP64 platforms.
113 expect_error \
114 'indent: Command line: argument "3000000000" to option "-ts" must be between 1 and 80' \
115 -ts3000000000
116 }
117
118 atf_test_case 'option_indent_size_zero'
119 option_indent_size_zero_body()
120 {
121 expect_error \
122 'indent: Command line: argument "0" to option "-i" must be between 1 and 80' \
123 -i0
124 }
125
126 atf_test_case 'option_int_trailing_garbage'
127 option_int_trailing_garbage_body()
128 {
129 expect_error \
130 'indent: Command line: argument "3garbage" to option "-i" must be an integer' \
131 -i3garbage
132 }
133
134 atf_test_case 'option_cli_trailing_garbage'
135 option_cli_trailing_garbage_body()
136 {
137 expect_error \
138 'indent: Command line: argument "3garbage" to option "-cli" must be numeric' \
139 -cli3garbage
140 }
141
142 atf_test_case 'option_npro_trailing_garbage'
143 option_npro_trailing_garbage_body()
144 {
145 atf_check -s 'exit:1' \
146 -e 'inline:indent: Command line: unknown option "-npro-garbage"\n' \
147 "$indent" -npro-garbage
148 }
149
150 atf_test_case 'option_st_trailing_garbage'
151 option_st_trailing_garbage_body()
152 {
153 atf_check -s 'exit:1' \
154 -e 'inline:indent: Command line: unknown option "-stdio"\n' \
155 "$indent" -stdio
156 }
157
158 atf_test_case 'option_version_trailing_garbage'
159 option_version_trailing_garbage_body()
160 {
161 atf_check -s 'exit:1' \
162 -e 'inline:indent: Command line: unknown option "--version-dump"\n' \
163 "$indent" --version-dump
164 }
165
166 atf_test_case 'option_buffer_overflow'
167 option_buffer_overflow_body()
168 {
169 opt='12345678123456781234567812345678' # 32
170 opt="$opt$opt$opt$opt$opt$opt$opt$opt" # 256
171 opt="$opt$opt$opt$opt$opt$opt$opt$opt" # 2048
172 opt="$opt$opt$opt$opt$opt$opt$opt$opt" # 16384
173 printf '%s\n' "-$opt" > indent.pro
174
175 expect_error \
176 'indent: buffer overflow in indent.pro, starting with '\''-123456781'\''' \
177 -Pindent.pro
178 }
179
180 atf_test_case 'option_special_missing_param'
181 option_special_missing_param_body()
182 {
183 expect_error \
184 'indent: Command line: ``-cli'\'\'' requires an argument' \
185 -cli
186
187 expect_error \
188 'indent: Command line: ``-T'\'\'' requires an argument' \
189 -T
190
191 expect_error \
192 'indent: Command line: ``-U'\'\'' requires an argument' \
193 -U
194 }
195
196 atf_test_case 'unterminated_comment_wrap'
197 unterminated_comment_wrap_body()
198 {
199 echo '/*' > comment.c
200
201 atf_check -s 'exit:1' \
202 -o 'inline:/*\n *\n' \
203 -e 'inline:error: Standard Input:2: Unterminated comment\n' \
204 "$indent" -st < comment.c
205 }
206
207 atf_test_case 'unterminated_comment_nowrap'
208 unterminated_comment_nowrap_body()
209 {
210 echo '/*-' > comment.c
211
212 atf_check -s 'exit:1' \
213 -o 'inline:/*-\n\n' \
214 -e 'inline:error: Standard Input:2: Unterminated comment\n' \
215 "$indent" -st < comment.c
216 }
217
218 atf_test_case 'in_place_wrong_backup'
219 in_place_wrong_backup_body()
220 {
221 cat <<-\EOF > code.c
222 int decl;
223 EOF
224 cp code.c code.c.orig
225
226 # Due to the strange backup suffix '/subdir', indent tries to create
227 # a file named 'code.c/subdir', but 'code.c' is already a regular
228 # file, not a directory.
229 atf_check -s 'exit:1' \
230 -e 'inline:indent: code.c/subdir: Not a directory\n' \
231 env SIMPLE_BACKUP_SUFFIX="/subdir" "$indent" code.c
232
233 # Since there was an early error, the original file is kept as is.
234 atf_check -o 'file:code.c.orig' \
235 cat code.c
236 }
237
238 atf_test_case 'argument_input_enoent'
239 argument_input_enoent_body()
240 {
241 atf_check -s 'exit:1' \
242 -e 'inline:indent: ./nonexistent.c: No such file or directory\n' \
243 "$indent" ./nonexistent.c
244 }
245
246 atf_test_case 'argument_output_equals_input_name'
247 argument_output_equals_input_name_body()
248 {
249 echo '/* comment */' > code.c
250
251 atf_check -s 'exit:1' \
252 -e 'inline:indent: input and output files must be different\n' \
253 "$indent" code.c code.c
254 }
255
256 atf_test_case 'argument_output_equals_input_file'
257 argument_output_equals_input_file_body()
258 {
259 echo '/* comment */' > code.c
260
261 atf_check \
262 "$indent" code.c ./code.c
263
264 # Oops, the file has become empty since the output is first emptied,
265 # before reading any of the input.
266 atf_check \
267 cat code.c
268 }
269
270 atf_test_case 'argument_output_enoent'
271 argument_output_enoent_body()
272 {
273 expect_error \
274 'indent: subdir/nonexistent.c: No such file or directory' \
275 /dev/null subdir/nonexistent.c
276 }
277
278 atf_test_case 'argument_too_many'
279 argument_too_many_body()
280 {
281 echo '/* comment */' > arg1.c
282
283 expect_error \
284 'indent: too many arguments: arg3.c' \
285 arg1.c arg2.c arg3.c arg4.c
286 }
287
288 atf_test_case 'unexpected_end_of_file'
289 unexpected_end_of_file_body()
290 {
291 echo 'struct{' > code.c
292
293 expect_error \
294 'error: code.c:1: Stuff missing from end of file' \
295 code.c
296
297 atf_check \
298 -o 'inline:struct {\n' \
299 cat code.c
300 }
301
302 atf_test_case 'unexpected_closing_brace_top_level'
303 unexpected_closing_brace_top_level_body()
304 {
305 echo '}' > code.c
306
307 expect_error \
308 'error: code.c:1: Statement nesting error' \
309 code.c
310 atf_check \
311 -o 'inline:}\n' \
312 cat code.c
313 }
314
315 atf_test_case 'unexpected_closing_brace_decl'
316 unexpected_closing_brace_decl_body()
317 {
318 echo 'int i = 3};' > code.c
319
320 expect_error \
321 'error: code.c:1: Statement nesting error' \
322 code.c
323 # Despite the error message, the original file got overwritten with a
324 # best-effort rewrite of the code.
325 atf_check \
326 -o 'inline:int i = 3};\n' \
327 cat code.c
328 }
329
330 atf_test_case 'preprocessing_overflow'
331 preprocessing_overflow_body()
332 {
333 cat <<-\EOF > code.c
334 #if 1
335 #if 2
336 #if 3
337 #if 4
338 #if 5
339 #if 6
340 #endif 6
341 #endif 5
342 #endif 4
343 #endif 3
344 #endif 2
345 #endif 1
346 #endif too much
347 EOF
348 cat <<-\EOF > stderr.exp
349 error: code.c:6: #if stack overflow
350 error: code.c:12: Unmatched #endif
351 error: code.c:13: Unmatched #endif
352 EOF
353
354 atf_check -s 'exit:1' \
355 -e 'file:stderr.exp' \
356 "$indent" code.c
357 }
358
359 atf_test_case 'preprocessing_unrecognized'
360 preprocessing_unrecognized_body()
361 {
362 cat <<-\EOF > code.c
363 #unknown
364 # 3 "file.c"
365 #elif 3
366 #else
367 EOF
368 cat <<-\EOF > stderr.exp
369 error: code.c:1: Unrecognized cpp directive "unknown"
370 error: code.c:2: Unrecognized cpp directive ""
371 error: code.c:3: Unmatched #elif
372 error: code.c:4: Unmatched #else
373 EOF
374
375 atf_check -s 'exit:1' \
376 -e 'file:stderr.exp' \
377 "$indent" code.c
378 }
379
380 atf_test_case 'unbalanced_parentheses_1'
381 unbalanced_parentheses_1_body()
382 {
383 cat <<-\EOF > code.c
384 int var =
385 (
386 ;
387 )
388 ;
389 EOF
390 cat <<-\EOF > stderr.exp
391 error: code.c:3: Unbalanced parentheses
392 warning: code.c:4: Extra ')'
393 EOF
394
395 atf_check -s 'exit:1' -e 'file:stderr.exp' \
396 "$indent" code.c
397 }
398
399 atf_test_case 'unbalanced_parentheses_2'
400 unbalanced_parentheses_2_body()
401 {
402 # '({...})' is the GCC extension "Statement expression".
403 cat <<-\EOF > code.c
404 int var =
405 (
406 {
407 1
408 }
409 )
410 ;
411 EOF
412 cat <<-\EOF > stderr.exp
413 error: code.c:3: Unbalanced parentheses
414 warning: code.c:6: Extra ')'
415 EOF
416
417 atf_check -s 'exit:1' -e 'file:stderr.exp' \
418 "$indent" code.c
419 }
420
421 atf_test_case 'unbalanced_parentheses_3'
422 unbalanced_parentheses_3_body()
423 {
424 # '({...})' is the GCC extension "Statement expression".
425 cat <<-\EOF > code.c
426 int var =
427 (
428 1
429 }
430 ;
431 EOF
432 cat <<-\EOF > stderr.exp
433 error: code.c:4: Unbalanced parentheses
434 error: code.c:4: Statement nesting error
435 EOF
436
437 atf_check -s 'exit:1' -e 'file:stderr.exp' \
438 "$indent" code.c
439 }
440
441 atf_test_case 'crash_comment_after_controlling_expression'
442 crash_comment_after_controlling_expression_body()
443 {
444 # Before 2023-05-11, indent crashed while
445 # trying to format the following artificial code.
446
447 printf '{if(expr\n)/*c*/;}\n' > code.c
448
449 cat <<\EOF > code.exp
450 {
451 if (expr
452 ) /* c */ ;
453 }
454 EOF
455
456 atf_check -o 'file:code.exp' \
457 "$indent" code.c -st
458 }
459
460 atf_test_case 'comment_fits_in_one_line'
461 comment_fits_in_one_line_body()
462 {
463 # The comment is placed after 'if (0) ...', where it is processed
464 # by search_stmt_comment. That function redirects the input buffer to
465 # a temporary buffer that is not guaranteed to be terminated by '\n'.
466 # Before NetBSD pr_comment.c 1.91 from 2021-10-30, this produced an
467 # assertion failure in fits_in_one_line.
468 cat <<EOF > code.c
469 int f(void)
470 {
471 if (0)
472 /* 0123456789012345678901 */;
473 }
474 EOF
475
476 # Indent tries hard to make the comment fit to the 34-character line
477 # length, but it is just not possible.
478 cat <<EOF > expected.out
479 int
480 f(void)
481 {
482 if (0)
483 /*
484 * 0123456789012345678901
485 */ ;
486 }
487 EOF
488
489 atf_check -o 'file:expected.out' \
490 "$indent" -l34 code.c -st
491 }
492
493
494 atf_test_case 'compound_literal'
495 compound_literal_body()
496 {
497 # Test handling of compound literals (C99 6.5.2.5), as well as casts.
498
499 cat <<EOF > code.c
500 void
501 function(void)
502 {
503 origin =
504 ((int)
505 ((-1)*
506 (struct point){0,0}
507 )
508 );
509 }
510 EOF
511
512 sed '/^#/d' <<EOF > expected.out
513 void
514 function(void)
515 {
516 origin =
517 ((int)
518 ((-1) *
519 (struct point){
520 # FIXME: the '{' is part of the expression, not a separate block.
521 0, 0
522 # FIXME: the '}' is part of the expression, not a separate block.
523 }
524 # FIXME: the ')' must be aligned with the corresponding '('.
525 )
526 );
527 }
528 EOF
529 sed '/^#/d' <<EOF > expected.err
530 # FIXME: The parentheses _are_ balanced, the '}' does not end the block.
531 error: code.c:7: Unbalanced parentheses
532 warning: code.c:8: Extra ')'
533 warning: code.c:9: Extra ')'
534 EOF
535
536 atf_check -s 'exit:1' -o 'file:expected.out' -e 'file:expected.err' \
537 "$indent" -nfc1 -ci12 code.c -st
538 }
539
540 atf_init_test_cases()
541 {
542 atf_add_test_case 'option_unknown'
543 atf_add_test_case 'option_bool_trailing_garbage'
544 atf_add_test_case 'option_int_missing_argument'
545 atf_add_test_case 'option_profile_not_found'
546 atf_add_test_case 'option_buffer_overflow'
547 atf_add_test_case 'option_typedefs_not_found'
548 atf_add_test_case 'option_special_missing_param'
549 atf_add_test_case 'option_tabsize_negative'
550 atf_add_test_case 'option_tabsize_zero'
551 atf_add_test_case 'option_tabsize_large'
552 atf_add_test_case 'option_tabsize_very_large'
553 atf_add_test_case 'option_int_trailing_garbage'
554 atf_add_test_case 'option_cli_trailing_garbage'
555 atf_add_test_case 'option_npro_trailing_garbage'
556 atf_add_test_case 'option_st_trailing_garbage'
557 atf_add_test_case 'option_version_trailing_garbage'
558 atf_add_test_case 'option_indent_size_zero'
559 atf_add_test_case 'unterminated_comment_wrap'
560 atf_add_test_case 'unterminated_comment_nowrap'
561 atf_add_test_case 'in_place_wrong_backup'
562 atf_add_test_case 'argument_input_enoent'
563 atf_add_test_case 'argument_output_equals_input_name'
564 atf_add_test_case 'argument_output_equals_input_file'
565 atf_add_test_case 'argument_output_enoent'
566 atf_add_test_case 'argument_too_many'
567 atf_add_test_case 'unexpected_end_of_file'
568 atf_add_test_case 'unexpected_closing_brace_top_level'
569 atf_add_test_case 'unexpected_closing_brace_decl'
570 atf_add_test_case 'preprocessing_overflow'
571 atf_add_test_case 'preprocessing_unrecognized'
572 atf_add_test_case 'unbalanced_parentheses_1'
573 atf_add_test_case 'unbalanced_parentheses_2'
574 atf_add_test_case 'unbalanced_parentheses_3'
575 atf_add_test_case 'crash_comment_after_controlling_expression'
576 atf_add_test_case 'comment_fits_in_one_line'
577 atf_add_test_case 'compound_literal'
578 }
579