t_errors.sh revision 1.19 1 #! /bin/sh
2 # $NetBSD: t_errors.sh,v 1.19 2021/11/07 15:54:00 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 # $FreeBSD$
29
30 # Tests for error handling in indent.
31
32 indent=$(atf_config_get usr.bin.indent.test_indent /usr/bin/indent)
33 nl='
34 '
35
36 expect_error()
37 {
38 local msg
39
40 msg="$1"
41 shift
42
43 atf_check -s 'exit:1' \
44 -e "inline:$msg$nl" \
45 "$indent" "$@"
46 }
47
48 atf_test_case 'option_unknown'
49 option_unknown_body()
50 {
51 expect_error \
52 'indent: Command line: unknown option "-Z-unknown"' \
53 -Z-unknown
54 }
55
56 atf_test_case 'option_bool_trailing_garbage'
57 option_bool_trailing_garbage_body()
58 {
59 expect_error \
60 'indent: Command line: unknown option "-bacchus"' \
61 -bacchus
62 }
63
64 atf_test_case 'option_int_missing_argument'
65 option_int_missing_argument_body()
66 {
67 expect_error \
68 'indent: Command line: argument "x" to option "-ts" must be an integer' \
69 -tsx
70 }
71
72 atf_test_case 'option_profile_not_found'
73 option_profile_not_found_body()
74 {
75 expect_error \
76 'indent: profile ./nonexistent: No such file or directory' \
77 -P./nonexistent
78 }
79
80 atf_test_case 'option_typedefs_not_found'
81 option_typedefs_not_found_body()
82 {
83 expect_error \
84 'indent: cannot open file ./nonexistent' \
85 -U./nonexistent
86 }
87
88 atf_test_case 'option_tabsize_negative'
89 option_tabsize_negative_body()
90 {
91 expect_error \
92 'indent: Command line: argument "-1" to option "-ts" must be between 1 and 80' \
93 -ts-1
94 }
95
96 atf_test_case 'option_tabsize_zero'
97 option_tabsize_zero_body()
98 {
99 expect_error \
100 'indent: Command line: argument "0" to option "-ts" must be between 1 and 80' \
101 -ts0
102 }
103
104 atf_test_case 'option_tabsize_large'
105 option_tabsize_large_body()
106 {
107 # Integer overflow, on both ILP32 and LP64 platforms.
108 expect_error \
109 'indent: Command line: argument "81" to option "-ts" must be between 1 and 80' \
110 -ts81
111 }
112
113 atf_test_case 'option_tabsize_very_large'
114 option_tabsize_very_large_body()
115 {
116 # Integer overflow, on both ILP32 and LP64 platforms.
117 expect_error \
118 'indent: Command line: argument "3000000000" to option "-ts" must be between 1 and 80' \
119 -ts3000000000
120 }
121
122 atf_test_case 'option_indent_size_zero'
123 option_indent_size_zero_body()
124 {
125 expect_error \
126 'indent: Command line: argument "0" to option "-i" must be between 1 and 80' \
127 -i0
128 }
129
130 atf_test_case 'option_int_trailing_garbage'
131 option_int_trailing_garbage_body()
132 {
133 expect_error \
134 'indent: Command line: argument "3garbage" to option "-i" must be an integer' \
135 -i3garbage
136 }
137
138 atf_test_case 'option_cli_trailing_garbage'
139 option_cli_trailing_garbage_body()
140 {
141 expect_error \
142 'indent: Command line: argument "3garbage" to option "-cli" must be numeric' \
143 -cli3garbage
144 }
145
146 atf_test_case 'option_npro_trailing_garbage'
147 option_npro_trailing_garbage_body()
148 {
149 # TODO: reject -npro-garbage, only allow -npro without trailing garbage.
150
151 atf_check \
152 "$indent" -npro-garbage
153 }
154
155 atf_test_case 'option_st_trailing_garbage'
156 option_st_trailing_garbage_body()
157 {
158 # TODO: reject -stdio, only allow -st without trailing garbage.
159
160 atf_check \
161 "$indent" -stdio
162 }
163
164 atf_test_case 'option_version_trailing_garbage'
165 option_version_trailing_garbage_body()
166 {
167 # TODO: reject --version-dump, only allow --version without trailing garbage.
168
169 atf_check -o 'inline:NetBSD indent 2.1'"$nl" \
170 "$indent" --version-dump
171 }
172
173 atf_test_case 'option_buffer_overflow'
174 option_buffer_overflow_body()
175 {
176 opt='12345678123456781234567812345678' # 32
177 opt="$opt$opt$opt$opt$opt$opt$opt$opt" # 256
178 opt="$opt$opt$opt$opt$opt$opt$opt$opt" # 2048
179 opt="$opt$opt$opt$opt$opt$opt$opt$opt" # 16384
180 printf '%s\n' "-$opt" > indent.pro
181
182 expect_error \
183 'indent: buffer overflow in indent.pro, starting with '\''-123456781'\''' \
184 -Pindent.pro
185 }
186
187 atf_test_case 'option_special_missing_param'
188 option_special_missing_param_body()
189 {
190 # TODO: Write '-cli' instead of only 'cli'.
191 expect_error \
192 'indent: Command line: ``cli'\'\'' requires an argument' \
193 -cli
194
195 expect_error \
196 'indent: Command line: ``T'\'\'' requires an argument' \
197 -T
198
199 expect_error \
200 'indent: Command line: ``U'\'\'' requires an argument' \
201 -U
202 }
203
204 atf_test_case 'unterminated_comment_wrap'
205 unterminated_comment_wrap_body()
206 {
207 echo '/*' > comment.c
208
209 atf_check -s 'exit:1' \
210 -o 'inline:/*'"$nl"' *'"$nl" \
211 -e 'inline:error: Standard Input:2: Unterminated comment'"$nl" \
212 "$indent" -st < comment.c
213 }
214
215 atf_test_case 'unterminated_comment_nowrap'
216 unterminated_comment_nowrap_body()
217 {
218 echo '/*-' > comment.c
219
220 atf_check -s 'exit:1' \
221 -o 'inline:/*-'"$nl$nl" \
222 -e 'inline:error: Standard Input:2: Unterminated comment'"$nl" \
223 "$indent" -st < comment.c
224 }
225
226 atf_test_case 'in_place_wrong_backup'
227 in_place_wrong_backup_body()
228 {
229 cat <<-\EOF > code.c
230 int decl;
231 EOF
232 cp code.c code.c.orig
233
234 # Due to the strange backup suffix '/subdir', indent tries to create
235 # a file named 'code.c/subdir', but 'code.c' is already a regular
236 # file, not a directory.
237 atf_check -s 'exit:1' \
238 -e 'inline:indent: code.c/subdir: Not a directory'"$nl" \
239 env SIMPLE_BACKUP_SUFFIX="/subdir" "$indent" code.c
240
241 # Since there was an early error, the original file is kept as is.
242 atf_check -o 'file:code.c.orig' \
243 cat code.c
244 }
245
246 atf_test_case 'argument_input_enoent'
247 argument_input_enoent_body()
248 {
249 atf_check -s 'exit:1' \
250 -e 'inline:indent: ./nonexistent.c: No such file or directory'"$nl" \
251 "$indent" ./nonexistent.c
252 }
253
254 atf_test_case 'argument_output_equals_input_name'
255 argument_output_equals_input_name_body()
256 {
257 echo '/* comment */' > code.c
258
259 atf_check -s 'exit:1' \
260 -e 'inline:indent: input and output files must be different'"$nl" \
261 "$indent" code.c code.c
262 }
263
264 atf_test_case 'argument_output_equals_input_file'
265 argument_output_equals_input_file_body()
266 {
267 echo '/* comment */' > code.c
268
269 atf_check \
270 "$indent" code.c ./code.c
271
272 # Oops, the file has become empty since the output is first emptied,
273 # before reading any of the input.
274 atf_check \
275 cat code.c
276 }
277
278 atf_test_case 'argument_output_enoent'
279 argument_output_enoent_body()
280 {
281 expect_error \
282 'indent: subdir/nonexistent.c: No such file or directory' \
283 /dev/null subdir/nonexistent.c
284 }
285
286 atf_test_case 'argument_too_many'
287 argument_too_many_body()
288 {
289 echo '/* comment */' > arg1.c
290
291 expect_error \
292 'indent: too many arguments: arg3.c' \
293 arg1.c arg2.c arg3.c arg4.c
294 }
295
296 atf_test_case 'unexpected_end_of_file'
297 unexpected_end_of_file_body()
298 {
299 echo 'struct{' > code.c
300
301 expect_error \
302 'error: code.c:1: Stuff missing from end of file' \
303 code.c
304
305 atf_check \
306 -o 'inline:struct {'"$nl" \
307 cat code.c
308 }
309
310 atf_test_case 'unexpected_closing_brace_top_level'
311 unexpected_closing_brace_top_level_body()
312 {
313 echo '}' > code.c
314
315 expect_error \
316 'error: code.c:1: Statement nesting error' \
317 code.c
318 atf_check \
319 -o 'inline:}'"$nl" \
320 cat code.c
321 }
322
323 atf_test_case 'unexpected_closing_brace_decl'
324 unexpected_closing_brace_decl_body()
325 {
326 echo 'int i = 3};' > code.c
327
328 expect_error \
329 'error: code.c:1: Statement nesting error' \
330 code.c
331 # Despite the error message, the original file got overwritten with a
332 # best-effort rewrite of the code.
333 atf_check \
334 -o 'inline:int i = 3};'"$nl" \
335 cat code.c
336 }
337
338 atf_test_case 'preprocessing_overflow'
339 preprocessing_overflow_body()
340 {
341 cat <<-\EOF > code.c
342 #if 1
343 #if 2
344 #if 3
345 #if 4
346 #if 5
347 #if 6
348 #endif 6
349 #endif 5
350 #endif 4
351 #endif 3
352 #endif 2
353 #endif 1
354 #endif too much
355 EOF
356 cat <<-\EOF > stderr.exp
357 error: code.c:6: #if stack overflow
358 error: code.c:12: Unmatched #endif
359 error: code.c:13: Unmatched #endif
360 EOF
361
362 atf_check -s 'exit:1' \
363 -e 'file:stderr.exp' \
364 "$indent" code.c
365 }
366
367 atf_test_case 'preprocessing_unrecognized'
368 preprocessing_unrecognized_body()
369 {
370 cat <<-\EOF > code.c
371 #unknown
372 # 3 "file.c"
373 #elif 3
374 #else
375 EOF
376 cat <<-\EOF > stderr.exp
377 error: code.c:1: Unrecognized cpp directive
378 error: code.c:2: Unrecognized cpp directive
379 error: code.c:3: Unmatched #elif
380 error: code.c:4: Unmatched #else
381 EOF
382
383 atf_check -s 'exit:1' \
384 -e 'file:stderr.exp' \
385 "$indent" code.c
386 }
387
388 atf_test_case 'unbalanced_parentheses_1'
389 unbalanced_parentheses_1_body()
390 {
391 cat <<-\EOF > code.c
392 int var =
393 (
394 ;
395 )
396 ;
397 EOF
398 cat <<-\EOF > stderr.exp
399 error: code.c:3: Unbalanced parentheses
400 warning: code.c:4: Extra ')'
401 EOF
402
403 atf_check -s 'exit:1' -e 'file:stderr.exp' \
404 "$indent" code.c
405 }
406
407 atf_test_case 'unbalanced_parentheses_2'
408 unbalanced_parentheses_2_body()
409 {
410 # '({...})' is the GCC extension "Statement expression".
411 cat <<-\EOF > code.c
412 int var =
413 (
414 {
415 1
416 }
417 )
418 ;
419 EOF
420 cat <<-\EOF > stderr.exp
421 error: code.c:3: Unbalanced parentheses
422 warning: code.c:6: Extra ')'
423 EOF
424
425 atf_check -s 'exit:1' -e 'file:stderr.exp' \
426 "$indent" code.c
427 }
428
429 atf_test_case 'unbalanced_parentheses_3'
430 unbalanced_parentheses_3_body()
431 {
432 # '({...})' is the GCC extension "Statement expression".
433 cat <<-\EOF > code.c
434 int var =
435 (
436 1
437 }
438 ;
439 EOF
440 cat <<-\EOF > stderr.exp
441 error: code.c:4: Unbalanced parentheses
442 error: code.c:4: Statement nesting error
443 EOF
444
445 atf_check -s 'exit:1' -e 'file:stderr.exp' \
446 "$indent" code.c
447 }
448
449 atf_test_case 'search_stmt_comment_segv'
450 search_stmt_comment_segv_body()
451 {
452 # As of NetBSD indent.c 1.188 from 2021-10-30, indent crashes while
453 # trying to format the following artificial code.
454
455 printf '{if(expr\n)/*c*/;}\n' > code.c
456
457 cat <<\EOF > code.exp
458 {
459 if (expr
460 ) /* c */
461 ;
462 }
463 EOF
464
465 # TODO: actually produce code.exp instead of an assertion failure.
466 atf_check -s 'signal' -o 'ignore' -e 'match:assert' \
467 "$indent" code.c -st
468 }
469
470 atf_test_case 'search_stmt_fits_in_one_line'
471 search_stmt_fits_in_one_line_body()
472 {
473 # The comment is placed after 'if (0) ...', where it is processed
474 # by search_stmt_comment. That function redirects the input buffer to
475 # a temporary buffer that is not guaranteed to be terminated by '\n'.
476 # Before NetBSD pr_comment.c 1.91 from 2021-10-30, this produced an
477 # assertion failure in fits_in_one_line.
478 cat <<EOF > code.c
479 int f(void)
480 {
481 if (0)
482 /* 0123456789012345678901 */;
483 }
484 EOF
485
486 # Indent tries hard to make the comment fit to the 34-character line
487 # length, but it is just not possible.
488 cat <<EOF > expected.out
489 int
490 f(void)
491 {
492 if (0)
493 /*
494 * 0123456789012345678901
495 */ ;
496 }
497 EOF
498
499 atf_check -o 'file:expected.out' \
500 "$indent" -l34 code.c -st
501 }
502
503
504 atf_init_test_cases()
505 {
506 atf_add_test_case 'option_unknown'
507 atf_add_test_case 'option_bool_trailing_garbage'
508 atf_add_test_case 'option_int_missing_argument'
509 atf_add_test_case 'option_profile_not_found'
510 atf_add_test_case 'option_buffer_overflow'
511 atf_add_test_case 'option_typedefs_not_found'
512 atf_add_test_case 'option_special_missing_param'
513 atf_add_test_case 'option_tabsize_negative'
514 atf_add_test_case 'option_tabsize_zero'
515 atf_add_test_case 'option_tabsize_large'
516 atf_add_test_case 'option_tabsize_very_large'
517 atf_add_test_case 'option_int_trailing_garbage'
518 atf_add_test_case 'option_cli_trailing_garbage'
519 atf_add_test_case 'option_npro_trailing_garbage'
520 atf_add_test_case 'option_st_trailing_garbage'
521 atf_add_test_case 'option_version_trailing_garbage'
522 atf_add_test_case 'option_indent_size_zero'
523 atf_add_test_case 'unterminated_comment_wrap'
524 atf_add_test_case 'unterminated_comment_nowrap'
525 atf_add_test_case 'in_place_wrong_backup'
526 atf_add_test_case 'argument_input_enoent'
527 atf_add_test_case 'argument_output_equals_input_name'
528 atf_add_test_case 'argument_output_equals_input_file'
529 atf_add_test_case 'argument_output_enoent'
530 atf_add_test_case 'argument_too_many'
531 atf_add_test_case 'unexpected_end_of_file'
532 atf_add_test_case 'unexpected_closing_brace_top_level'
533 atf_add_test_case 'unexpected_closing_brace_decl'
534 atf_add_test_case 'preprocessing_overflow'
535 atf_add_test_case 'preprocessing_unrecognized'
536 atf_add_test_case 'unbalanced_parentheses_1'
537 atf_add_test_case 'unbalanced_parentheses_2'
538 atf_add_test_case 'unbalanced_parentheses_3'
539 atf_add_test_case 'search_stmt_comment_segv'
540 atf_add_test_case 'search_stmt_fits_in_one_line'
541 }
542