t_errors.sh revision 1.18 1 #! /bin/sh
2 # $NetBSD: t_errors.sh,v 1.18 2021/11/07 10:42:58 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_buffer_overflow'
147 option_buffer_overflow_body()
148 {
149 opt='12345678123456781234567812345678' # 32
150 opt="$opt$opt$opt$opt$opt$opt$opt$opt" # 256
151 opt="$opt$opt$opt$opt$opt$opt$opt$opt" # 2048
152 opt="$opt$opt$opt$opt$opt$opt$opt$opt" # 16384
153 printf '%s\n' "-$opt" > indent.pro
154
155 expect_error \
156 'indent: buffer overflow in indent.pro, starting with '\''-123456781'\''' \
157 -Pindent.pro
158 }
159
160 atf_test_case 'option_special_missing_param'
161 option_special_missing_param_body()
162 {
163 # TODO: Write '-cli' instead of only 'cli'.
164 expect_error \
165 'indent: Command line: ``cli'\'\'' requires an argument' \
166 -cli
167
168 expect_error \
169 'indent: Command line: ``T'\'\'' requires an argument' \
170 -T
171
172 expect_error \
173 'indent: Command line: ``U'\'\'' requires an argument' \
174 -U
175 }
176
177 atf_test_case 'unterminated_comment_wrap'
178 unterminated_comment_wrap_body()
179 {
180 echo '/*' > comment.c
181
182 atf_check -s 'exit:1' \
183 -o 'inline:/*'"$nl"' *'"$nl" \
184 -e 'inline:error: Standard Input:2: Unterminated comment'"$nl" \
185 "$indent" -st < comment.c
186 }
187
188 atf_test_case 'unterminated_comment_nowrap'
189 unterminated_comment_nowrap_body()
190 {
191 echo '/*-' > comment.c
192
193 atf_check -s 'exit:1' \
194 -o 'inline:/*-'"$nl$nl" \
195 -e 'inline:error: Standard Input:2: Unterminated comment'"$nl" \
196 "$indent" -st < comment.c
197 }
198
199 atf_test_case 'in_place_wrong_backup'
200 in_place_wrong_backup_body()
201 {
202 cat <<-\EOF > code.c
203 int decl;
204 EOF
205 cp code.c code.c.orig
206
207 # Due to the strange backup suffix '/subdir', indent tries to create
208 # a file named 'code.c/subdir', but 'code.c' is already a regular
209 # file, not a directory.
210 atf_check -s 'exit:1' \
211 -e 'inline:indent: code.c/subdir: Not a directory'"$nl" \
212 env SIMPLE_BACKUP_SUFFIX="/subdir" "$indent" code.c
213
214 # Since there was an early error, the original file is kept as is.
215 atf_check -o 'file:code.c.orig' \
216 cat code.c
217 }
218
219 atf_test_case 'argument_input_enoent'
220 argument_input_enoent_body()
221 {
222 atf_check -s 'exit:1' \
223 -e 'inline:indent: ./nonexistent.c: No such file or directory'"$nl" \
224 "$indent" ./nonexistent.c
225 }
226
227 atf_test_case 'argument_output_equals_input_name'
228 argument_output_equals_input_name_body()
229 {
230 echo '/* comment */' > code.c
231
232 atf_check -s 'exit:1' \
233 -e 'inline:indent: input and output files must be different'"$nl" \
234 "$indent" code.c code.c
235 }
236
237 atf_test_case 'argument_output_equals_input_file'
238 argument_output_equals_input_file_body()
239 {
240 echo '/* comment */' > code.c
241
242 atf_check \
243 "$indent" code.c ./code.c
244
245 # Oops, the file has become empty since the output is first emptied,
246 # before reading any of the input.
247 atf_check \
248 cat code.c
249 }
250
251 atf_test_case 'argument_output_enoent'
252 argument_output_enoent_body()
253 {
254 expect_error \
255 'indent: subdir/nonexistent.c: No such file or directory' \
256 /dev/null subdir/nonexistent.c
257 }
258
259 atf_test_case 'argument_too_many'
260 argument_too_many_body()
261 {
262 echo '/* comment */' > arg1.c
263
264 expect_error \
265 'indent: too many arguments: arg3.c' \
266 arg1.c arg2.c arg3.c arg4.c
267 }
268
269 atf_test_case 'unexpected_end_of_file'
270 unexpected_end_of_file_body()
271 {
272 echo 'struct{' > code.c
273
274 expect_error \
275 'error: code.c:1: Stuff missing from end of file' \
276 code.c
277
278 atf_check \
279 -o 'inline:struct {'"$nl" \
280 cat code.c
281 }
282
283 atf_test_case 'unexpected_closing_brace_top_level'
284 unexpected_closing_brace_top_level_body()
285 {
286 echo '}' > code.c
287
288 expect_error \
289 'error: code.c:1: Statement nesting error' \
290 code.c
291 atf_check \
292 -o 'inline:}'"$nl" \
293 cat code.c
294 }
295
296 atf_test_case 'unexpected_closing_brace_decl'
297 unexpected_closing_brace_decl_body()
298 {
299 echo 'int i = 3};' > code.c
300
301 expect_error \
302 'error: code.c:1: Statement nesting error' \
303 code.c
304 # Despite the error message, the original file got overwritten with a
305 # best-effort rewrite of the code.
306 atf_check \
307 -o 'inline:int i = 3};'"$nl" \
308 cat code.c
309 }
310
311 atf_test_case 'preprocessing_overflow'
312 preprocessing_overflow_body()
313 {
314 cat <<-\EOF > code.c
315 #if 1
316 #if 2
317 #if 3
318 #if 4
319 #if 5
320 #if 6
321 #endif 6
322 #endif 5
323 #endif 4
324 #endif 3
325 #endif 2
326 #endif 1
327 #endif too much
328 EOF
329 cat <<-\EOF > stderr.exp
330 error: code.c:6: #if stack overflow
331 error: code.c:12: Unmatched #endif
332 error: code.c:13: Unmatched #endif
333 EOF
334
335 atf_check -s 'exit:1' \
336 -e 'file:stderr.exp' \
337 "$indent" code.c
338 }
339
340 atf_test_case 'preprocessing_unrecognized'
341 preprocessing_unrecognized_body()
342 {
343 cat <<-\EOF > code.c
344 #unknown
345 # 3 "file.c"
346 #elif 3
347 #else
348 EOF
349 cat <<-\EOF > stderr.exp
350 error: code.c:1: Unrecognized cpp directive
351 error: code.c:2: Unrecognized cpp directive
352 error: code.c:3: Unmatched #elif
353 error: code.c:4: Unmatched #else
354 EOF
355
356 atf_check -s 'exit:1' \
357 -e 'file:stderr.exp' \
358 "$indent" code.c
359 }
360
361 atf_test_case 'unbalanced_parentheses_1'
362 unbalanced_parentheses_1_body()
363 {
364 cat <<-\EOF > code.c
365 int var =
366 (
367 ;
368 )
369 ;
370 EOF
371 cat <<-\EOF > stderr.exp
372 error: code.c:3: Unbalanced parentheses
373 warning: code.c:4: Extra ')'
374 EOF
375
376 atf_check -s 'exit:1' -e 'file:stderr.exp' \
377 "$indent" code.c
378 }
379
380 atf_test_case 'unbalanced_parentheses_2'
381 unbalanced_parentheses_2_body()
382 {
383 # '({...})' is the GCC extension "Statement expression".
384 cat <<-\EOF > code.c
385 int var =
386 (
387 {
388 1
389 }
390 )
391 ;
392 EOF
393 cat <<-\EOF > stderr.exp
394 error: code.c:3: Unbalanced parentheses
395 warning: code.c:6: Extra ')'
396 EOF
397
398 atf_check -s 'exit:1' -e 'file:stderr.exp' \
399 "$indent" code.c
400 }
401
402 atf_test_case 'unbalanced_parentheses_3'
403 unbalanced_parentheses_3_body()
404 {
405 # '({...})' is the GCC extension "Statement expression".
406 cat <<-\EOF > code.c
407 int var =
408 (
409 1
410 }
411 ;
412 EOF
413 cat <<-\EOF > stderr.exp
414 error: code.c:4: Unbalanced parentheses
415 error: code.c:4: Statement nesting error
416 EOF
417
418 atf_check -s 'exit:1' -e 'file:stderr.exp' \
419 "$indent" code.c
420 }
421
422 atf_test_case 'search_stmt_comment_segv'
423 search_stmt_comment_segv_body()
424 {
425 # As of NetBSD indent.c 1.188 from 2021-10-30, indent crashes while
426 # trying to format the following artificial code.
427
428 printf '{if(expr\n)/*c*/;}\n' > code.c
429
430 cat <<\EOF > code.exp
431 {
432 if (expr
433 ) /* c */
434 ;
435 }
436 EOF
437
438 # TODO: actually produce code.exp instead of an assertion failure.
439 atf_check -s 'signal' -o 'ignore' -e 'match:assert' \
440 "$indent" code.c -st
441 }
442
443 atf_test_case 'search_stmt_fits_in_one_line'
444 search_stmt_fits_in_one_line_body()
445 {
446 # The comment is placed after 'if (0) ...', where it is processed
447 # by search_stmt_comment. That function redirects the input buffer to
448 # a temporary buffer that is not guaranteed to be terminated by '\n'.
449 # Before NetBSD pr_comment.c 1.91 from 2021-10-30, this produced an
450 # assertion failure in fits_in_one_line.
451 cat <<EOF > code.c
452 int f(void)
453 {
454 if (0)
455 /* 0123456789012345678901 */;
456 }
457 EOF
458
459 # Indent tries hard to make the comment fit to the 34-character line
460 # length, but it is just not possible.
461 cat <<EOF > expected.out
462 int
463 f(void)
464 {
465 if (0)
466 /*
467 * 0123456789012345678901
468 */ ;
469 }
470 EOF
471
472 atf_check -o 'file:expected.out' \
473 "$indent" -l34 code.c -st
474 }
475
476
477 atf_init_test_cases()
478 {
479 atf_add_test_case 'option_unknown'
480 atf_add_test_case 'option_bool_trailing_garbage'
481 atf_add_test_case 'option_int_missing_argument'
482 atf_add_test_case 'option_profile_not_found'
483 atf_add_test_case 'option_buffer_overflow'
484 atf_add_test_case 'option_typedefs_not_found'
485 atf_add_test_case 'option_special_missing_param'
486 atf_add_test_case 'option_tabsize_negative'
487 atf_add_test_case 'option_tabsize_zero'
488 atf_add_test_case 'option_tabsize_large'
489 atf_add_test_case 'option_tabsize_very_large'
490 atf_add_test_case 'option_int_trailing_garbage'
491 atf_add_test_case 'option_cli_trailing_garbage'
492 atf_add_test_case 'option_indent_size_zero'
493 atf_add_test_case 'unterminated_comment_wrap'
494 atf_add_test_case 'unterminated_comment_nowrap'
495 atf_add_test_case 'in_place_wrong_backup'
496 atf_add_test_case 'argument_input_enoent'
497 atf_add_test_case 'argument_output_equals_input_name'
498 atf_add_test_case 'argument_output_equals_input_file'
499 atf_add_test_case 'argument_output_enoent'
500 atf_add_test_case 'argument_too_many'
501 atf_add_test_case 'unexpected_end_of_file'
502 atf_add_test_case 'unexpected_closing_brace_top_level'
503 atf_add_test_case 'unexpected_closing_brace_decl'
504 atf_add_test_case 'preprocessing_overflow'
505 atf_add_test_case 'preprocessing_unrecognized'
506 atf_add_test_case 'unbalanced_parentheses_1'
507 atf_add_test_case 'unbalanced_parentheses_2'
508 atf_add_test_case 'unbalanced_parentheses_3'
509 atf_add_test_case 'search_stmt_comment_segv'
510 atf_add_test_case 'search_stmt_fits_in_one_line'
511 }
512