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