xref: /illumos-gate/usr/src/cmd/tail/tests/sun_solaris_tail.sh (revision 7932179448259e425d93162dedd251930575d83e)
1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or http://www.opensolaris.org/os/licensing.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21
22#
23# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24#
25
26#
27# Additional tests borrowed from ksh93 builtin tail test script
28# (usr/src/lib/libshell/common/tests/sun_solaris_builtin_tail).  Modified
29# to use /usr/bin/tail rather than the ksh93 builtin.
30#
31TAIL=/usr/bin/tail
32
33# test setup
34function err_exit
35{
36	print -u2 -n "\t"
37	print -u2 -r ${Command}[$1]: "${@:2}"
38	(( Errors < 127 && Errors++ ))
39}
40alias err_exit='err_exit $LINENO'
41
42set -o nounset
43Command=${0##*/}
44integer Errors=0
45
46# common functions
47function isvalidpid
48{
49        kill -0 ${1} 2>/dev/null && return 0
50        return 1
51}
52
53function waitpidtimeout
54{
55	integer pid=$1
56	float timeout=$2
57	float i
58	float -r STEP=0.5 # const
59
60	(( timeout=timeout/STEP ))
61
62	for (( i=0 ; i < timeout ; i+=STEP )) ; do
63		isvalidpid ${pid} || break
64		sleep ${STEP}
65	done
66
67	return 0
68}
69
70function myintseq
71{
72        integer i
73	float arg1=$1
74	float arg2=$2
75	float arg3=$3
76
77        case $# in
78                1)
79                        for (( i=1 ; i <= arg1 ; i++ )) ; do
80                                printf "%d\n" i
81                        done
82                        ;;
83                2)
84                        for (( i=arg1 ; i <= arg2 ; i++ )) ; do
85                                printf "%d\n" i
86                        done
87                        ;;
88                3)
89                        for (( i=arg1 ; i <= arg3 ; i+=arg2 )) ; do
90                                printf "%d\n" i
91                        done
92                        ;;
93                *)
94                        print -u2 -f "%s: Illegal number of arguments %d\n" "$0" $#
95			return 1
96                        ;;
97        esac
98
99        return 0
100}
101
102# quote input string but use single-backslash that "err_exit" prints
103# the strings correctly
104function singlebackslashquote
105{
106	typeset s
107	s="$(printf "%q\n" "$1")"
108	print -r "$s"
109	return 0
110}
111
112# quote input string but use double-backslash that "err_exit" prints
113# the strings correctly
114function doublebackslashquote
115{
116	typeset s
117	s="$(printf "%q\n" "$1")"
118	s="${s//\\/\\\\}"
119	print -r "$s"
120	return 0
121}
122
123
124# main
125builtin mktemp || err_exit "mktemp builtin not found"
126builtin rm || err_exit "rm builtin not found"
127# builtin tail || err_exit "tail builtin not found"
128
129typeset ocwd
130typeset tmpdir
131
132# create temporary test directory
133ocwd="$PWD"
134tmpdir="$(mktemp -t -d "test_sun_solaris_builtin_tail.XXXXXXXX")" || err_exit "Cannot create temporary directory"
135
136cd "${tmpdir}" || { err_exit "cd ${tmpdir} failed." ; exit $((Errors)) ; }
137
138
139# run tests:
140
141# test1: basic tests
142compound -a testcases=(
143	(
144		name="reverse_n"
145		input=$'hello\nworld'
146		compound -A tail_args=(
147			[legacy]=(   argv=( "-r"  ) )
148		)
149		expected_output=$'world\nhello'
150	)
151	(
152		name="revlist0n"
153		input=$'1\n2\n3\n4'
154		compound -A tail_args=(
155			[legacy]=(   argv=( "-0"	 ) )
156#			[std_like]=( argv=( "-n" "0" ) )
157		)
158		expected_output=$''
159	)
160	(
161		name="revlist0nr"
162		input=$'1\n2\n3\n4'
163		compound -A tail_args=(
164			[legacy]=(       argv=( "-0r"	      ) )
165#			[std_like]=(     argv=( "-n" "0" "-r" ) )
166#			[long_options]=( argv=( "--lines" "0" "--reverse" ) )
167		)
168		expected_output=$'' )
169	(
170		name="revlist1n"
171		input=$'1\n2\n3\n4'
172		compound -A tail_args=(
173			[legacy]=(       argv=( "-1"     ) )
174#			[std_like]=(     argv=( "-n" "1" ) )
175#			[long_options]=( argv=( "--lines" "1" ) )
176		)
177		expected_output=$'4' )
178	(
179		name="revlist1nr"
180		input=$'1\n2\n3\n4'
181		compound -A tail_args=(
182			[legacy]=(       argv=( "-1r" ) )
183#			[std_like]=(     argv=( "-n" "1" "-r" ) )
184#			[long_options]=( argv=( "--lines" "1" "--reverse" ) )
185		)
186		expected_output=$'4'
187	)
188	(
189		name="revlist2n"
190		input=$'1\n2\n3\n4'
191		compound -A tail_args=(
192			[legacy]=(   argv=( "-2"  ) )
193#			[std_like]=( argv=( "-n" "2" ) )
194		)
195		expected_output=$'3\n4'
196	)
197	(
198		name="revlist2nr"
199		input=$'1\n2\n3\n4'
200		compound -A tail_args=(
201			[legacy]=(   argv=( "-2r" ) )
202#			[std_like]=( argv=( "-n" "2" "-r" ) )
203			)
204		expected_output=$'4\n3'
205	)
206	(
207		name="revlist3nr"
208		input=$'1\n2\n3\n4'
209		compound -A tail_args=(
210			[legacy]=(   argv=( "-3r" ) )
211#			[std_like]=( argv=( "-n" "3" "-r" ) )
212		)
213		expected_output=$'4\n3\n2'
214	)
215	(
216		name="revlist2p"
217		input=$'1\n2\n3\n4'
218		compound -A tail_args=(
219			[legacy]=(   argv=( "+2"  ) )
220#			[std_like]=( argv=( "-n" "+2" ) )
221			)
222		expected_output=$'2\n3\n4'
223	)
224#	Note:  following test case trips up legacy Solaris 'tail' as well
225#	(
226#		name="revlist2pr"
227#		input=$'1\n2\n3\n4'
228#		compound -A tail_args=(
229#			[legacy]=(   argv=( "+2r" ) )
230#			[std_like]=( argv=( "-n" "+2" "-r" ) )
231#		)
232#		expected_output=$'4\n3\n2'
233#	)
234	(
235		name="revlist3p"
236		input=$'1\n2\n3\n4'
237		compound -A tail_args=(
238			[legacy]=(   argv=( "+3"  ) )
239			[std_like]=( argv=( "-n" "+3"  ) )
240		)
241		expected_output=$'3\n4'
242	)
243#	Note: following test case trips up legacy Solaris 'tail' as well
244#	(
245#		name="revlist3pr"
246#		input=$'1\n2\n3\n4'
247#		compound -A tail_args=(
248#			[legacy]=(   argv=( "+3r" ) )
249#			[std_like]=( argv=( "-n" "+3" "-r" ) )
250#		)
251#		expected_output=$'4\n3'
252#	)
253	(
254		name="revlist4p"
255		input=$'1\n2\n3\n4'
256		compound -A tail_args=(
257			[legacy]=(   argv=( "+4"  ) )
258#			[std_like]=( argv=( "-n" "+4"  ) )
259		)
260		expected_output=$'4'
261	)
262#	Note: following test case trips up legacy Solaris 'tail' as well
263#	(
264#		name="revlist4pr"
265#		input=$'1\n2\n3\n4'
266#		compound -A tail_args=(
267#			[legacy]=(   argv=( "+4r" ) )
268#			[std_like]=( argv=( "-n" "+4" "-r" ) )
269#		)
270#		expected_output=$'4'
271#	)
272	(
273		name="revlist5p"
274		input=$'1\n2\n3\n4'
275		compound -A tail_args=(
276			[legacy]=(   argv=( "+5"  ) )
277#			[std_like]=( argv=( "-n" "+5"  ) )
278		)
279		expected_output=$''
280	)
281#	Note: following test case trips up legacy Solaris 'tail' as well
282#	(
283#		name="revlist5pr"
284#		input=$'1\n2\n3\n4'
285#		compound -A tail_args=(
286#			[legacy]=(   argv=( "+5r" ) )
287#			[std_like]=( argv=( "-n" "+5" "-r" ) )
288#		)
289#		expected_output=$''
290#	)
291)
292
293for testid in "${!testcases[@]}" ; do
294	nameref tc=testcases[${testid}]
295
296	for argv_variants in "${!tc.tail_args[@]}" ; do
297		nameref argv=tc.tail_args[${argv_variants}].argv
298		output=$(
299				set -o pipefail
300	          		(trap "" PIPE ; print -r -- "${tc.input}") | $TAIL "${argv[@]}"
301			) || err_exit "test ${tc.name}/${argv_variants}: command failed with exit code $?"
302
303		[[ "${output}" == "${tc.expected_output}" ]] || err_exit "test ${tc.name}/${argv_variants}: Expected $(doublebackslashquote "${tc.expected_output}"), got $(doublebackslashquote "${output}")"
304	done
305done
306
307
308# test2: test "tail -r </etc/profile | rev -l" vs. "cat </etc/profile"
309[[ "$($TAIL -r </etc/profile | rev -l)" == "$( cat /etc/profile )" ]] || err_exit "'tail -r </etc/profile | rev -l' output does not match 'cat /etc/profile'"
310
311# Test case not applicable to FreeBSD 'tail'
312# test 3: ast-ksh.2009-05-05 "tail" builtin may crash if we pass unsupported long options
313#$SHELL -o errexit -c 'builtin tail ; print "hello" | tail --attack_of_chicken_monsters' >/dev/null 2>&1
314#(( $? == 2 )) || err_exit "expected exit code 2 for unsupported long option, got $?"
315
316
317# test 4: FIFO tests
318
319# FIFO test functions
320# (we use functions here to do propper garbage collection)
321function test_tail_fifo_1
322{
323	typeset tail_cmd="$1"
324	integer i
325	integer tail_pid=-1
326
327	# cleanup trap
328	trap "rm -f tailtestfifo tailout" EXIT
329
330	# create test FIFO
331	mkfifo tailtestfifo
332
333	${tail_cmd} -f <tailtestfifo >tailout &
334	tail_pid=$!
335
336	myintseq 20 >tailtestfifo
337
338	waitpidtimeout ${tail_pid} 5
339
340	if isvalidpid ${tail_pid} ; then
341		err_exit "test_tail_fifo_1: # tail hung (not expected)"
342		kill -KILL ${tail_pid}
343	fi
344
345	wait || err_exit "tail child returned non-zero exit code=$?"
346
347	[[ "$(cat tailout)" == $'11\n12\n13\n14\n15\n16\n17\n18\n19\n20' ]] || err_exit "test_tail_fifo_1: Expected $(doublebackslashquote '11\n12\n13\n14\n15\n16\n17\n18\n19\n20'), got $(doublebackslashquote "$(cat tailout)")"
348
349	return 0
350}
351
352function test_tail_fifo_2
353{
354	typeset tail_cmd="$1"
355	integer i
356	integer tail_pid=-1
357
358	# cleanup trap
359	trap "rm -f tailtestfifo tailout" EXIT
360
361	# create test FIFO
362	mkfifo tailtestfifo
363
364	${tail_cmd} -f tailtestfifo >tailout &
365	tail_pid=$!
366
367	myintseq 14 >tailtestfifo
368
369	waitpidtimeout ${tail_pid} 5
370
371	if isvalidpid ${tail_pid} ; then
372		[[ "$(cat tailout)" == $'5\n6\n7\n8\n9\n10\n11\n12\n13\n14' ]] || err_exit "test_tail_fifo_2: Expected $(doublebackslashquote $'5\n6\n7\n8\n9\n10\n11\n12\n13\n14'), got $(doublebackslashquote "$(cat tailout)")"
373
374		myintseq 15 >>tailtestfifo
375
376		waitpidtimeout ${tail_pid} 5
377
378		if isvalidpid ${tail_pid} ; then
379			kill -KILL ${tail_pid}
380		else
381			err_exit "test_tail_fifo_2: # tail exit with return code $? (not expected)"
382		fi
383	fi
384
385	wait || err_exit "tail child returned non-zero exit code=$?"
386
387	[[ "$(cat tailout)" == $'5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15' ]] || err_exit "test_tail_fifo_2: Expected $(doublebackslashquote $'5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15'), got $(doublebackslashquote "$(cat tailout)")"
388
389	return 0
390}
391
392# fixme: This should test /usr/bin/tail and /usr/xpg4/bin/tail in Solaris
393test_tail_fifo_1 "$TAIL"
394test_tail_fifo_2 "$TAIL"
395
396
397# test 5: "tail -f" tests
398function followtest1
399{
400	typeset -r FOLLOWFILE="followfile.txt"
401	typeset -r OUTFILE="outfile.txt"
402
403	typeset title="$1"
404	typeset testcmd="$2"
405	typeset usenewline=$3
406	typeset followstr=""
407	typeset newline=""
408	integer i
409	integer tailchild=-1
410
411	if ${usenewline} ; then
412		newline=$'\n'
413	fi
414
415	rm -f "${FOLLOWFILE}" "${OUTFILE}"
416	print -n "${newline}" > "${FOLLOWFILE}"
417
418	${testcmd} -f "${FOLLOWFILE}" >"${OUTFILE}" &
419	(( tailchild=$! ))
420
421	for (( i=0 ; i < 10 ; i++)) ; do
422		followstr+="${newline}${i}"
423		print -n "${i}${newline}" >>"${FOLLOWFILE}"
424		sleep 2
425
426		[[ "$( < "${OUTFILE}")" == "${followstr}" ]] || err_exit "${title}: Expected $(doublebackslashquote "${followstr}"), got "$(doublebackslashquote "$( < "${OUTFILE}")")""
427	done
428
429	kill -KILL ${tailchild} 2>/dev/null
430	#kill -TERM ${tailchild} 2>/dev/null
431	waitpidtimeout ${tailchild} 5
432
433	if isvalidpid ${tailchild} ; then
434		err_exit "${title}: tail pid=${tailchild} hung."
435		kill -KILL ${tailchild} 2>/dev/null
436	fi
437
438	wait ${tailchild} 2>/dev/null
439
440	rm -f "${FOLLOWFILE}" "${OUTFILE}"
441
442	return 0
443}
444
445followtest1 "test5a" "$TAIL" true
446# fixme: later we should test this, too:
447#followtest1 "test5b" "tail" false
448#followtest1 "test5c" "/usr/xpg4/bin/tail" true
449#followtest1 "test5d" "/usr/xpg4/bin/tail" false
450#followtest1 "test5e" "/usr/bin/tail" true
451#followtest1 "test5f" "/usr/bin/tail" false
452
453
454# test 6: "tail -f" tests
455function followtest2
456{
457	typeset -r FOLLOWFILE="followfile.txt"
458	typeset -r OUTFILE="outfile.txt"
459
460	typeset title="$1"
461	typeset testcmd="$2"
462	integer tailchild=-1
463
464	rm -f "${FOLLOWFILE}" "${OUTFILE}"
465
466	myintseq 50000 >"${FOLLOWFILE}"
467
468	${testcmd} -n 60000 -f "${FOLLOWFILE}" >"${OUTFILE}" &
469	(( tailchild=$! ))
470
471	sleep 10
472
473	kill -KILL ${tailchild} 2>/dev/null
474	#kill -TERM ${tailchild} 2>/dev/null
475	waitpidtimeout ${tailchild} 5
476
477	if isvalidpid ${tailchild} ; then
478		err_exit "${title}: tail pid=${tailchild} hung."
479		kill -KILL ${tailchild} 2>/dev/null
480	fi
481
482	wait ${tailchild} 2>/dev/null
483
484	# this tail should be an external process
485	outstr=$(/usr/bin/tail "${OUTFILE}") || err_exit "tail returned non-zero exit code $?"
486        [[ "${outstr}" == 49991*50000 ]] || err_exit "${title}: Expected match for 49991*50000, got "$(singlebackslashquote "${outstr}")""
487
488	rm -f "${FOLLOWFILE}" "${OUTFILE}"
489
490	return 0
491}
492
493followtest2 "test6a" "$TAIL"
494followtest2 "test6b" "$TAIL"
495# fixme: later we should test this, too:
496#followtest2 "test6c" "/usr/bin/tail"
497
498
499# cleanup
500cd "${ocwd}"
501rmdir "${tmpdir}" || err_exit "Cannot remove temporary directory ${tmpdir}".
502
503
504# tests done
505exit $((Errors))
506