xref: /netbsd-src/tests/usr.bin/indent/t_errors.sh (revision 122b5006ee1bd67145794b4cde92f4fe4781a5ec)
1#! /bin/sh
2# $NetBSD: t_errors.sh,v 1.17 2021/10/30 16:57: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# $FreeBSD$
29
30# Tests for error handling in indent.
31
32indent=$(atf_config_get usr.bin.indent.test_indent /usr/bin/indent)
33nl='
34'
35
36expect_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
48atf_test_case 'option_unknown'
49option_unknown_body()
50{
51	expect_error \
52	    'indent: Command line: unknown option "-Z-unknown"' \
53	    -Z-unknown
54}
55
56atf_test_case 'option_bool_trailing_garbage'
57option_bool_trailing_garbage_body()
58{
59	expect_error \
60	    'indent: Command line: unknown option "-bacchus"' \
61	    -bacchus
62}
63
64atf_test_case 'option_int_missing_argument'
65option_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
72atf_test_case 'option_profile_not_found'
73option_profile_not_found_body()
74{
75	expect_error \
76	    'indent: profile ./nonexistent: No such file or directory' \
77	    -P./nonexistent
78}
79
80atf_test_case 'option_typedefs_not_found'
81option_typedefs_not_found_body()
82{
83	expect_error \
84	    'indent: cannot open file ./nonexistent' \
85	    -U./nonexistent
86}
87
88atf_test_case 'option_tabsize_negative'
89option_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
96atf_test_case 'option_tabsize_zero'
97option_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
104atf_test_case 'option_tabsize_large'
105option_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
113atf_test_case 'option_tabsize_very_large'
114option_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
122atf_test_case 'option_indent_size_zero'
123option_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
130atf_test_case 'option_int_trailing_garbage'
131option_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
138atf_test_case 'option_cli_trailing_garbage'
139option_cli_trailing_garbage_body()
140{
141	expect_error \
142	    'indent: Command line: argument "3garbage" to option "-cli" must be numeric' \
143	    -cli3garbage
144}
145
146atf_test_case 'option_buffer_overflow'
147option_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
160atf_test_case 'option_special_missing_param'
161option_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
177atf_test_case 'unterminated_comment'
178unterminated_comment_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
188atf_test_case 'in_place_wrong_backup'
189in_place_wrong_backup_body()
190{
191	cat <<-\EOF > code.c
192		int decl;
193	EOF
194	cp code.c code.c.orig
195
196	# Due to the strange backup suffix '/subdir', indent tries to create
197	# a file named 'code.c/subdir', but 'code.c' is already a regular
198	# file, not a directory.
199	atf_check -s 'exit:1' \
200	    -e 'inline:indent: code.c/subdir: Not a directory'"$nl" \
201	    env SIMPLE_BACKUP_SUFFIX="/subdir" "$indent" code.c
202
203	# Since there was an early error, the original file is kept as is.
204	atf_check -o 'file:code.c.orig' \
205	    cat code.c
206}
207
208atf_test_case 'argument_input_enoent'
209argument_input_enoent_body()
210{
211	atf_check -s 'exit:1' \
212	    -e 'inline:indent: ./nonexistent.c: No such file or directory'"$nl" \
213	    "$indent" ./nonexistent.c
214}
215
216atf_test_case 'argument_output_equals_input_name'
217argument_output_equals_input_name_body()
218{
219	echo '/* comment */' > code.c
220
221	atf_check -s 'exit:1' \
222	    -e 'inline:indent: input and output files must be different'"$nl" \
223	    "$indent" code.c code.c
224}
225
226atf_test_case 'argument_output_equals_input_file'
227argument_output_equals_input_file_body()
228{
229	echo '/* comment */' > code.c
230
231	atf_check \
232	    "$indent" code.c ./code.c
233
234	# Oops, the file has become empty since the output is first emptied,
235	# before reading any of the input.
236	atf_check \
237	    cat code.c
238}
239
240atf_test_case 'argument_output_enoent'
241argument_output_enoent_body()
242{
243	expect_error \
244	    'indent: subdir/nonexistent.c: No such file or directory' \
245	    /dev/null subdir/nonexistent.c
246}
247
248atf_test_case 'argument_too_many'
249argument_too_many_body()
250{
251	echo '/* comment */' > arg1.c
252
253	expect_error \
254	    'indent: too many arguments: arg3.c' \
255	    arg1.c arg2.c arg3.c arg4.c
256}
257
258atf_test_case 'unexpected_end_of_file'
259unexpected_end_of_file_body()
260{
261	echo 'struct{' > code.c
262
263	expect_error \
264	    'error: code.c:1: Stuff missing from end of file' \
265	    code.c
266
267	atf_check \
268	    -o 'inline:struct {'"$nl" \
269	    cat code.c
270}
271
272atf_test_case 'unexpected_closing_brace_top_level'
273unexpected_closing_brace_top_level_body()
274{
275	echo '}' > code.c
276
277	expect_error \
278	    'error: code.c:1: Statement nesting error' \
279	    code.c
280	atf_check \
281	    -o 'inline:}'"$nl" \
282	    cat code.c
283}
284
285atf_test_case 'unexpected_closing_brace_decl'
286unexpected_closing_brace_decl_body()
287{
288	echo 'int i = 3};' > code.c
289
290	expect_error \
291	    'error: code.c:1: Statement nesting error' \
292	    code.c
293	# Despite the error message, the original file got overwritten with a
294	# best-effort rewrite of the code.
295	atf_check \
296	    -o 'inline:int		i = 3};'"$nl" \
297	    cat code.c
298}
299
300atf_test_case 'preprocessing_overflow'
301preprocessing_overflow_body()
302{
303	cat <<-\EOF > code.c
304		#if 1
305		#if 2
306		#if 3
307		#if 4
308		#if 5
309		#if 6
310		#endif 6
311		#endif 5
312		#endif 4
313		#endif 3
314		#endif 2
315		#endif 1
316		#endif too much
317	EOF
318	cat <<-\EOF > stderr.exp
319		error: code.c:6: #if stack overflow
320		error: code.c:12: Unmatched #endif
321		error: code.c:13: Unmatched #endif
322	EOF
323
324	atf_check -s 'exit:1' \
325	    -e 'file:stderr.exp' \
326	    "$indent" code.c
327}
328
329atf_test_case 'preprocessing_unrecognized'
330preprocessing_unrecognized_body()
331{
332	cat <<-\EOF > code.c
333		#unknown
334		# 3 "file.c"
335		#elif 3
336		#else
337	EOF
338	cat <<-\EOF > stderr.exp
339		error: code.c:1: Unrecognized cpp directive
340		error: code.c:2: Unrecognized cpp directive
341		error: code.c:3: Unmatched #elif
342		error: code.c:4: Unmatched #else
343	EOF
344
345	atf_check -s 'exit:1' \
346	    -e 'file:stderr.exp' \
347	    "$indent" code.c
348}
349
350atf_test_case 'unbalanced_parentheses_1'
351unbalanced_parentheses_1_body()
352{
353	cat <<-\EOF > code.c
354		int var =
355		(
356		;
357		)
358		;
359	EOF
360	cat <<-\EOF > stderr.exp
361		error: code.c:3: Unbalanced parentheses
362		warning: code.c:4: Extra ')'
363	EOF
364
365	atf_check -s 'exit:1' -e 'file:stderr.exp' \
366	    "$indent" code.c
367}
368
369atf_test_case 'unbalanced_parentheses_2'
370unbalanced_parentheses_2_body()
371{
372	# '({...})' is the GCC extension "Statement expression".
373	cat <<-\EOF > code.c
374		int var =
375		(
376		{
377		1
378		}
379		)
380		;
381	EOF
382	cat <<-\EOF > stderr.exp
383		error: code.c:3: Unbalanced parentheses
384		warning: code.c:6: Extra ')'
385	EOF
386
387	atf_check -s 'exit:1' -e 'file:stderr.exp' \
388	    "$indent" code.c
389}
390
391atf_test_case 'unbalanced_parentheses_3'
392unbalanced_parentheses_3_body()
393{
394	# '({...})' is the GCC extension "Statement expression".
395	cat <<-\EOF > code.c
396		int var =
397		(
398		1
399		}
400		;
401	EOF
402	cat <<-\EOF > stderr.exp
403		error: code.c:4: Unbalanced parentheses
404		error: code.c:4: Statement nesting error
405	EOF
406
407	atf_check -s 'exit:1' -e 'file:stderr.exp' \
408	    "$indent" code.c
409}
410
411atf_test_case 'search_stmt_comment_segv'
412search_stmt_comment_segv_body()
413{
414	# As of NetBSD indent.c 1.188 from 2021-10-30, indent crashes while
415	# trying to format the following artificial code.
416
417	printf '{if(expr\n)/*c*/;}\n' > code.c
418
419	cat <<\EOF > code.exp
420{
421	if (expr
422		)		/* c */
423		;
424}
425EOF
426
427	# TODO: actually produce code.exp instead of an assertion failure.
428	atf_check -s 'signal' -o 'ignore' -e 'match:assert' \
429	    "$indent" code.c -st
430}
431
432atf_test_case 'search_stmt_fits_in_one_line'
433search_stmt_fits_in_one_line_body()
434{
435	# The comment is placed after 'if (0) ...', where it is processed
436	# by search_stmt_comment. That function redirects the input buffer to
437	# a temporary buffer that is not guaranteed to be terminated by '\n'.
438	# Before NetBSD pr_comment.c 1.91 from 2021-10-30, this produced an
439	# assertion failure in fits_in_one_line.
440	cat <<EOF > code.c
441int f(void)
442{
443	if (0)
444		/* 0123456789012345678901 */;
445}
446EOF
447
448	# Indent tries hard to make the comment fit to the 34-character line
449	# length, but it is just not possible.
450	cat <<EOF > expected.out
451int
452f(void)
453{
454	if (0)
455		/*
456		 * 0123456789012345678901
457		  */ ;
458}
459EOF
460
461	atf_check -o 'file:expected.out' \
462	    "$indent" -l34 code.c -st
463}
464
465
466atf_init_test_cases()
467{
468	atf_add_test_case 'option_unknown'
469	atf_add_test_case 'option_bool_trailing_garbage'
470	atf_add_test_case 'option_int_missing_argument'
471	atf_add_test_case 'option_profile_not_found'
472	atf_add_test_case 'option_buffer_overflow'
473	atf_add_test_case 'option_typedefs_not_found'
474	atf_add_test_case 'option_special_missing_param'
475	atf_add_test_case 'option_tabsize_negative'
476	atf_add_test_case 'option_tabsize_zero'
477	atf_add_test_case 'option_tabsize_large'
478	atf_add_test_case 'option_tabsize_very_large'
479	atf_add_test_case 'option_int_trailing_garbage'
480	atf_add_test_case 'option_cli_trailing_garbage'
481	atf_add_test_case 'option_indent_size_zero'
482	atf_add_test_case 'unterminated_comment'
483	atf_add_test_case 'in_place_wrong_backup'
484	atf_add_test_case 'argument_input_enoent'
485	atf_add_test_case 'argument_output_equals_input_name'
486	atf_add_test_case 'argument_output_equals_input_file'
487	atf_add_test_case 'argument_output_enoent'
488	atf_add_test_case 'argument_too_many'
489	atf_add_test_case 'unexpected_end_of_file'
490	atf_add_test_case 'unexpected_closing_brace_top_level'
491	atf_add_test_case 'unexpected_closing_brace_decl'
492	atf_add_test_case 'preprocessing_overflow'
493	atf_add_test_case 'preprocessing_unrecognized'
494	atf_add_test_case 'unbalanced_parentheses_1'
495	atf_add_test_case 'unbalanced_parentheses_2'
496	atf_add_test_case 'unbalanced_parentheses_3'
497	atf_add_test_case 'search_stmt_comment_segv'
498	atf_add_test_case 'search_stmt_fits_in_one_line'
499}
500