aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2022-06-11 23:42:52 +1000
committerSteve Bennett <steveb@workware.net.au>2023-08-22 21:03:54 +1000
commit908530a0edd1e2d3cd99d6c9f08c21d2c7e2cc42 (patch)
tree6aca3f921465dc87760867f6285e2f70273e0df7
parentfe7cd9c218db93c7da128a55a4b10c5349a0061d (diff)
downloadjimtcl-908530a0edd1e2d3cd99d6c9f08c21d2c7e2cc42.zip
jimtcl-908530a0edd1e2d3cd99d6c9f08c21d2c7e2cc42.tar.gz
jimtcl-908530a0edd1e2d3cd99d6c9f08c21d2c7e2cc42.tar.bz2
tests: add tests for TIP424 exec syntax
Signed-off-by: Steve Bennett <steveb@workware.net.au>
-rw-r--r--jim-exec.c5
-rw-r--r--tests/exec-tip424.test421
-rw-r--r--tests/exec.test7
3 files changed, 425 insertions, 8 deletions
diff --git a/jim-exec.c b/jim-exec.c
index 07345f5..2b6783a 100644
--- a/jim-exec.c
+++ b/jim-exec.c
@@ -714,7 +714,6 @@ static int JimParsePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
for (i = 0; i < argc; i++) {
unsigned ett;
if (first) {
- printf("first, so adding cmdList=%s\n", Jim_String(argv[i]));
if (Jim_ListLength(interp, argv[i]) == 0) {
Jim_SetResultString(interp, "empty command list", -1);
return JIM_ERR;
@@ -726,13 +725,11 @@ static int JimParsePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
/* Remaining items should be redirections or | */
arg = Jim_String(argv[i]);
ett = JimExecClassifyArg(arg);
- printf("not first, so procesing %s => 0x%04x\n", arg, ett);
if (ett == JIM_ETT_BAD || ett == JIM_ETT_CMD) {
Jim_SetResultFormatted(interp, "invalid redirection %s", arg);
return JIM_ERR;
}
if (ett & JIM_ETT_PIPE) {
- printf("pipe\n");
Jim_ListAppendElement(interp, cmdList, argv[i]);
first = 1;
continue;
@@ -1353,8 +1350,6 @@ JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t
ret = JimParsePipelineLegacy(interp, argc, argv, cmdList, redirectList);
}
if (ret == JIM_OK) {
- printf("cmdList=%s\n", Jim_String(cmdList));
- printf("redirectList=%s\n", Jim_String(redirectList));
/* OK, try to exec */
rc = JimExecPipeline(interp, cmdList, redirectList, pidArrayPtr, outPipePtr, errFilePtr);
}
diff --git a/tests/exec-tip424.test b/tests/exec-tip424.test
new file mode 100644
index 0000000..0f249e2
--- /dev/null
+++ b/tests/exec-tip424.test
@@ -0,0 +1,421 @@
+# The same tests as exec.test, but changed to TIP424 exec syntax
+
+source [file dirname [info script]]/testing.tcl
+
+needs cmd exec
+needs cmd flush
+
+# Need [pipe] to implement [open |command]
+testCmdConstraints pipe
+
+testConstraint unix [expr {$tcl_platform(platform) eq {unix}}]
+
+# Sleep which supports fractions of a second
+if {[info commands sleep] eq {}} {
+ proc sleep {n} {
+ exec {*}$::sleepx $n
+ }
+}
+
+set f [open sleepx w]
+puts $f {
+ sleep "$@"
+}
+close $f
+#catch {exec chmod +x sleepx}
+set sleepx [list sh sleepx]
+
+# Basic operations.
+
+test exec-1.1 {basic exec operation} {
+ exec | {echo a b c}
+} "a b c"
+test exec-1.2 {pipelining} {
+ exec | {echo a b c d} | cat | cat
+} "a b c d"
+test exec-1.3 {pipelining} {
+ set a [exec | {echo a b c d} | cat | wc]
+ list [scan $a "%d %d %d" b c d] $b $c
+} {3 1 4}
+set arg {12345678901234567890123456789012345678901234567890}
+set arg "$arg$arg$arg$arg$arg$arg"
+test exec-1.4 {long command lines} {
+ exec | [list echo $arg]
+} $arg
+set arg {}
+
+# I/O redirection: input from Tcl command.
+
+test exec-2.1 {redirecting input from immediate source} {
+ exec | cat << "Sample text"
+} {Sample text}
+test exec-2.2 {redirecting input from immediate source} {
+ exec | cat << "Sample text" | cat
+} {Sample text}
+test exec-2.4 {redirecting input from immediate source} {
+ exec | cat | cat << "Sample text"
+} {Sample text}
+test exec-2.5 {redirecting input from immediate source} {
+ exec | cat "<<Joined to arrows"
+} {Joined to arrows}
+test exec-2.6 {redirecting input from immediate source, with UTF} {
+ # If this fails, it may give back:
+ # "\uC3\uA9\uC3\uA0\uC3\uBC\uC3\uB1"
+ # If it does, this means that the UTF -> external conversion did not
+ # occur before writing out the temp file.
+ exec | cat << "\uE9\uE0\uFC\uF1"
+} "\uE9\uE0\uFC\uF1"
+test exec-2.7 {redirecting input from immediate source with nulls} {
+ exec | cat << "Sample\0text"
+} "Sample\0text"
+
+# I/O redirection: output to file.
+
+file delete gorp.file
+test exec-3.1 {redirecting output to file} {
+ exec | {echo "Some simple words"} > gorp.file
+ exec | {cat gorp.file}
+} "Some simple words"
+test exec-3.2 {redirecting output to file} {
+ exec | {echo "More simple words"} | cat >gorp.file | cat
+ exec | {cat gorp.file}
+} "More simple words"
+test exec-3.3 {redirecting output to file} {
+ exec | {echo "Different simple words"} > gorp.file | cat | cat
+ exec | {cat gorp.file}
+} "Different simple words"
+test exec-3.4 {redirecting output to file} {
+ exec | {echo "Some simple words"} >gorp.file
+ exec | {cat gorp.file}
+} "Some simple words"
+test exec-3.5 {redirecting output to file} {
+ exec | {echo "First line"} >gorp.file
+ exec | {echo "Second line"} >> gorp.file
+ exec | {cat gorp.file}
+} "First line\nSecond line"
+test exec-3.7 {redirecting output to file} {
+ set f [open gorp.file w]
+ puts $f "Line 1"
+ flush $f
+ exec | {echo "More text"} >@ $f
+ exec | {echo "Even more"} >@$f
+ puts $f "Line 3"
+ close $f
+ exec | {cat gorp.file}
+} "Line 1\nMore text\nEven more\nLine 3"
+
+# I/O redirection: output and stderr to file.
+
+file delete gorp.file
+test exec-4.1 {redirecting output and stderr to file} {
+ exec | {echo "test output"} >& gorp.file
+ exec | {cat gorp.file}
+} "test output"
+test exec-4.2 {redirecting output and stderr to file} {
+ list [exec | {sh -c "echo foo bar 1>&2"} >&gorp.file] \
+ [exec | {cat gorp.file}]
+} {{} {foo bar}}
+test exec-4.3 {redirecting output and stderr to file} {
+ exec | {echo "first line"} > gorp.file
+ list [exec | {sh -c "echo foo bar 1>&2"} >>&gorp.file] \
+ [exec | {cat gorp.file}]
+} "{} {first line\nfoo bar}"
+test exec-4.4 {redirecting output and stderr to file} {
+ set f [open gorp.file w]
+ puts $f "Line 1"
+ flush $f
+ exec | {echo "More text"} >&@ $f
+ exec | {echo "Even more"} >&@$f
+ puts $f "Line 3"
+ close $f
+ exec | {cat gorp.file}
+} "Line 1\nMore text\nEven more\nLine 3"
+test exec-4.5 {redirecting output and stderr to file} {
+ set f [open gorp.file w]
+ puts $f "Line 1"
+ flush $f
+ exec | {sh -c "echo foo bar 1>&2"} >&@ $f
+ exec | {sh -c "echo xyzzy 1>&2"} >&@$f
+ puts $f "Line 3"
+ close $f
+ exec | {cat gorp.file}
+} "Line 1\nfoo bar\nxyzzy\nLine 3"
+
+# I/O redirection: input from file.
+
+exec | {echo "Just a few thoughts"} > gorp.file
+
+test exec-5.1 {redirecting input from file} {
+ exec | cat < gorp.file
+} {Just a few thoughts}
+test exec-5.2 {redirecting input from file} {
+ exec | cat | cat < gorp.file
+} {Just a few thoughts}
+test exec-5.3 {redirecting input from file} {
+ exec | cat < gorp.file | cat
+} {Just a few thoughts}
+test exec-5.5 {redirecting input from file} {
+ exec | cat <gorp.file
+} {Just a few thoughts}
+test exec-5.6 {redirecting input from file} {
+ set f [open gorp.file r]
+ set result [exec | cat <@ $f]
+ close $f
+ set result
+} {Just a few thoughts}
+test exec-5.7 {redirecting input from file} {
+ set f [open gorp.file r]
+ set result [exec | cat <@$f]
+ close $f
+ set result
+} {Just a few thoughts}
+
+# I/O redirection: standard error through a pipeline.
+
+test exec-6.1 {redirecting stderr through a pipeline} {
+ exec | {sh -c "echo foo bar"} |& cat
+} "foo bar"
+test exec-6.2 {redirecting stderr through a pipeline} {
+ exec | {sh -c "echo foo bar 1>&2"} |& cat
+} "foo bar"
+test exec-6.3 {redirecting stderr through a pipeline} {
+ exec | {sh -c "echo foo bar 1>&2"} \
+ |& cat |& cat
+} "foo bar"
+
+# I/O redirection: combinations.
+
+file delete gorp.file2
+test exec-7.1 {multiple I/O redirections} {
+ exec | cat << "command input" > gorp.file2 < gorp.file
+ exec | {cat gorp.file2}
+} {Just a few thoughts}
+test exec-7.2 {multiple I/O redirections} {
+ exec cat < gorp.file << "command input"
+} {command input}
+
+# Long input to command and output from command.
+
+set a [string repeat a 1000000]
+test exec-8.1 {long input and output} {
+ string length [exec | cat << $a]
+} 1000000
+
+# More than 20 arguments to exec.
+
+test exec-8.1 {long input and output} {
+ exec | {echo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23}
+} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23}
+
+# Commands that return errors.
+
+test exec-9.1 {commands returning errors} {
+ catch {exec | gorp456}
+} {1}
+test exec-9.2 {commands returning errors} {
+ catch {exec | {echo foo} | foo123} msg
+} {1}
+test exec-9.3 {commands returning errors} {
+ list [catch {exec | [list {*}$sleepx 0.1] | false | [list {*}$sleepx 0.1]} msg]
+} {1}
+test exec-9.4 {commands returning errors} jim {
+ list [catch {exec | false | {echo "foo bar"}} msg] $msg
+} {1 {foo bar}}
+test exec-9.5 {commands returning errors} {
+ list [catch {exec | gorp456 | {echo a b c}} msg]
+} {1}
+test exec-9.6 {commands returning errors} jim {
+ list [catch {exec | {sh -c "echo error msg 1>&2"}} msg] $msg
+} {0 {error msg}}
+test exec-9.7 {commands returning errors} jim {
+ # Note: Use sleep here to ensure the order
+ list [catch {exec | {sh -c "echo error msg 1 1>&2"} \
+ | {sh -c "sleep 0.1; echo error msg 2 1>&2"}} msg] $msg
+} {0 {error msg 1
+error msg 2}}
+
+# Errors in executing the Tcl command, as opposed to errors in the
+# processes that are invoked.
+
+test exec-10.1 {errors in exec invocation} {
+ list [catch {exec |} msg]
+} {1}
+test exec-10.3 {errors in exec invocation} {
+ list [catch {exec | cat |} msg] $msg
+} {1 {cmdlist required after |}}
+test exec-10.4 {errors in exec invocation} {
+ list [catch {exec | cat | | cat} msg] $msg
+} {1 {invalid redirection cat}}
+test exec-10.5 {errors in exec invocation} {
+ list [catch {exec | cat | |& cat} msg] $msg
+} {1 {invalid redirection cat}}
+test exec-10.6 {errors in exec invocation} {
+ list [catch {exec | cat |&} msg] $msg
+} {1 {cmdlist required after |&}}
+test exec-10.7 {errors in exec invocation} {
+ list [catch {exec | cat <} msg] $msg
+} {1 {can't specify "<" as last word in command}}
+test exec-10.8 {errors in exec invocation} {
+ list [catch {exec | cat >} msg] $msg
+} {1 {can't specify ">" as last word in command}}
+test exec-10.9 {errors in exec invocation} {
+ list [catch {exec | cat <<} msg] $msg
+} {1 {can't specify "<<" as last word in command}}
+test exec-10.10 {errors in exec invocation} {
+ list [catch {exec | cat >>} msg] $msg
+} {1 {can't specify ">>" as last word in command}}
+test exec-10.11 {errors in exec invocation} {
+ list [catch {exec | cat >&} msg] $msg
+} {1 {can't specify ">&" as last word in command}}
+test exec-10.12 {errors in exec invocation} {
+ list [catch {exec | cat >>&} msg] $msg
+} {1 {can't specify ">>&" as last word in command}}
+test exec-10.13 {errors in exec invocation} {
+ list [catch {exec | cat >@} msg] $msg
+} {1 {can't specify ">@" as last word in command}}
+test exec-10.14 {errors in exec invocation} {
+ list [catch {exec | cat <@} msg] $msg
+} {1 {can't specify "<@" as last word in command}}
+test exec-10.15 {errors in exec invocation} {
+ list [catch {exec | cat < a/b/c} msg] [string tolower $msg]
+} {1 {couldn't read file "a/b/c": no such file or directory}}
+test exec-10.16 {errors in exec invocation} {
+ list [catch {exec | cat << foo > a/b/c} msg] [string tolower $msg]
+} {1 {couldn't write file "a/b/c": no such file or directory}}
+test exec-10.17 {errors in exec invocation} {
+ list [catch {exec | cat << foo > a/b/c} msg] [string tolower $msg]
+} {1 {couldn't write file "a/b/c": no such file or directory}}
+set f [open gorp.file w]
+test exec-10.18 {errors in exec invocation} {
+ list [catch {exec | cat <<test <@ $f} msg]
+} 1
+close $f
+set f [open gorp.file r]
+test exec-10.19 {errors in exec invocation} {
+ list [catch {exec | cat <<test >@ $f} msg]
+} 1
+close $f
+
+# Commands in background.
+
+test exec-11.1 {commands in background} {
+ set x [lindex [time {exec | [list {*}$sleepx 0.2] &}] 0]
+ expr $x<1000000
+} 1
+test exec-11.2 {commands in background} {
+ list [catch {exec | {echo a &b}} msg] $msg
+} {0 {a &b}}
+test exec-11.3 {commands in background} {
+ llength [exec | [list {*}$sleepx 0.1] &]
+} 1
+test exec-11.4 {commands in background} {
+ llength [exec | [list {*}$sleepx 0.1] | [list {*}$sleepx 0.1] | [list {*}$sleepx 0.1] &]
+} 3
+
+# Make sure that background commands are properly reaped when
+# they eventually die.
+
+exec | [list {*}$sleepx 0.3]
+
+test exec-12.1 {reaping background processes} -constraints unix -body {
+ for {set i 0} {$i < 20} {incr i} {
+ exec | {echo foo} > exec.tmp1 &
+ }
+ exec | [list {*}$sleepx 0.1]
+ catch {exec | ps | {fgrep "echo foo"} | {fgrep -v grep} | wc} msg
+ lindex $msg 0
+} -cleanup {
+ file delete exec.tmp1
+} -result 0
+
+# Redirecting standard error separately from standard output
+
+test exec-15.1 {standard error redirection} {
+ exec | {echo "First line"} > gorp.file
+ list [exec | {sh -c "echo foo bar 1>&2"} 2> gorp.file] \
+ [exec | {cat gorp.file}]
+} {{} {foo bar}}
+test exec-15.2 {standard error redirection} {
+ list [exec | {sh -c "echo foo bar 1>&2"} \
+ | {echo biz baz} >gorp.file 2> gorp.file2] \
+ [exec | {cat gorp.file}] \
+ [exec | {cat gorp.file2}]
+} {{} {biz baz} {foo bar}}
+test exec-15.3 {standard error redirection} {
+ list [exec | {sh -c "echo foo bar 1>&2"} \
+ | {echo biz baz} 2>gorp.file > gorp.file2] \
+ [exec | {cat gorp.file}] \
+ [exec | {cat gorp.file2}]
+} {{} {foo bar} {biz baz}}
+test exec-15.4 {standard error redirection} {
+ set f [open gorp.file w]
+ puts $f "Line 1"
+ flush $f
+ exec | {sh -c "echo foo bar 1>&2"} 2>@ $f
+ puts $f "Line 3"
+ close $f
+ exec | {cat gorp.file}
+} {Line 1
+foo bar
+Line 3}
+test exec-15.5 {standard error redirection} {
+ exec | {echo "First line"} > gorp.file
+ exec | {sh -c "echo foo bar 1>&2"} 2>> gorp.file
+ exec | {cat gorp.file}
+} {First line
+foo bar}
+test exec-15.6 {standard error redirection} {
+ exec | {sh -c "echo foo bar 1>&2"} > gorp.file2 2> gorp.file \
+ >& gorp.file 2> gorp.file2 | {echo biz baz}
+ list [exec | {cat gorp.file}] [exec | {cat gorp.file2}]
+} {{biz baz} {foo bar}}
+test exec-15.7 {combine standard output/standard error} -body {
+ exec | {sh -c "echo foo bar 1>&2"} > gorp.file 2>@1
+ exec | {cat gorp.file}
+} -cleanup {
+ file delete gorp.file gorp.file2
+} -result {foo bar}
+
+test exec-16.1 {flush output before exec} -body {
+ set f [open gorp.file w]
+ puts $f "First line"
+ exec | {echo "Second line"} >@ $f
+ puts $f "Third line"
+ close $f
+ exec | {cat gorp.file}
+} -cleanup {
+ file delete gorp.file
+} -result {First line
+Second line
+Third line}
+
+test exec-17.1 {redirecting from command pipeline} -setup {
+ makeFile "abc\nghi\njkl" gorp.file
+} -constraints pipe -body {
+ set f [open "|| {cat gorp.file} | {wc -l}" r]
+ set result [lindex [exec | cat <@$f] 0]
+ close $f
+ set result
+} -cleanup {
+ file delete gorp.file
+} -result {3}
+
+test exec-17.2 {redirecting to command pipeline} -setup {
+ makeFile "abc\nghi\njkl" gorp.file
+} -constraints pipe -body {
+ set f [open "|| {wc -l} >gorp2.file" w]
+ exec | {cat gorp.file} >@$f
+ flush $f
+ close $f
+ lindex [exec | {cat gorp2.file}] 0
+} -cleanup {
+ file delete gorp.file gorp2.file
+} -result {3}
+
+file delete sleepx
+
+# Now we probably have a lot of unreaped zombies at this point
+# so reap them to avoid confusing further tests
+wait
+
+testreport
diff --git a/tests/exec.test b/tests/exec.test
index 85014a7..e957ec3 100644
--- a/tests/exec.test
+++ b/tests/exec.test
@@ -268,9 +268,10 @@ error msg 2}}
test exec-10.1 {errors in exec invocation} {
list [catch {exec} msg]
} {1}
-test exec-10.2 {errors in exec invocation} {
- list [catch {exec | cat} msg] $msg
-} {1 {illegal use of | or |& in command}}
+# This is no longer an error - it is TIP424 syntax
+#test exec-10.2 {errors in exec invocation} {
+# list [catch {exec | cat} msg] $msg
+#} {1 {illegal use of | or |& in command}}
test exec-10.3 {errors in exec invocation} {
list [catch {exec cat |} msg] $msg
} {1 {illegal use of | or |& in command}}