1# Commands covered: exec 2# 3# This file contains a collection of tests for one or more of the Tcl built-in 4# commands. Sourcing this file into Tcl runs the tests and generates output 5# for errors. No output means no errors were found. 6# 7# Copyright © 1991-1994 The Regents of the University of California. 8# Copyright © 1994-1997 Sun Microsystems, Inc. 9# Copyright © 1998-1999 Scriptics Corporation. 10# 11# See the file "license.terms" for information on usage and redistribution of 12# this file, and for a DISCLAIMER OF ALL WARRANTIES. 13 14# There is no point in running Valgrind on cases where [exec] forks but then 15# fails and the child process doesn't go through full cleanup. 16 17if {"::tcltest" ni [namespace children]} { 18 package require tcltest 2.5 19 namespace import -force ::tcltest::* 20} 21 22loadTestedCommands 23catch [list package require -exact tcl::test [info patchlevel]] 24package require tcltests 25 26# All tests require the "exec" command. 27# Skip them if exec is not defined. 28testConstraint exec [llength [info commands exec]] 29# Some skips when running in a macOS CI environment 30testConstraint noosxCI [expr {![info exists ::env(MAC_CI)]}] 31 32unset -nocomplain path 33 34# Utilities that are like bourne shell stalwarts, but cross-platform. 35set path(echo) [makeFile { 36 puts -nonewline [lindex $argv 0] 37 foreach str [lrange $argv 1 end] { 38 puts -nonewline " $str" 39 } 40 puts {} 41 exit 42} echo] 43set path(echo2) [makeFile { 44 puts stdout [join $argv] 45 puts stderr [lindex $argv 1] 46 exit 47} echo2] 48set path(cat) [makeFile { 49 if {$argv eq ""} { 50 set argv - 51 } 52 fconfigure stdout -translation binary 53 foreach name $argv { 54 if {$name eq "-"} { 55 set f stdin 56 } elseif {[catch {open $name r} f] != 0} { 57 puts stderr $f 58 continue 59 } 60 fconfigure $f -translation binary 61 while {[eof $f] == 0} { 62 puts -nonewline [read $f] 63 } 64 if {$f ne "stdin"} { 65 close $f 66 } 67 } 68 exit 69} cat] 70set path(wc) [makeFile { 71 set data [read stdin] 72 set lines [regsub -all "\n" $data {} dummy] 73 set words [regsub -all "\[^ \t\n]+" $data {} dummy] 74 set chars [string length $data] 75 puts [format "%8.d%8.d%8.d" $lines $words $chars] 76 exit 77} wc] 78set path(sh) [makeFile { 79 if {[lindex $argv 0] ne "-c"} { 80 error "sh: unexpected arguments $argv" 81 } 82 set cmd [lindex $argv 1] 83 lappend cmd ";" 84 set newcmd {} 85 foreach arg $cmd { 86 if {$arg eq ";"} { 87 exec >@stdout 2>@stderr [info nameofexecutable] {*}$newcmd 88 set newcmd {} 89 continue 90 } 91 if {$arg eq "1>&2"} { 92 set arg >@stderr 93 } 94 lappend newcmd $arg 95 } 96 exit 97} sh] 98set path(sh2) [makeFile { 99 if {[lindex $argv 0] ne "-c"} { 100 error "sh: unexpected arguments $argv" 101 } 102 set cmd [lindex $argv 1] 103 lappend cmd ";" 104 set newcmd {} 105 foreach arg $cmd { 106 if {$arg eq ";"} { 107 exec -ignorestderr >@stdout [info nameofexecutable] {*}$newcmd 108 set newcmd {} 109 continue 110 } 111 lappend newcmd $arg 112 } 113 exit 114} sh2] 115set path(sleep) [makeFile { 116 after [expr {$argv*1000}] 117 exit 118} sleep] 119set path(exit) [makeFile { 120 exit $argv 121} exit] 122 123proc readfile filename { 124 set f [open $filename] 125 set d [read $f] 126 close $f 127 return [string trimright $d \n] 128} 129 130# ---------------------------------------------------------------------- 131# Basic operations. 132 133test exec-1.1 {basic exec operation} {exec} { 134 exec [interpreter] $path(echo) a b c 135} "a b c" 136test exec-1.2 {pipelining} {exec stdio} { 137 exec [interpreter] $path(echo) a b c d | [interpreter] $path(cat) | [interpreter] $path(cat) 138} "a b c d" 139test exec-1.3 {pipelining} {exec stdio} { 140 set a [exec [interpreter] $path(echo) a b c d | [interpreter] $path(cat) | [interpreter] $path(wc)] 141 list [scan $a "%d %d %d" b c d] $b $c 142} {3 1 4} 143set arg {12345678901234567890123456789012345678901234567890} 144set arg "$arg$arg$arg$arg$arg$arg" 145test exec-1.4 {long command lines} {exec} { 146 exec [interpreter] $path(echo) $arg 147} $arg 148set arg {} 149 150# I/O redirection: input from Tcl command. 151 152test exec-2.1 {redirecting input from immediate source} {exec stdio} { 153 exec [interpreter] $path(cat) << "Sample text" 154} {Sample text} 155test exec-2.2 {redirecting input from immediate source} {exec stdio} { 156 exec << "Sample text" [interpreter] $path(cat) | [interpreter] $path(cat) 157} {Sample text} 158test exec-2.3 {redirecting input from immediate source} {exec stdio} { 159 exec [interpreter] $path(cat) << "Sample text" | [interpreter] $path(cat) 160} {Sample text} 161test exec-2.4 {redirecting input from immediate source} {exec stdio} { 162 exec [interpreter] $path(cat) | [interpreter] $path(cat) << "Sample text" 163} {Sample text} 164test exec-2.5 {redirecting input from immediate source} {exec} { 165 exec [interpreter] $path(cat) "<<Joined to arrows" 166} {Joined to arrows} 167test exec-2.6 {redirecting input from immediate source, with UTF} -setup { 168 set sysenc [encoding system] 169 encoding system iso8859-1 170 proc quotenonascii s { 171 regsub -all {\[|\\|\]} $s {\\&} s 172 regsub -all "\[\x7F-\xFF\]" $s \ 173 {[apply {c {format {\x%02X} [scan $c %c]}} &]} s 174 return [subst -novariables $s] 175 } 176} -constraints {exec} -body { 177 # If this fails, it may give back: "\xC3\xA9\xC3\xA0\xC3\xBC\xC3\xB1" 178 # If it does, this means that the UTF -> external conversion did not occur 179 # before writing out the temp file. 180 quotenonascii [exec [interpreter] $path(cat) << "\xE9\xE0\xFC\xF1"] 181} -cleanup { 182 encoding system $sysenc 183 rename quotenonascii {} 184} -result {\xE9\xE0\xFC\xF1} 185 186# I/O redirection: output to file. 187 188set path(gorp.file) [makeFile {} gorp.file] 189file delete $path(gorp.file) 190 191test exec-3.1 {redirecting output to file} {exec} { 192 exec [interpreter] $path(echo) "Some simple words" > $path(gorp.file) 193 exec [interpreter] $path(cat) $path(gorp.file) 194} "Some simple words" 195test exec-3.2 {redirecting output to file} {exec stdio} { 196 exec [interpreter] $path(echo) "More simple words" | >$path(gorp.file) [interpreter] $path(cat) | [interpreter] $path(cat) 197 exec [interpreter] $path(cat) $path(gorp.file) 198} "More simple words" 199test exec-3.3 {redirecting output to file} {exec stdio} { 200 exec > $path(gorp.file) [interpreter] $path(echo) "Different simple words" | [interpreter] $path(cat) | [interpreter] $path(cat) 201 exec [interpreter] $path(cat) $path(gorp.file) 202} "Different simple words" 203test exec-3.4 {redirecting output to file} {exec} { 204 exec [interpreter] $path(echo) "Some simple words" >$path(gorp.file) 205 exec [interpreter] $path(cat) $path(gorp.file) 206} "Some simple words" 207test exec-3.5 {redirecting output to file} {exec} { 208 exec [interpreter] $path(echo) "First line" >$path(gorp.file) 209 exec [interpreter] $path(echo) "Second line" >> $path(gorp.file) 210 exec [interpreter] $path(cat) $path(gorp.file) 211} "First line\nSecond line" 212test exec-3.6 {redirecting output to file} {exec} { 213 exec [interpreter] $path(echo) "First line" >$path(gorp.file) 214 exec [interpreter] $path(echo) "Second line" >>$path(gorp.file) 215 exec [interpreter] $path(cat) $path(gorp.file) 216} "First line\nSecond line" 217test exec-3.7 {redirecting output to file} {exec} { 218 set f [open $path(gorp.file) w] 219 puts $f "Line 1" 220 flush $f 221 exec [interpreter] $path(echo) "More text" >@ $f 222 exec [interpreter] $path(echo) >@$f "Even more" 223 puts $f "Line 3" 224 close $f 225 exec [interpreter] $path(cat) $path(gorp.file) 226} "Line 1\nMore text\nEven more\nLine 3" 227 228# I/O redirection: output and stderr to file. 229 230file delete $path(gorp.file) 231 232test exec-4.1 {redirecting output and stderr to file} {exec} { 233 exec [interpreter] $path(echo) "test output" >& $path(gorp.file) 234 exec [interpreter] $path(cat) $path(gorp.file) 235} "test output" 236test exec-4.2 {redirecting output and stderr to file} {exec} { 237 list [exec [interpreter] $path(sh) -c "\"$path(echo)\" foo bar 1>&2" >&$path(gorp.file)] \ 238 [exec [interpreter] $path(cat) $path(gorp.file)] 239} {{} {foo bar}} 240test exec-4.3 {redirecting output and stderr to file} {exec} { 241 exec [interpreter] $path(echo) "first line" > $path(gorp.file) 242 list [exec [interpreter] $path(sh) -c "\"$path(echo)\" foo bar 1>&2" >>&$path(gorp.file)] \ 243 [exec [interpreter] $path(cat) $path(gorp.file)] 244} "{} {first line\nfoo bar}" 245test exec-4.4 {redirecting output and stderr to file} {exec} { 246 set f [open $path(gorp.file) w] 247 puts $f "Line 1" 248 flush $f 249 exec [interpreter] $path(echo) "More text" >&@ $f 250 exec [interpreter] $path(echo) >&@$f "Even more" 251 puts $f "Line 3" 252 close $f 253 exec [interpreter] $path(cat) $path(gorp.file) 254} "Line 1\nMore text\nEven more\nLine 3" 255test exec-4.5 {redirecting output and stderr to file} {exec} { 256 set f [open $path(gorp.file) w] 257 puts $f "Line 1" 258 flush $f 259 exec >&@ $f [interpreter] $path(sh) -c "\"$path(echo)\" foo bar 1>&2" 260 exec >&@$f [interpreter] $path(sh) -c "\"$path(echo)\" xyzzy 1>&2" 261 puts $f "Line 3" 262 close $f 263 exec [interpreter] $path(cat) $path(gorp.file) 264} "Line 1\nfoo bar\nxyzzy\nLine 3" 265 266# I/O redirection: input from file. 267 268if {[testConstraint exec]} { 269 exec [interpreter] $path(echo) "Just a few thoughts" > $path(gorp.file) 270} 271test exec-5.1 {redirecting input from file} {exec} { 272 exec [interpreter] $path(cat) < $path(gorp.file) 273} {Just a few thoughts} 274test exec-5.2 {redirecting input from file} {exec stdio} { 275 exec [interpreter] $path(cat) | [interpreter] $path(cat) < $path(gorp.file) 276} {Just a few thoughts} 277test exec-5.3 {redirecting input from file} {exec stdio} { 278 exec [interpreter] $path(cat) < $path(gorp.file) | [interpreter] $path(cat) 279} {Just a few thoughts} 280test exec-5.4 {redirecting input from file} {exec stdio} { 281 exec < $path(gorp.file) [interpreter] $path(cat) | [interpreter] $path(cat) 282} {Just a few thoughts} 283test exec-5.5 {redirecting input from file} {exec} { 284 exec [interpreter] $path(cat) <$path(gorp.file) 285} {Just a few thoughts} 286test exec-5.6 {redirecting input from file} -constraints {exec} -body { 287 set f [open $path(gorp.file) r] 288 exec [interpreter] $path(cat) <@ $f 289} -cleanup { 290 close $f 291} -result {Just a few thoughts} 292test exec-5.7 {redirecting input from file} -constraints {exec} -body { 293 set f [open $path(gorp.file) r] 294 exec <@$f [interpreter] $path(cat) 295} -cleanup { 296 close $f 297} -result {Just a few thoughts} 298 299# I/O redirection: standard error through a pipeline. 300 301test exec-6.1 {redirecting stderr through a pipeline} {exec stdio} { 302 exec [interpreter] $path(sh) -c "\"$path(echo)\" foo bar" |& [interpreter] $path(cat) 303} "foo bar" 304test exec-6.2 {redirecting stderr through a pipeline} {exec stdio} { 305 exec [interpreter] $path(sh) -c "\"$path(echo)\" foo bar 1>&2" |& [interpreter] $path(cat) 306} "foo bar" 307test exec-6.3 {redirecting stderr through a pipeline} {exec stdio} { 308 exec [interpreter] $path(sh) -c "\"$path(echo)\" foo bar 1>&2" \ 309 |& [interpreter] $path(sh) -c "\"$path(echo)\" second msg 1>&2 ; \"$path(cat)\"" |& [interpreter] $path(cat) 310} "second msg\nfoo bar" 311 312# I/O redirection: combinations. 313 314set path(gorp.file2) [makeFile {} gorp.file2] 315 316test exec-7.1 {multiple I/O redirections} {exec} { 317 exec << "command input" > $path(gorp.file2) [interpreter] $path(cat) < $path(gorp.file) 318 exec [interpreter] $path(cat) $path(gorp.file2) 319} {Just a few thoughts} 320test exec-7.2 {multiple I/O redirections} {exec} { 321 exec < $path(gorp.file) << "command input" [interpreter] $path(cat) 322} {command input} 323 324# Long input to command and output from command. 325set a "0123456789 xxxxxxxxx abcdefghi ABCDEFGHIJK\n" 326set a [concat $a $a $a $a] 327set a [concat $a $a $a $a] 328set a [concat $a $a $a $a] 329set a [concat $a $a $a $a] 330test exec-8.1 {long input and output} {exec} { 331 exec [interpreter] $path(cat) << $a 332} $a 333# More than 20 arguments to exec. 334test exec-8.2 {long input and output} {exec} { 335 exec [interpreter] $path(echo) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 336} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23} 337 338# Commands that return errors. 339 340test exec-9.1 {commands returning errors} {exec notValgrind} { 341 set x [catch {exec gorp456} msg] 342 list $x [string tolower $msg] [string tolower $errorCode] 343} {1 {couldn't execute "gorp456": no such file or directory} {posix enoent {no such file or directory}}} 344test exec-9.2 {commands returning errors} {exec notValgrind} { 345 string tolower [list [catch {exec [interpreter] echo foo | foo123} msg] $msg $errorCode] 346} {1 {couldn't execute "foo123": no such file or directory} {posix enoent {no such file or directory}}} 347test exec-9.3 {commands returning errors} -constraints {exec stdio} -body { 348 exec [interpreter] $path(sleep) 1 | [interpreter] $path(exit) 43 | [interpreter] $path(sleep) 1 349} -returnCodes error -result {child process exited abnormally} 350test exec-9.4 {commands returning errors} -constraints {exec stdio} -body { 351 exec [interpreter] $path(exit) 43 | [interpreter] $path(echo) "foo bar" 352} -returnCodes error -result {foo bar 353child process exited abnormally} 354test exec-9.5 {commands returning errors} -constraints {exec stdio notValgrind} -body { 355 exec gorp456 | [interpreter] echo a b c 356} -returnCodes error -result {couldn't execute "gorp456": no such file or directory} 357test exec-9.6 {commands returning errors} -constraints {exec} -body { 358 exec [interpreter] $path(sh) -c "\"$path(echo)\" error msg 1>&2" 359} -returnCodes error -result {error msg} 360test exec-9.7 {commands returning errors} -constraints {exec stdio nonPortable} -body { 361 # This test can fail easily on multiprocessor machines 362 exec [interpreter] $path(sh) -c "\"$path(echo)\" error msg 1>&2 ; \"$path(sleep)\" 1" \ 363 | [interpreter] $path(sh) -c "\"$path(echo)\" error msg 1>&2 ; \"$path(sleep)\" 1" 364} -returnCodes error -result {error msg 365error msg} 366set path(err) [makeFile {} err] 367test exec-9.8 {commands returning errors} -constraints {exec} -setup { 368 set f [open $path(err) w] 369 puts $f { 370 puts stdout out 371 puts stderr err 372 } 373 close $f 374} -body { 375 exec [interpreter] $path(err) 376} -returnCodes error -result {out 377err} 378 379# Errors in executing the Tcl command, as opposed to errors in the processes 380# that are invoked. 381 382test exec-10.1 {errors in exec invocation} -constraints {exec} -body { 383 exec 384} -returnCodes error -result {wrong # args: should be "exec ?-option ...? arg ?arg ...?"} 385test exec-10.2 {errors in exec invocation} -constraints {exec} -body { 386 exec | cat 387} -returnCodes error -result {illegal use of | or |& in command} 388test exec-10.3 {errors in exec invocation} -constraints {exec} -body { 389 exec cat | 390} -returnCodes error -result {illegal use of | or |& in command} 391test exec-10.4 {errors in exec invocation} -constraints {exec} -body { 392 exec cat | | cat 393} -returnCodes error -result {illegal use of | or |& in command} 394test exec-10.5 {errors in exec invocation} -constraints {exec} -body { 395 exec cat | |& cat 396} -returnCodes error -result {illegal use of | or |& in command} 397test exec-10.6 {errors in exec invocation} -constraints {exec} -body { 398 exec cat |& 399} -returnCodes error -result {illegal use of | or |& in command} 400test exec-10.7 {errors in exec invocation} -constraints {exec} -body { 401 exec cat < 402} -returnCodes error -result {can't specify "<" as last word in command} 403test exec-10.8 {errors in exec invocation} -constraints {exec} -body { 404 exec cat > 405} -returnCodes error -result {can't specify ">" as last word in command} 406test exec-10.9 {errors in exec invocation} -constraints {exec} -body { 407 exec cat << 408} -returnCodes error -result {can't specify "<<" as last word in command} 409test exec-10.10 {errors in exec invocation} -constraints {exec} -body { 410 exec cat >> 411} -returnCodes error -result {can't specify ">>" as last word in command} 412test exec-10.11 {errors in exec invocation} -constraints {exec} -body { 413 exec cat >& 414} -returnCodes error -result {can't specify ">&" as last word in command} 415test exec-10.12 {errors in exec invocation} -constraints {exec} -body { 416 exec cat >>& 417} -returnCodes error -result {can't specify ">>&" as last word in command} 418test exec-10.13 {errors in exec invocation} -constraints {exec} -body { 419 exec cat >@ 420} -returnCodes error -result {can't specify ">@" as last word in command} 421test exec-10.14 {errors in exec invocation} -constraints {exec} -body { 422 exec cat <@ 423} -returnCodes error -result {can't specify "<@" as last word in command} 424test exec-10.15 {errors in exec invocation} -constraints {exec} -body { 425 exec cat < a/b/c 426} -returnCodes error -result {couldn't read file "a/b/c": no such file or directory} 427test exec-10.16 {errors in exec invocation} -constraints {exec} -body { 428 exec cat << foo > a/b/c 429} -returnCodes error -result {couldn't write file "a/b/c": no such file or directory} 430test exec-10.17 {errors in exec invocation} -constraints {exec} -body { 431 exec cat << foo > a/b/c 432} -returnCodes error -result {couldn't write file "a/b/c": no such file or directory} 433set f [open $path(gorp.file) w] 434test exec-10.18 {errors in exec invocation} -constraints {exec} -body { 435 exec cat <@ $f 436} -returnCodes error -result "channel \"$f\" wasn't opened for reading" 437close $f 438set f [open $path(gorp.file) r] 439test exec-10.19 {errors in exec invocation} -constraints {exec} -body { 440 exec cat >@ $f 441} -returnCodes error -result "channel \"$f\" wasn't opened for writing" 442close $f 443test exec-10.20 {errors in exec invocation} -constraints {exec notValgrind} -body { 444 exec ~non_existent_user/foo/bar 445} -returnCodes error -result {user "non_existent_user" doesn't exist} 446test exec-10.21 {errors in exec invocation} -constraints {exec notValgrind} -body { 447 exec [interpreter] true | ~xyzzy_bad_user/x | false 448} -returnCodes error -result {user "xyzzy_bad_user" doesn't exist} 449test exec-10.22 {errors in exec invocation} -constraints {exec notValgrind} -body { 450 exec echo test > ~non_existent_user/foo/bar 451} -returnCodes error -result {user "non_existent_user" doesn't exist} 452# Commands in background. 453 454test exec-11.1 {commands in background} {exec} { 455 set time [time {exec [interpreter] $path(sleep) 2 &}] 456 expr {[lindex $time 0] < 1000000} 457} 1 458test exec-11.2 {commands in background} -constraints {exec} -body { 459 exec [interpreter] $path(echo) a &b 460} -result {a &b} 461test exec-11.3 {commands in background} {exec} { 462 llength [exec [interpreter] $path(sleep) 1 &] 463} 1 464test exec-11.4 {commands in background} {exec stdio} { 465 llength [exec [interpreter] $path(sleep) 1 | [interpreter] $path(sleep) 1 | [interpreter] $path(sleep) 1 &] 466} 3 467test exec-11.5 {commands in background} {exec} { 468 set f [open $path(gorp.file) w] 469 puts $f [list catch [list exec [info nameofexecutable] $path(echo) foo &]] 470 close $f 471 exec [interpreter] $path(gorp.file) 472} foo 473 474# Make sure that background commands are properly reaped when they 475# eventually die. 476 477if {[testConstraint exec] && [testConstraint nonPortable]} { 478 after 1300 479 exec [interpreter] $path(sleep) 1 480} 481test exec-12.1 {reaping background processes} {exec unix nonPortable} { 482 for {set i 0} {$i < 20} {incr i} { 483 exec echo foo > /dev/null & 484 } 485 after 1000 486 catch {exec ps | fgrep "echo foo" | fgrep -v fgrep | wc} msg 487 lindex $msg 0 488} 0 489test exec-12.2 {reaping background processes} {exec unix nonPortable} { 490 exec sleep 2 | sleep 2 | sleep 2 & 491 catch {exec ps | fgrep -i "sleep" | fgrep -i -v fgrep | wc} msg 492 set x [lindex $msg 0] 493 after 3000 494 catch {exec ps | fgrep -i "sleep" | fgrep -i -v fgrep | wc} msg 495 list $x [lindex $msg 0] 496} {3 0} 497test exec-12.3 {reaping background processes} {exec unix nonPortable} { 498 exec sleep 1000 & 499 exec sleep 1000 & 500 set x [exec ps | fgrep "sleep" | fgrep -v fgrep] 501 set pids {} 502 foreach i [split $x \n] { 503 lappend pids [lindex $i 0] 504 } 505 foreach i $pids { 506 catch {exec kill -STOP $i} 507 } 508 catch {exec ps | fgrep "sleep" | fgrep -v fgrep | wc} msg 509 set x [lindex $msg 0] 510 foreach i $pids { 511 catch {exec kill -KILL $i} 512 } 513 catch {exec ps | fgrep "sleep" | fgrep -v fgrep | wc} msg 514 list $x [lindex $msg 0] 515} {2 0} 516 517# Make sure "errorCode" is set correctly. 518 519test exec-13.1 {setting errorCode variable} {exec} { 520 list [catch {exec [interpreter] $path(cat) < a/b/c} msg] [string tolower $errorCode] 521} {1 {posix enoent {no such file or directory}}} 522test exec-13.2 {setting errorCode variable} {exec} { 523 list [catch {exec [interpreter] $path(cat) > a/b/c} msg] [string tolower $errorCode] 524} {1 {posix enoent {no such file or directory}}} 525test exec-13.3 {setting errorCode variable} {exec notValgrind} { 526 set x [catch {exec _weird_cmd_} msg] 527 list $x [string tolower $msg] [lindex $errorCode 0] \ 528 [string tolower [lrange $errorCode 2 end]] 529} {1 {couldn't execute "_weird_cmd_": no such file or directory} POSIX {{no such file or directory}}} 530test exec-13.4 {extended exit result codes} -setup { 531 set tmp [makeFile {exit 0x00000101} tmpfile.exec-13.4] 532} -constraints {win} -body { 533 list [catch {exec [interpreter] $tmp} err] [lreplace $::errorCode 1 1 {}] 534} -cleanup { 535 removeFile $tmp 536} -result {1 {CHILDSTATUS {} 257}} 537test exec-13.5 {extended exit result codes: max value} -setup { 538 set tmp [makeFile {exit 0x3fffffff} tmpfile.exec-13.5] 539} -constraints {win} -body { 540 list [catch {exec [interpreter] $tmp} err] [lreplace $::errorCode 1 1 {}] 541} -cleanup { 542 removeFile $tmp 543} -result {1 {CHILDSTATUS {} 1073741823}} 544test exec-13.6 {extended exit result codes: signalled} -setup { 545 set tmp [makeFile {exit 0xC0000016} tmpfile.exec-13.6] 546} -constraints {win} -body { 547 list [catch {exec [interpreter] $tmp} err] [lreplace $::errorCode 1 1 {}] 548} -cleanup { 549 removeFile $tmp 550} -result {1 {CHILDKILLED {} SIGABRT SIGABRT}} 551 552# Switches before the first argument 553 554test exec-14.1 {-keepnewline switch} {exec} { 555 exec -keepnewline [interpreter] $path(echo) foo 556} "foo\n" 557test exec-14.2 {-keepnewline switch} -constraints {exec} -body { 558 exec -keepnewline 559} -returnCodes error -result {wrong # args: should be "exec ?-option ...? arg ?arg ...?"} 560test exec-14.3 {unknown switch} -constraints {exec} -body { 561 exec -gorp 562} -returnCodes error -result {bad option "-gorp": must be -ignorestderr, -keepnewline, or --} 563test exec-14.4 {-- switch} -constraints {exec notValgrind} -body { 564 exec -- -gorp 565} -returnCodes error -result {couldn't execute "-gorp": no such file or directory} 566test exec-14.5 {-ignorestderr switch} {exec} { 567 # Alas, the use of -ignorestderr is buried here :-( 568 exec [interpreter] $path(sh2) -c [list $path(echo2) foo bar] 2>@1 569} "foo bar\nbar" 570 571# Redirecting standard error separately from standard output 572 573test exec-15.1 {standard error redirection} {exec} { 574 exec [interpreter] $path(echo) "First line" > $path(gorp.file) 575 list [exec [interpreter] $path(sh) -c "\"$path(echo)\" foo bar 1>&2" 2> $path(gorp.file)] \ 576 [exec [interpreter] $path(cat) $path(gorp.file)] 577} {{} {foo bar}} 578test exec-15.2 {standard error redirection} {exec stdio} { 579 list [exec [interpreter] $path(sh) -c "\"$path(echo)\" foo bar 1>&2" \ 580 | [interpreter] $path(echo) biz baz >$path(gorp.file) 2> $path(gorp.file2)] \ 581 [exec [interpreter] $path(cat) $path(gorp.file)] \ 582 [exec [interpreter] $path(cat) $path(gorp.file2)] 583} {{} {biz baz} {foo bar}} 584test exec-15.3 {standard error redirection} {exec stdio} { 585 list [exec [interpreter] $path(sh) -c "\"$path(echo)\" foo bar 1>&2" \ 586 | [interpreter] $path(echo) biz baz 2>$path(gorp.file) > $path(gorp.file2)] \ 587 [exec [interpreter] $path(cat) $path(gorp.file)] \ 588 [exec [interpreter] $path(cat) $path(gorp.file2)] 589} {{} {foo bar} {biz baz}} 590test exec-15.4 {standard error redirection} {exec} { 591 set f [open $path(gorp.file) w] 592 puts $f "Line 1" 593 flush $f 594 exec [interpreter] $path(sh) -c "\"$path(echo)\" foo bar 1>&2" 2>@ $f 595 puts $f "Line 3" 596 close $f 597 readfile $path(gorp.file) 598} {Line 1 599foo bar 600Line 3} 601test exec-15.5 {standard error redirection} {exec} { 602 exec [interpreter] $path(echo) "First line" > "$path(gorp.file)" 603 exec [interpreter] "$path(sh)" -c "\"$path(echo)\" foo bar 1>&2" 2>> "$path(gorp.file)" 604 readfile $path(gorp.file) 605} {First line 606foo bar} 607test exec-15.6 {standard error redirection} {exec stdio} { 608 exec [interpreter] "$path(sh)" -c "\"$path(echo)\" foo bar 1>&2" > "$path(gorp.file2)" 2> "$path(gorp.file)" \ 609 >& "$path(gorp.file)" 2> "$path(gorp.file2)" | [interpreter] $path(echo) biz baz 610 list [readfile $path(gorp.file)] [readfile $path(gorp.file2)] 611} {{biz baz} {foo bar}} 612test exec-15.7 {standard error redirection 2>@1} {exec stdio} { 613 # This redirects stderr output into normal result output from exec 614 exec [interpreter] "$path(sh)" -c "\"$path(echo)\" foo bar 1>&2" 2>@1 615} {foo bar} 616 617test exec-16.1 {flush output before exec} {exec} { 618 set f [open $path(gorp.file) w] 619 puts $f "First line" 620 exec [interpreter] $path(echo) "Second line" >@ $f 621 puts $f "Third line" 622 close $f 623 readfile $path(gorp.file) 624} {First line 625Second line 626Third line} 627test exec-16.2 {flush output before exec} {exec} { 628 set f [open $path(gorp.file) w] 629 puts $f "First line" 630 exec [interpreter] << {puts stderr {Second line}} >&@ $f > $path(gorp.file2) 631 puts $f "Third line" 632 close $f 633 readfile $path(gorp.file) 634} {First line 635Second line 636Third line} 637 638test exec-17.1 {inheriting standard I/O} -constraints {exec} -setup { 639 set path(script) [makeFile {} script] 640 set f [open $path(script) w] 641 puts $f [list lassign [list \ 642 [info nameofexecutable] $path(gorp.file) $path(echo) $path(sleep) \ 643 ] exe file echo sleep] 644 puts $f { 645 close stdout 646 set f [open $file w] 647 catch {exec $exe $echo foobar &} 648 exec $exe $sleep 2 649 close $f 650 } 651 close $f 652} -body { 653 catch {exec [interpreter] $path(script)} result 654 list $result [readfile $path(gorp.file)] 655} -cleanup { 656 removeFile $path(script) 657} -result {{} foobar} 658 659test exec-18.1 {exec deals with weird file names} -body { 660 set path(fooblah) [makeFile {contents} "foo\[\{blah"] 661 exec [interpreter] $path(cat) $path(fooblah) 662} -constraints {exec} -cleanup { 663 removeFile $path(fooblah) 664} -result contents 665test exec-18.2 {exec cat deals with weird file names} -body { 666 # This is cross-platform, but the cat isn't predictably correct on 667 # Windows. 668 set path(fooblah) [makeFile {contents} "foo\[\{blah"] 669 exec cat $path(fooblah) 670} -constraints {exec tempNotWin} -cleanup { 671 removeFile $path(fooblah) 672} -result contents 673 674# Note that this test cannot be adapted to work on Windows; that platform has 675# no kernel support for an analog of O_APPEND. OTOH, that means we can assume 676# that there is a POSIX shell... 677# 678# This test also fails in some cases when building with macOS 679test exec-19.1 {exec >> uses O_APPEND} -constraints {exec unix notValgrind noosxCI} -setup { 680 set tmpfile [makeFile {0} tmpfile.exec-19.1] 681} -body { 682 # Note that we have to allow for the current contents of the temporary 683 # file, which is why the result is 14 and not 12 684 exec /bin/sh -c \ 685 {for a in 1 2 3; do sleep 1; echo $a; done} >>$tmpfile & 686 exec /bin/sh -c \ 687 {for a in 4 5 6; do sleep 1; echo $a >&2; done} 2>>$tmpfile & 688 exec /bin/sh -c \ 689 {for a in a b c; do sleep 1; echo $a; done} >>$tmpfile & 690 exec /bin/sh -c \ 691 {for a in d e f; do sleep 1; echo $a >&2; done} 2>>$tmpfile & 692 # The above four shell invocations take about 3 seconds to finish, so allow 693 # 5s (in case the machine is busy) 694 after 5000 695 # Check that no bytes have got lost through mixups with overlapping 696 # appends, which is only guaranteed to work when we set O_APPEND on the 697 # file descriptor in the [exec >>...] 698 file size $tmpfile 699} -cleanup { 700 removeFile $tmpfile 701} -result 26 702 703# Tests to ensure batch files and .CMD (Bug 9ece99d58b) 704# can be executed on Windows 705test exec-20.0 {exec .bat file} -constraints {win} -body { 706 set log [makeFile {} exec20.log] 707 exec [makeFile "echo %1> $log" exec20.bat] "Testing exec-20.0" 708 viewFile $log 709} -result "\"Testing exec-20.0\"" 710test exec-20.1 {exec .CMD file} -constraints {win} -body { 711 set log [makeFile {} exec201.log] 712 exec [makeFile "echo %1> $log" exec201.CMD] "Testing exec-20.1" 713 viewFile $log 714} -result "\"Testing exec-20.1\"" 715 716# ---------------------------------------------------------------------- 717# cleanup 718 719foreach file {gorp.file gorp.file2 echo echo2 cat wc sh sh2 sleep exit err} { 720 removeFile $file 721} 722unset -nocomplain path 723 724::tcltest::cleanupTests 725return 726 727# Local Variables: 728# mode: tcl 729# End: 730