# These tests are design especially for the vfork() implementation
# of exec where sh -c must be used and thus we must take extra care
# in quoting arguments to exec.

source [file dirname [info script]]/testing.tcl

needs cmd exec
constraint cmd signal
constraint cmd wait
constraint cmd alarm
constraint cmd after

# Jim needs [pipe] to implement [open |command]
if {[testConstraint tcl]} {
	testConstraint pipe 1
} else {
	constraint cmd pipe
}

# Some Windows platforms (e.g. AppVeyor) produce ENOSPC rather than killing
# the child with SIGPIPE). So turn off this test for that platform
constraint expr nomingw32 {![info exists env(MSYSTEM)] || $env(MSYSTEM) ne "MINGW32"}

set d \"
set s '
set b \\

array set saveenv [array get env]

test exec2-1.1 "Quoting - Result" {
	exec echo ${d}double quoted${d} ${s}single quoted${s} ${b}backslash quoted${b}
} "\"double\ quoted\"\ 'single quoted'\ \\backslash\ quoted\\"

test exec2-1.2 "Quoting - Word Grouping" {
	string trim [exec echo ${d}double quoted${d} ${s}single quoted${s} ${b}backslash quoted${b} | wc -w]
} {6}

test exec2-2.1 "Add to exec environment" {
	set env(TESTENV) "the value"
	exec printenv | sed -n -e /^TESTENV=/p
} {TESTENV=the value}

test exec2-2.2 "Remove from exec environment" {
	set env(TESTENV2) "new value"
	unset env(TESTENV)
	exec printenv | sed -n -e /^TESTENV=/p
} {}


test exec2-2.3 "Remove all exec environment" {
	array unset env *
	exec printenv | sed -n -e /^TESTENV2=/p
} {}

test exec2-2.4 "Remove all env var" {
	unset -nocomplain env
	exec printenv | sed -n -e /^TESTENV2=/p
} {}

array set env [array get saveenv]

test exec2-3.1 "close pipeline return value" pipe {
	set f [open |false]
	set rc [catch {close $f} msg opts]
	lassign [dict get $opts -errorcode] status pid exitcode
	list $rc $msg $status $exitcode
} {1 {child process exited abnormally} CHILDSTATUS 1}

test exec2-3.2 "close pipeline return value" -constraints {pipe signal nomingw32} -body {
	signal ignore SIGPIPE
	# Write more than 64KB which is maximum size of the pipe buffers
	# on all systems we have seen
	set bigstring [string repeat a 100000]
	set f [open [list |cat << $bigstring]]
	set rc [catch {close $f} msg opts]
	lassign [dict get $opts -errorcode] status pid exitcode
	list $rc $msg $status $exitcode
} -match glob -result {1 {child killed*} CHILDKILLED SIGPIPE}

test exec2-3.3 "close pipeline with SIGPIPE blocked" -constraints {pipe signal nomingw32} -body {
	signal block SIGPIPE
	# Write more than 64KB which is maximum size of the pipe buffers
	# on all systems we have seen
	set bigstring [string repeat a 100000]
	set f [open [list |cat << $bigstring 2>/dev/null]]
	set rc [catch {close $f} msg opts]
	lassign [dict get $opts -errorcode] status pid exitcode
	list $rc $msg $status $exitcode
} -match glob -result {1 {child process exited*} CHILDSTATUS 1} -cleanup {
	signal default SIGPIPE
}

test exec2-3.4 "wait for background task" -constraints wait -body {
	set pid [exec sleep 0.1 &]
	lassign [wait $pid] status newpid exitcode
	if {$pid != $newpid} {
		error "wait $pid returned pid=$newpid"
	} else {
		list $status $exitcode
	}
} -result {CHILDSTATUS 0}

test exec2-4.1 {redirect from invalid filehandle} -body {
	exec cat <@bogus
} -returnCodes error -match glob -result {*"bogus"}

test exec2-4.2 {env is invalid dict} -constraints jim -body {
	set saveenv $env
	lappend env bogus
	catch {exec pwd}
} -result {0} -cleanup {
	set env $saveenv
}

test exec2-4.3 {signalled process during foreground exec} -constraints {jim alarm} -body {
	# We need to exec a pipeline and then have one process
	# be killed by a signal
	exec [info nameofexecutable] -e {alarm 0.1; sleep 0.5}
} -returnCodes error -result {child killed by signal SIGALRM}

test exec2-4.4 {exec - consecutive |} -body {
	exec echo | |  test
} -returnCodes error -result {illegal use of | or |& in command}

test exec2-4.5 {exec - consecutive | with &} -body {
	exec echo | |  test &
} -returnCodes error -result {illegal use of | or |& in command}

test exec2-4.6 {exec - illegal channel} -body {
	exec echo hello >@nonexistent
} -returnCodes error -match glob -result {*"nonexistent"}

test exec2-5.1 {wait with invalid pid} wait {
	wait 9999999
} {NONE -1 -1}

test exec2-5.2 {wait with invalid pid} -constraints wait -body {
	wait blah
} -returnCodes error -result {expected integer but got "blah"}

test exec2-5.3 {wait - bad args} -constraints wait -body {
	wait too many args
} -returnCodes error -result {wrong # args: should be "wait ?-nohang? ?pid?"}

test exec2-5.4 {wait -nohang} -constraints wait -body {
	set pid [exec sleep 0.2 &]
	# first wait will do nothing as the process is not finished
	wait -nohang $pid
	wait $pid
} -match glob -result {CHILDSTATUS * 0}

test exec2-5.5 {wait for all children} -constraints {after jim} -body {
	# We want to have children finish at different times
	# so that we test the handling of the wait table
	foreach i {0.1 0.2 0.6 0.5 0.4 0.3} {
		exec sleep $i &
	}
	# reap zombies, there should not be any
	wait
	after 300
	# reap zombies, 2-3 should be finished now
	wait
	after 400
	# reap zombies, all processes should be finished now
	wait
} -result {}

test exec2-5.6 {wait -1 to wait for any child} -constraints {after jim nomingw32} -body {
	set pid [exec sleep 0.1 &]
	# Now wait for any child
	lassign [wait -1] status waitpid code
	list $status $($waitpid == $pid) $code
} -result {CHILDSTATUS 1 0}

testreport
