elvish/pkg/eval/compile_effect_test.elvts
Qi Xiao d7fe04414b pkg/eval/evaltest: Import all builtin modules.
This removes the need for various "use-foo" setups for the standard library
modules - test code can just call "use foo" like normal Elvish code.
2024-02-01 14:46:37 +00:00

466 lines
12 KiB
Plaintext

/////////
# chunk #
/////////
## empty chunk ##
~>
## outputs of pipelines in a chunk are concatenated ##
~> put x; put y; put z
▶ x
▶ y
▶ z
## a failed pipeline cause the whole chunk to fail ##
~> put a; fail bad; put b
▶ a
Exception: bad
[tty]:1:8-15: put a; fail bad; put b
////////////
# pipeline #
////////////
## pure byte pipeline on Unix ##
//only-on unix
~> echo "Albert\nAllan\nAlbraham\nBerlin" | sed s/l/1/g | grep e
A1bert
Ber1in
## pure byte pipeline on Windows ##
//only-on windows
~> echo "Albert\nAllan\nAlbraham\nBerlin" | findstr e
Albert
Berlin
## pure value pipeline ##
~> put 233 42 19 | each {|x|+ $x 10}
▶ (num 243)
▶ (num 52)
▶ (num 29)
## pipeline draining ##
~> range 100 | put x
▶ x
// TODO: Add a useful hybrid pipeline sample
## reader gone ##
// Internal commands writing to byte output raises ReaderGone when the reader has
// exited, which is then suppressed by the pipeline.
~> while $true { echo y } | nop
~> var reached = $false
{ while $true { echo y }; reached = $true } | nop
put $reached
▶ $false
// Similar for value output.
~> while $true { put y } | nop
~> var reached = $false
{ while $true { put y }; reached = $true } | nop
put $reached
▶ $false
## reader gone from SIGPIPE ##
//only-on unix
// External commands terminated by SIGPIPE due to reader exiting early raise
// ReaderGone, which is then suppressed by the pipeline.
~> yes | true
~> var reached = $false
{ yes; reached = $true } | true
put $reached
▶ $false
///////////////////////
# background pipeline #
///////////////////////
//eval use file
## basic behavior ##
~> set notify-bg-job-success = $false
var p = (file:pipe)
{ print foo > $p; file:close $p[w] }&
slurp < $p; file:close $p[r]
▶ foo
## notification ##
//recv-bg-job-notification-in-global
~> set notify-bg-job-success = $true
var p = (file:pipe)
fn f { file:close $p[w] }
f &
slurp < $p; file:close $p[r]
recv-bg-job-notification
▶ ''
▶ 'job f & finished'
## notification with exception ##
//recv-bg-job-notification-in-global
~> set notify-bg-job-success = $true
var p = (file:pipe)
fn f { file:close $p[w]; fail foo }
f &
slurp < $p; file:close $p[r]
recv-bg-job-notification
▶ ''
▶ 'job f & finished, errors = foo'
///////////
# command #
///////////
~> put foo
▶ foo
## error conditions ##
// head is not a single value
~> {put put} foo
Exception: arity mismatch: command must be 1 value, but is 2 values
[tty]:1:1-9: {put put} foo
// head is not callable or string containing slash
~> [] foo
Exception: bad value: command must be callable or string containing slash, but is []
[tty]:1:1-2: [] foo
// argument throws
~> put [][1]
Exception: out of range: index must be from 0 to -1, but is 1
[tty]:1:5-9: put [][1]
// option key is not string
~> put &[]=[]
Exception: bad value: option key must be string, but is list
[tty]:1:1-10: put &[]=[]
// option evaluation throws
~> put &x=[][1]
Exception: out of range: index must be from 0 to -1, but is 1
[tty]:1:8-12: put &x=[][1]
## regression test for b.elv.sh/1204 ##
// Ensure that the arguments of special forms are not accidentally compiled
// twice.
~> nop (and (use builtin))
nop $builtin:echo~
/////////////////////////////
# external command as value #
/////////////////////////////
~> kind-of (external true)
▶ fn
~> repr (external true)
<external true>
~> put [&(external true)=$true][(external true)]
▶ $true
////////////////////////////////
# external commands invocation #
////////////////////////////////
## common behavior ##
// Doesn't support options
~> (external foo) &option
Exception: external commands don't accept elvish options
[tty]:1:1-22: (external foo) &option
## external command from PATH ##
//in-temp-dir
//unset-env PATH
~> use os
os:mkdir bin
to-lines ['#!/bin/sh' 'echo hello'] > bin/say-hello
os:chmod 0o700 bin/say-hello
to-lines ['@echo hello'] > bin/say-hello.bat
set paths = [$pwd/bin]
~> say-hello
hello
// Explicit e:
~> e:say-hello
hello
// Dynamic string does not work for finding external commands from PATH
~> var x = say-hello
$x
Exception: bad value: command must be callable or string containing slash, but is say-hello
[tty]:2:1-2: $x
// Command searching is affected by the unknown-command pragma
~> { pragma unknown-command = disallow; say-hello }
Compilation error: unknown command disallowed by current pragma
[tty]:1:38-46: { pragma unknown-command = disallow; say-hello }
~> { pragma unknown-command = disallow; { say-hello } }
Compilation error: unknown command disallowed by current pragma
[tty]:1:40-48: { pragma unknown-command = disallow; { say-hello } }
~> { pragma unknown-command = external; say-hello }
hello
// But explicit e: is always allowed
~> { pragma unknown-command = disallow; e:say-hello }
hello
## external command relative to working directory ##
//in-temp-dir
~> use os
fn make-echo-script {|name msg|
to-lines ['#!/bin/sh' 'echo '$msg] > $name
os:chmod 0o700 $name
to-lines ['@echo '$msg] > $name.bat
}
make-echo-script say-hello hello
os:mkdir lorem
make-echo-script lorem/ipsum 'lorem ipsum'
~> ./say-hello
hello
~> ./lorem/ipsum
lorem ipsum
~> lorem/ipsum
lorem ipsum
// Explicit e:
~> e:./say-hello
hello
~> e:./lorem/ipsum
lorem ipsum
~> e:lorem/ipsum
lorem ipsum
// Dynamic string
~> var x = ./say-hello
$x
hello
~> var x = ./lorem/ipsum
$x
lorem ipsum
~> var x = lorem/ipsum
$x
lorem ipsum
// Relative external commands are not affected by the unknown-command pragma
~> { pragma unknown-command = disallow; ./say-hello }
hello
~> { pragma unknown-command = disallow; lorem/ipsum }
lorem ipsum
~> { pragma unknown-command = disallow; var x = ./say-hello; $x }
hello
## non-existent command on Unix ##
//only-on unix
//unset-env PATH
~> set paths = []
~> nonexistent-command
Exception: exec: "nonexistent-command": executable file not found in $PATH
[tty]:1:1-19: nonexistent-command
## non-existent command on Windows ##
//only-on windows
//unset-env PATH
~> set paths = []
~> nonexistent-command
Exception: exec: "nonexistent-command": executable file not found in %PATH%
[tty]:1:1-19: nonexistent-command
//////////////////////////////////////
# legacy temporary assignment syntax #
//////////////////////////////////////
~> var a b = alice bob; {a,@b}=(put amy ben) put $a $@b; put $a $b
▶ amy
▶ ben
▶ alice
▶ bob
Deprecation: the legacy temporary assignment syntax is deprecated; use "tmp" instead
[tty]:1:22-41: var a b = alice bob; {a,@b}=(put amy ben) put $a $@b; put $a $b
## temporary assignment of list element ##
~> var l = [a]; l[0]=x put $l[0]; put $l[0]
▶ x
▶ a
Deprecation: the legacy temporary assignment syntax is deprecated; use "tmp" instead
[tty]:1:14-19: var l = [a]; l[0]=x put $l[0]; put $l[0]
## temporary assignment of map element ##
~> var m = [&k=v]; m[k]=v2 put $m[k]; put $m[k]
▶ v2
▶ v
Deprecation: the legacy temporary assignment syntax is deprecated; use "tmp" instead
[tty]:1:17-23: var m = [&k=v]; m[k]=v2 put $m[k]; put $m[k]
## temporary assignment before special form ##
~> li=[foo bar] for x $li { put $x }
▶ foo
▶ bar
Deprecation: the legacy temporary assignment syntax is deprecated; use "tmp" instead
[tty]:1:1-12: li=[foo bar] for x $li { put $x }
## multiple LHSs in temporary assignments. ##
~> {a b}={foo bar} put $a $b
▶ foo
▶ bar
Deprecation: the legacy temporary assignment syntax is deprecated; use "tmp" instead
[tty]:1:1-15: {a b}={foo bar} put $a $b
## rest variable ##
~> @a=(put a b) put $@a
▶ a
▶ b
Deprecation: the legacy temporary assignment syntax is deprecated; use "tmp" instead
[tty]:1:1-12: @a=(put a b) put $@a
## non-rest and rest variable ##
~> {a,@b}=(put a b c) put $@b
▶ b
▶ c
Deprecation: the legacy temporary assignment syntax is deprecated; use "tmp" instead
[tty]:1:1-18: {a,@b}=(put a b c) put $@b
## using syntax of temporary assignment for non-temporary assignment no longer compiles ##
~> x=y
Compilation error: using the syntax of temporary assignment for non-temporary assignment is no longer supported; use "var" or "set" instead
[tty]:1:1-3: x=y
///////////////
# redirection #
///////////////
//in-temp-dir
## output and input redirection ##
~> echo 233 > out1
slurp < out1
▶ "233\n"
## append ##
~> echo 1 > out; echo 2 >> out; slurp < out
▶ "1\n2\n"
## read and write ##
// TODO: Add a meaningful use case that actually uses both read and write.
~> echo 233 <> out1
slurp < out1
▶ "233\n"
## redirections from special form ##
~> for x [lorem ipsum] { echo $x } > out2
slurp < out2
▶ "lorem\nipsum\n"
## using numeric FDs as source and destination ##
~> { echo foobar >&2 } 2> out3
slurp < out3
▶ "foobar\n"
## using named FDs as source and destination ##
~> echo 233 stdout> out1
slurp stdin< out1
▶ "233\n"
## using named FDs (stderr) as source and destination ##
~> { echo foobar >&stderr } stderr> out4
slurp < out4
▶ "foobar\n"
## using a new FD as source throws an exception ##
~> echo foo >&4
Exception: invalid fd: 4
[tty]:1:10-12: echo foo >&4
## using a new FD as destination is OK, and makes it available ##
~> { echo foo >&4 } 4>out5
slurp < out5
▶ "foo\n"
## using a new FD for external command is OK ##
// Regression test against b.elv.sh/788.
//only-on unix
// TODO: Enable for Windows too.
~> /bin/sh -c 'echo ok' 5</dev/null
ok
## redirections from file objects ##
~> use file
echo haha > out3
var f = (file:open out3)
slurp <$f
file:close $f
▶ "haha\n"
## redirections from pipe objects ##
~> use file
var p = (file:pipe)
echo haha > $p
file:close $p[w]
slurp < $p
file:close $p[r]
▶ "haha\n"
## regression test for b.elv.sh/1010 ##
// Don't hang when iterating over input from a file.
~> echo abc > bytes
each $echo~ < bytes
abc
~> echo def > bytes
only-values < bytes | count
▶ (num 0)
## writing value output to file throws an exception ##
~> put foo >a
Exception: port does not support value output
[tty]:1:1-10: put foo >a
## writing value output to closed port throws an exception ##
~> put foo >&-
Exception: port does not support value output
[tty]:1:1-11: put foo >&-
## invalid redirection destination ##
~> echo []> test
Exception: bad value: redirection destination must be fd name or number, but is []
[tty]:1:6-7: echo []> test
## invalid fd redirection source ##
~> echo >&test
Exception: bad value: redirection source must be fd name or number or '-', but is test
[tty]:1:8-11: echo >&test
## invalid redirection source ##
~> echo > []
Exception: bad value: redirection source must be string, file or map, but is list
[tty]:1:8-9: echo > []
## invalid map for redirection ##
~> echo < [&]
Exception: bad value: map for input redirection must be map with file in the 'r' field, but is [&]
[tty]:1:8-10: echo < [&]
~> echo > [&]
Exception: bad value: map for output redirection must be map with file in the 'w' field, but is [&]
[tty]:1:8-10: echo > [&]
## exception when evaluating source or destination ##
~> echo > (fail foo)
Exception: foo
[tty]:1:9-16: echo > (fail foo)
~> echo (fail foo)> file
Exception: foo
[tty]:1:7-14: echo (fail foo)> file
////////////////
# stack traces #
////////////////
// Stack traces of increasing depths.
~> fail oops
Exception: oops
[tty]:1:1-9: fail oops
~> fn f { fail oops }
f
Exception: oops
[tty]:1:8-17: fn f { fail oops }
[tty]:2:1-1: f
~> fn f { fail oops }
fn g { f }
g
Exception: oops
[tty]:1:8-17: fn f { fail oops }
[tty]:2:8-9: fn g { f }
[tty]:3:1-1: g
// Error thrown before execution.
~> fn f { }
f a
Exception: arity mismatch: arguments must be 0 values, but is 1 value
[tty]:2:1-3: f a
// Error from builtin.
~> count 1 2 3
Exception: arity mismatch: arguments must be 0 to 1 values, but is 3 values
[tty]:1:1-11: count 1 2 3