From 908530a0edd1e2d3cd99d6c9f08c21d2c7e2cc42 Mon Sep 17 00:00:00 2001 From: Steve Bennett Date: Sat, 11 Jun 2022 23:42:52 +1000 Subject: tests: add tests for TIP424 exec syntax Signed-off-by: Steve Bennett --- jim-exec.c | 5 - tests/exec-tip424.test | 421 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/exec.test | 7 +- 3 files changed, 425 insertions(+), 8 deletions(-) create mode 100644 tests/exec-tip424.test 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 "< 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 &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 <@ $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}} -- cgit v1.1