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