xref: /netbsd-src/tests/usr.bin/indent/t_errors.sh (revision 5442314cf240a44fa261cd6bb97009c2adb01e90)
1#! /bin/sh
2# $NetBSD: t_errors.sh,v 1.30 2023/05/21 10:18:44 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_missing_argument'
61option_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
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 'in_place_wrong_backup'
218in_place_wrong_backup_body()
219{
220	cat <<-\EOF > code.c
221		int decl;
222	EOF
223	cp code.c code.c.orig
224
225	# Due to the strange backup suffix '/subdir', indent tries to create
226	# a file named 'code.c/subdir', but 'code.c' is already a regular
227	# file, not a directory.
228	atf_check -s 'exit:1' \
229	    -e 'inline:indent: code.c/subdir: Not a directory\n' \
230	    env SIMPLE_BACKUP_SUFFIX="/subdir" "$indent" code.c
231
232	# Since there was an early error, the original file is kept as is.
233	atf_check -o 'file:code.c.orig' \
234	    cat code.c
235}
236
237atf_test_case 'argument_input_enoent'
238argument_input_enoent_body()
239{
240	atf_check -s 'exit:1' \
241	    -e 'inline:indent: ./nonexistent.c: No such file or directory\n' \
242	    "$indent" ./nonexistent.c
243}
244
245atf_test_case 'argument_output_equals_input_name'
246argument_output_equals_input_name_body()
247{
248	echo '/* comment */' > code.c
249
250	atf_check -s 'exit:1' \
251	    -e 'inline:indent: input and output files must be different\n' \
252	    "$indent" code.c code.c
253}
254
255atf_test_case 'argument_output_equals_input_file'
256argument_output_equals_input_file_body()
257{
258	echo '/* comment */' > code.c
259
260	atf_check \
261	    "$indent" code.c ./code.c
262
263	# Oops, the file has become empty since the output is first emptied,
264	# before reading any of the input.
265	atf_check \
266	    cat code.c
267}
268
269atf_test_case 'argument_output_enoent'
270argument_output_enoent_body()
271{
272	expect_error \
273	    'indent: subdir/nonexistent.c: No such file or directory' \
274	    /dev/null subdir/nonexistent.c
275}
276
277atf_test_case 'argument_too_many'
278argument_too_many_body()
279{
280	echo '/* comment */' > arg1.c
281
282	expect_error \
283	    'indent: too many arguments: arg3.c' \
284	    arg1.c arg2.c arg3.c arg4.c
285}
286
287atf_test_case 'unexpected_end_of_file'
288unexpected_end_of_file_body()
289{
290	echo 'struct{' > code.c
291
292	expect_error \
293	    'error: code.c:1: Stuff missing from end of file' \
294	    code.c
295
296	atf_check \
297	    -o 'inline:struct {\n' \
298	    cat code.c
299}
300
301atf_test_case 'unexpected_closing_brace_top_level'
302unexpected_closing_brace_top_level_body()
303{
304	echo '}' > code.c
305
306	expect_error \
307	    'error: code.c:1: Statement nesting error' \
308	    code.c
309	atf_check \
310	    -o 'inline:}\n' \
311	    cat code.c
312}
313
314atf_test_case 'unexpected_closing_brace_decl'
315unexpected_closing_brace_decl_body()
316{
317	echo 'int i = 3};' > code.c
318
319	expect_error \
320	    'error: code.c:1: Statement nesting error' \
321	    code.c
322	# Despite the error message, the original file got overwritten with a
323	# best-effort rewrite of the code.
324	atf_check \
325	    -o 'inline:int		i = 3};\n' \
326	    cat code.c
327}
328
329atf_test_case 'preprocessing_overflow'
330preprocessing_overflow_body()
331{
332	cat <<-\EOF > code.c
333		#if 1
334		#if 2
335		#if 3
336		#if 4
337		#if 5
338		#if 6
339		#endif 6
340		#endif 5
341		#endif 4
342		#endif 3
343		#endif 2
344		#endif 1
345		#endif too much
346	EOF
347	cat <<-\EOF > stderr.exp
348		error: code.c:6: #if stack overflow
349		error: code.c:12: Unmatched #endif
350		error: code.c:13: Unmatched #endif
351	EOF
352
353	atf_check -s 'exit:1' \
354	    -e 'file:stderr.exp' \
355	    "$indent" code.c
356}
357
358atf_test_case 'preprocessing_unrecognized'
359preprocessing_unrecognized_body()
360{
361	cat <<-\EOF > code.c
362		#unknown
363		# 3 "file.c"
364		#elif 3
365		#else
366	EOF
367	cat <<-\EOF > stderr.exp
368		error: code.c:3: Unmatched #elif
369		error: code.c:4: Unmatched #else
370	EOF
371
372	atf_check -s 'exit:1' \
373	    -e 'file:stderr.exp' \
374	    "$indent" code.c
375}
376
377atf_test_case 'unbalanced_parentheses_1'
378unbalanced_parentheses_1_body()
379{
380	cat <<-\EOF > code.c
381		int var =
382		(
383		;
384		)
385		;
386	EOF
387	cat <<-\EOF > stderr.exp
388		error: code.c:3: Unbalanced parentheses
389		warning: code.c:4: Extra ')'
390	EOF
391
392	atf_check -s 'exit:1' -e 'file:stderr.exp' \
393	    "$indent" code.c
394}
395
396atf_test_case 'unbalanced_parentheses_2'
397unbalanced_parentheses_2_body()
398{
399	# '({...})' is the GCC extension "Statement expression".
400	cat <<-\EOF > code.c
401		int var =
402		(
403		{
404		1
405		}
406		)
407		;
408	EOF
409	cat <<-\EOF > stderr.exp
410		error: code.c:3: Unbalanced parentheses
411		warning: code.c:6: Extra ')'
412	EOF
413
414	atf_check -s 'exit:1' -e 'file:stderr.exp' \
415	    "$indent" code.c
416}
417
418atf_test_case 'unbalanced_parentheses_3'
419unbalanced_parentheses_3_body()
420{
421	# '({...})' is the GCC extension "Statement expression".
422	cat <<-\EOF > code.c
423		int var =
424		(
425		1
426		}
427		;
428	EOF
429	cat <<-\EOF > stderr.exp
430		error: code.c:4: Unbalanced parentheses
431		error: code.c:4: Statement nesting error
432	EOF
433
434	atf_check -s 'exit:1' -e 'file:stderr.exp' \
435	    "$indent" code.c
436}
437
438atf_test_case 'crash_comment_after_controlling_expression'
439crash_comment_after_controlling_expression_body()
440{
441	# Before 2023-05-11, indent crashed while
442	# trying to format the following artificial code.
443
444	printf '{if(expr\n)/*c*/;}\n' > code.c
445
446	cat <<\EOF > code.exp
447{
448	if (expr
449		) /* c */ ;
450}
451EOF
452
453	atf_check -o 'file:code.exp' \
454	    "$indent" code.c -st
455}
456
457atf_test_case 'comment_fits_in_one_line'
458comment_fits_in_one_line_body()
459{
460	# The comment is placed after 'if (0) ...'. Before NetBSD pr_comment.c
461	# 1.91 from 2021-10-30, this produced an assertion failure in
462	# fits_in_one_line.
463	cat <<EOF > code.c
464int f(void)
465{
466	if (0)
467		/* 0123456789012345678901 */;
468}
469EOF
470
471	# Indent tries hard to make the comment fit to the 34-character line
472	# length, but it is just not possible.
473	cat <<EOF > expected.out
474int
475f(void)
476{
477	if (0)
478		/*
479		 * 0123456789012345678901
480		 */ ;
481}
482EOF
483
484	atf_check -o 'file:expected.out' \
485	    "$indent" -l34 code.c -st
486}
487
488
489atf_test_case 'compound_literal'
490compound_literal_body()
491{
492	# Test handling of compound literals (C99 6.5.2.5), as well as casts.
493
494	cat <<EOF > code.c
495void
496function(void)
497{
498	origin =
499	((int)
500	((-1)*
501	(struct point){0,0}
502	)
503	);
504}
505EOF
506
507	sed '/^#/d' <<EOF > expected.out
508void
509function(void)
510{
511	origin =
512		    ((int)
513		     ((-1) *
514		      (struct point){
515# FIXME: the '{' is part of the expression, not a separate block.
516		0, 0
517# FIXME: the '}' is part of the expression, not a separate block.
518	}
519# FIXME: the ')' must be aligned with the corresponding '('.
520	)
521		    );
522}
523EOF
524	sed '/^#/d' <<EOF > expected.err
525# FIXME: The parentheses _are_ balanced, the '}' does not end the block.
526error: code.c:7: Unbalanced parentheses
527warning: code.c:8: Extra ')'
528warning: code.c:9: Extra ')'
529EOF
530
531	atf_check -s 'exit:1' -o 'file:expected.out' -e 'file:expected.err' \
532	    "$indent" -nfc1 -ci12 code.c -st
533}
534
535atf_init_test_cases()
536{
537	atf_add_test_case 'option_unknown'
538	atf_add_test_case 'option_bool_trailing_garbage'
539	atf_add_test_case 'option_int_missing_argument'
540	atf_add_test_case 'option_profile_not_found'
541	atf_add_test_case 'option_buffer_overflow'
542	atf_add_test_case 'option_typedefs_not_found'
543	atf_add_test_case 'option_special_missing_param'
544	atf_add_test_case 'option_tabsize_negative'
545	atf_add_test_case 'option_tabsize_zero'
546	atf_add_test_case 'option_tabsize_large'
547	atf_add_test_case 'option_tabsize_very_large'
548	atf_add_test_case 'option_int_trailing_garbage'
549	atf_add_test_case 'option_cli_trailing_garbage'
550	atf_add_test_case 'option_npro_trailing_garbage'
551	atf_add_test_case 'option_st_trailing_garbage'
552	atf_add_test_case 'option_version_trailing_garbage'
553	atf_add_test_case 'option_indent_size_zero'
554	atf_add_test_case 'unterminated_comment_wrap'
555	atf_add_test_case 'unterminated_comment_nowrap'
556	atf_add_test_case 'in_place_wrong_backup'
557	atf_add_test_case 'argument_input_enoent'
558	atf_add_test_case 'argument_output_equals_input_name'
559	atf_add_test_case 'argument_output_equals_input_file'
560	atf_add_test_case 'argument_output_enoent'
561	atf_add_test_case 'argument_too_many'
562	atf_add_test_case 'unexpected_end_of_file'
563	atf_add_test_case 'unexpected_closing_brace_top_level'
564	atf_add_test_case 'unexpected_closing_brace_decl'
565	atf_add_test_case 'preprocessing_overflow'
566	atf_add_test_case 'preprocessing_unrecognized'
567	atf_add_test_case 'unbalanced_parentheses_1'
568	atf_add_test_case 'unbalanced_parentheses_2'
569	atf_add_test_case 'unbalanced_parentheses_3'
570	atf_add_test_case 'crash_comment_after_controlling_expression'
571	atf_add_test_case 'comment_fits_in_one_line'
572	atf_add_test_case 'compound_literal'
573}
574