#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
#

#
# Written by Roland Mainz <roland.mainz@nrubsig.org>
#

#
# Test whether the ksh93/libcmd tail builtin is compatible to
# Solaris/SystemV { /usr/bin/tail, /usr/xpg4/bin/tail } and
# POSIX "tail" and passes all definitions made in the PSARC
# cases
#

# test setup
function err_exit
{
	print -u2 -n '\t'
	print -u2 -r ${Command}[$1]: "${@:2}"
	(( Errors++ ))
}
alias err_exit='err_exit $LINENO'

set -o nounset
Command=${0##*/}
integer Errors=0

# common functions
function isvalidpid
{
        kill -n 0 ${1} 2>'/dev/null' && return 0
        return 1
}

function waitpidtimeout
{
	integer pid=$1
	float timeout=$2
	float i
	float -r STEP=0.5 # const

	(( timeout=timeout/STEP ))
	
	for (( i=0 ; i < timeout ; i+=STEP )) ; do
		isvalidpid ${pid} || break
		sleep ${STEP}
	done
	
	return 0
}

function myintseq
{
        integer i
	float arg1=$1
	float arg2=$2
	float arg3=$3

        case $# in
                1)
                        for (( i=1 ; i <= arg1 ; i++ )) ; do
                                printf '%d\n' i
                        done
                        ;;
                2)
                        for (( i=arg1 ; i <= arg2 ; i++ )) ; do
                                printf '%d\n' i
                        done
                        ;;
                3)
                        for (( i=arg1 ; i <= arg3 ; i+=arg2 )) ; do
                                printf '%d\n' i
                        done
                        ;;
                *)
                        print -u2 -f '%s: Illegal number of arguments %d\n' "$0" $#
			return 1
                        ;;
        esac
        
        return 0
}

# quote input string but use single-backslash that "err_exit" prints
# the strings correctly
function singlebackslashquote
{
	typeset s
	s="${ printf '%q\n' "$1" ; }"
	print -r "$s"
	return 0
}

# quote input string but use double-backslash that "err_exit" prints
# the strings correctly
function doublebackslashquote
{
	typeset s
	s="${ printf '%q\n' "$1" ; }"
	s="${s//\\/\\\\}"
	print -r "$s"
	return 0
}


# main
builtin mktemp || err_exit 'mktemp builtin not found'
builtin rm || err_exit 'rm builtin not found'
builtin tail || err_exit 'tail builtin not found'

typeset ocwd
typeset tmpdir

# create temporary test directory
ocwd="$PWD"
tmpdir="${ mktemp -t -d 'test_sun_solaris_builtin_tail.XXXXXXXX' ; }" || err_exit 'Cannot create temporary directory'

cd "${tmpdir}" || { err_exit "cd ${tmpdir} failed." ; exit $((Errors<125?Errors:125)) ; }


# run tests:

# test 4: FIFO tests
# FIFO test functions
# (we use functions here to do propper garbage collection)
function test_tail_fifo_1
{
	typeset tail_cmd="$1"
	integer i
	integer tail_pid=-1
	
	# cleanup trap
	trap 'rm -f tailtestfifo tailout' EXIT

	# create test FIFO
	/usr/bin/mkfifo 'tailtestfifo'

	${tail_cmd} -f <'tailtestfifo' >'tailout' &
	(( tail_pid=$! ))

	myintseq 20 >'tailtestfifo'

	waitpidtimeout ${tail_pid} 5

	if isvalidpid ${tail_pid} ; then
		err_exit 'test_tail_fifo_1: # tail hung (not expected)'
		kill -s KILL ${tail_pid}
	fi

	wait || err_exit "tail child returned non-zero exit code=$?"
	
	[[ "$(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)")"

	return 0
}


# test 5: "tail -f" tests
function followtest1
{
	typeset -r FOLLOWFILE='followfile.txt'
	typeset -r OUTFILE='outfile.txt'

	typeset title="$1"
	typeset testcmd="$2"
	typeset usenewline=$3
	typeset followstr=''
	typeset newline=''
	integer i
	integer tailchild=-1

	if ${usenewline} ; then
		newline=$'\n'
	fi
	
	rm -f "${FOLLOWFILE}" "${OUTFILE}"
	print -n "${newline}" > "${FOLLOWFILE}"

	${testcmd} -f "${FOLLOWFILE}" >"${OUTFILE}" &
	(( tailchild=$! ))

	for (( i=0 ; i < 10 ; i++ )) ; do
		followstr+="${newline}${i}"
		print -n "${i}${newline}" >>"${FOLLOWFILE}"
		sleep 2

		[[ "$( < "${OUTFILE}")" == "${followstr}" ]] || err_exit "${title}: Expected $(doublebackslashquote "${followstr}"), got \"$(doublebackslashquote "$( < "${OUTFILE}")")\""
	done

	kill -s TERM ${tailchild} 2>'/dev/null'
	waitpidtimeout ${tailchild} 5
	
	if isvalidpid ${tailchild} ; then
		err_exit "${title}: tail pid=${tailchild} hung."
		kill -s KILL ${tailchild} 2>'/dev/null'
	fi
	
	wait ${tailchild} 2>'/dev/null'
	
	rm -f "${FOLLOWFILE}" "${OUTFILE}"

	return 0
}


# test 6: "tail -f" tests
function followtest2
{
	typeset -r FOLLOWFILE='followfile.txt'
	typeset -r OUTFILE='outfile.txt'

	typeset title="$1"
	typeset testcmd="$2"
	integer tailchild=-1

	rm -f "${FOLLOWFILE}" "${OUTFILE}"

	myintseq 50000 >"${FOLLOWFILE}"

	${testcmd} -n 60000 -f "${FOLLOWFILE}" >"${OUTFILE}" &
	(( tailchild=$! ))
	
	sleep 10

	kill -s TERM ${tailchild} 2>'/dev/null'
	waitpidtimeout ${tailchild} 5
	
	if isvalidpid ${tailchild} ; then
		err_exit "${title}: tail pid=${tailchild} hung."
		kill -s KILL ${tailchild} 2>'/dev/null'
	fi
	
	wait ${tailchild} 2>'/dev/null'
		
	# this tail should be an external process
	outstr=$(/usr/bin/tail "${OUTFILE}") || err_exit "${title}: tail returned non-zero exit code $?"
        [[ "${outstr}" == 49991*50000 ]] || err_exit "${title}: Expected match for 49991*50000, got \"$(singlebackslashquote "${outstr}")\""	
	
	rm -f "${FOLLOWFILE}" "${OUTFILE}"

	return 0
}

builtin tail



# this one must run first to cause hangs in followtest[12]
test_tail_fifo_1 '/usr/bin/tail'

# these two will hang
followtest1 'test5a' 'tail' true
followtest2 'test6a' 'tail'


# cleanup
cd "${ocwd}"
rmdir "${tmpdir}" || err_exit "Cannot remove temporary directory ${tmpdir}."


# tests done
exit $((Errors<125?Errors:125))
