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