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