xref: /netbsd-src/tests/usr.bin/indent/t_errors.sh (revision e914aa7b64febc5304e99be7f921a14dbe6ae4db)
1#! /bin/sh
2# $NetBSD: t_errors.sh,v 1.39 2025/01/03 23:37: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
30indent=$(atf_config_get usr.bin.indent.test_indent /usr/bin/indent)
31
32expect_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
44atf_test_case 'option_unknown'
45option_unknown_body()
46{
47	expect_error \
48	    'indent: Command line: unknown option "-Z-unknown"' \
49	    -Z-unknown
50}
51
52atf_test_case 'option_bool_trailing_garbage'
53option_bool_trailing_garbage_body()
54{
55	expect_error \
56	    'indent: Command line: unknown option "-bacchus"' \
57	    -bacchus
58}
59
60atf_test_case 'option_int_wrong_argument'
61option_int_wrong_argument_body()
62{
63	expect_error \
64	    'indent: Command line: argument "x" to option "-ts" must be an integer' \
65	    -tsx
66}
67
68atf_test_case 'option_profile_not_found'
69option_profile_not_found_body()
70{
71	expect_error \
72	    'indent: profile ./nonexistent: No such file or directory' \
73	    -P./nonexistent
74}
75
76atf_test_case 'option_typedefs_not_found'
77option_typedefs_not_found_body()
78{
79	expect_error \
80	    'indent: cannot open file ./nonexistent' \
81	    -U./nonexistent
82}
83
84atf_test_case 'option_tabsize_negative'
85option_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
92atf_test_case 'option_tabsize_zero'
93option_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
100atf_test_case 'option_tabsize_large'
101option_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
108atf_test_case 'option_tabsize_very_large'
109option_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
117atf_test_case 'option_indent_size_zero'
118option_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
125atf_test_case 'option_int_trailing_garbage'
126option_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
133atf_test_case 'option_cli_trailing_garbage'
134option_cli_trailing_garbage_body()
135{
136	expect_error \
137	    'indent: Command line: argument "3garbage" to option "-cli" must be numeric' \
138	    -cli3garbage
139}
140
141atf_test_case 'option_npro_trailing_garbage'
142option_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
149atf_test_case 'option_st_trailing_garbage'
150option_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
157atf_test_case 'option_version_trailing_garbage'
158option_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
165atf_test_case 'option_buffer_overflow'
166option_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
179atf_test_case 'option_special_missing_param'
180option_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
195atf_test_case 'unterminated_comment_wrap'
196unterminated_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
206atf_test_case 'unterminated_comment_nowrap'
207unterminated_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
217atf_test_case 'unterminated_char_constant'
218unterminated_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
228atf_test_case 'unterminated_string_literal'
229unterminated_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
239atf_test_case 'in_place_wrong_backup'
240in_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
259atf_test_case 'argument_input_enoent'
260argument_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
267atf_test_case 'argument_output_equals_input_name'
268argument_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
277atf_test_case 'argument_output_equals_input_file'
278argument_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
291atf_test_case 'argument_output_enoent'
292argument_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
299atf_test_case 'argument_too_many'
300argument_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
309atf_test_case 'unexpected_end_of_file'
310unexpected_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
323atf_test_case 'unexpected_closing_brace_top_level'
324unexpected_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
336atf_test_case 'unexpected_closing_brace_decl'
337unexpected_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
351atf_test_case 'unbalanced_parentheses'
352unbalanced_parentheses_body()
353{
354	cat <<-\EOF > code.c
355		int var =
356		(
357		;
358		)
359		;
360	EOF
361	cat <<-\EOF > stderr.exp
362		error: code.c:3: Unbalanced parentheses
363		warning: code.c:4: Extra ')'
364	EOF
365
366	atf_check -s 'exit:1' -e 'file:stderr.exp' \
367	    "$indent" code.c
368}
369
370atf_test_case 'gcc_statement_expression'
371gcc_statement_expression_body()
372{
373	# '({...})' is the GCC extension "Statement expression".
374	cat <<-\EOF > code.c
375		int var =
376		(
377		1
378		}
379		;
380	EOF
381	cat <<-\EOF > stderr.exp
382		error: code.c:4: Unbalanced parentheses
383		error: code.c:4: Statement nesting error
384	EOF
385
386	atf_check -s 'exit:1' -e 'file:stderr.exp' \
387	    "$indent" code.c
388}
389
390atf_test_case 'crash_comment_after_controlling_expression'
391crash_comment_after_controlling_expression_body()
392{
393	# Before 2023-05-11, indent crashed while
394	# trying to format the following artificial code.
395
396	printf '{if(expr\n)/*c*/;}\n' > code.c
397
398	cat <<\EOF > code.exp
399{
400	if (expr
401		)		/* c */
402		;
403}
404EOF
405
406	atf_check -o 'file:code.exp' \
407	    "$indent" code.c -st
408}
409
410atf_test_case 'comment_fits_in_one_line'
411comment_fits_in_one_line_body()
412{
413	# The comment is placed after 'if (0) ...'. Before NetBSD pr_comment.c
414	# 1.91 from 2021-10-30, this produced an assertion failure in
415	# fits_in_one_line.
416	cat <<EOF > code.c
417int f(void)
418{
419	if (0)
420		/* 0123456789012345678901 */;
421}
422EOF
423
424	# Indent tries hard to make the comment fit to the 34-character line
425	# length, but it is just not possible.
426	cat <<EOF > expected.out
427int
428f(void)
429{
430	if (0)
431		/*
432		 * 0123456789012345678901
433		 */;
434}
435EOF
436
437	atf_check -o 'file:expected.out' \
438	    "$indent" -l34 code.c -st
439}
440
441atf_test_case 'stack_overflow'
442stack_overflow_body()
443{
444	cat <<-EOF > code.c
445		{{{{{{{{{{ {{{{{{{{{{ {{{{{{{{{{ {{{{{{{{{{ {{{{{{{{{{
446		{{{{{{{{{{ {{{{{{{{{{ {{{{{{{{{{ {{{{{{{{{{ {{{{{{{{{{
447		{{{{{{{{{{ {{{{{{{{{{ {{{{{{{
448	EOF
449
450	atf_check \
451	    -s 'exit:1' \
452	    -e 'inline:error: code.c:3: Stuff missing from end of file\n' \
453	    "$indent" code.c
454
455	cat <<-EOF > code.c
456		{{{{{{{{{{ {{{{{{{{{{ {{{{{{{{{{ {{{{{{{{{{ {{{{{{{{{{
457		{{{{{{{{{{ {{{{{{{{{{ {{{{{{{{{{ {{{{{{{{{{ {{{{{{{{{{
458		{{{{{{{{{{ {{{{{{{{{{ {{{{{{{ {
459	EOF
460
461	atf_check \
462	    -s 'exit:1' \
463	    -e 'inline:error: code.c:3: Stuff missing from end of file\n' \
464	    "$indent" code.c
465}
466
467
468atf_init_test_cases()
469{
470	atf_add_test_case 'option_unknown'
471	atf_add_test_case 'option_bool_trailing_garbage'
472	atf_add_test_case 'option_int_wrong_argument'
473	atf_add_test_case 'option_profile_not_found'
474	atf_add_test_case 'option_buffer_overflow'
475	atf_add_test_case 'option_typedefs_not_found'
476	atf_add_test_case 'option_special_missing_param'
477	atf_add_test_case 'option_tabsize_negative'
478	atf_add_test_case 'option_tabsize_zero'
479	atf_add_test_case 'option_tabsize_large'
480	atf_add_test_case 'option_tabsize_very_large'
481	atf_add_test_case 'option_int_trailing_garbage'
482	atf_add_test_case 'option_cli_trailing_garbage'
483	atf_add_test_case 'option_npro_trailing_garbage'
484	atf_add_test_case 'option_st_trailing_garbage'
485	atf_add_test_case 'option_version_trailing_garbage'
486	atf_add_test_case 'option_indent_size_zero'
487	atf_add_test_case 'unterminated_comment_wrap'
488	atf_add_test_case 'unterminated_comment_nowrap'
489	atf_add_test_case 'unterminated_char_constant'
490	atf_add_test_case 'unterminated_string_literal'
491	atf_add_test_case 'in_place_wrong_backup'
492	atf_add_test_case 'argument_input_enoent'
493	atf_add_test_case 'argument_output_equals_input_name'
494	atf_add_test_case 'argument_output_equals_input_file'
495	atf_add_test_case 'argument_output_enoent'
496	atf_add_test_case 'argument_too_many'
497	atf_add_test_case 'unexpected_end_of_file'
498	atf_add_test_case 'unexpected_closing_brace_top_level'
499	atf_add_test_case 'unexpected_closing_brace_decl'
500	atf_add_test_case 'unbalanced_parentheses'
501	atf_add_test_case 'gcc_statement_expression'
502	atf_add_test_case 'crash_comment_after_controlling_expression'
503	atf_add_test_case 'comment_fits_in_one_line'
504	atf_add_test_case 'stack_overflow'
505}
506