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