mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-13 01:47:51 +08:00
f681a73a1d
Qualified imports of pkg/testutil outnumber unqualified (55 to 7). Improve consistency, and clarity, by changing the dot (unqualified) imports of that package symbols to qualified.
223 lines
7.2 KiB
Go
223 lines
7.2 KiB
Go
package eval_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
. "src.elv.sh/pkg/eval"
|
|
"src.elv.sh/pkg/eval/errs"
|
|
"src.elv.sh/pkg/testutil"
|
|
|
|
. "src.elv.sh/pkg/eval/evaltest"
|
|
"src.elv.sh/pkg/eval/vals"
|
|
)
|
|
|
|
func TestCompound(t *testing.T) {
|
|
Test(t,
|
|
That("put {fi,elvi}sh{1.0,1.1}").Puts(
|
|
"fish1.0", "fish1.1", "elvish1.0", "elvish1.1"),
|
|
|
|
// As a special case, an empty compound expression evaluates to an empty
|
|
// string.
|
|
That("put {}").Puts(""),
|
|
That("put [&k=][k]").Puts(""),
|
|
|
|
// TODO: Test the case where fsutil.GetHome returns an error.
|
|
|
|
// Error in any of the components throws an exception.
|
|
That("put a{[][1]}").Throws(ErrorWithType(errs.OutOfRange{}), "[][1]"),
|
|
// Error in concatenating the values throws an exception.
|
|
That("put []a").Throws(ErrorWithMessage("cannot concatenate list and string")),
|
|
// Error when applying tilde throws an exception.
|
|
That("put ~[]").Throws(ErrorWithMessage("tilde doesn't work on value of type list")),
|
|
)
|
|
}
|
|
|
|
func TestIndexing(t *testing.T) {
|
|
Test(t,
|
|
That("put [a b c][2]").Puts("c"),
|
|
That("put [][0]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"),
|
|
That("put [&key=value][key]").Puts("value"),
|
|
That("put [&key=value][bad]").Throws(
|
|
vals.NoSuchKey("bad"), "[&key=value][bad]"),
|
|
|
|
That("put (fail x)[a]").Throws(FailError{"x"}, "fail x"),
|
|
That("put [foo][(fail x)]").Throws(FailError{"x"}, "fail x"),
|
|
)
|
|
}
|
|
|
|
func TestListLiteral(t *testing.T) {
|
|
Test(t,
|
|
That("put [a b c]").Puts(vals.MakeList("a", "b", "c")),
|
|
That("put []").Puts(vals.EmptyList),
|
|
// List expression errors if an element expression errors.
|
|
That("put [ [][0] ]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"),
|
|
)
|
|
}
|
|
|
|
func TestMapLiteral(t *testing.T) {
|
|
Test(t,
|
|
That("put [&key=value]").Puts(vals.MakeMap("key", "value")),
|
|
That("put [&]").Puts(vals.EmptyMap),
|
|
// Map keys and values may evaluate to multiple values as long as their
|
|
// numbers match.
|
|
That("put [&{a b}={foo bar}]").Puts(vals.MakeMap("a", "foo", "b", "bar")),
|
|
// Map expression errors if a key or value expression errors.
|
|
That("put [ &[][0]=a ]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"),
|
|
That("put [ &a=[][0] ]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"),
|
|
// Map expression errors if number of keys and values in a single pair
|
|
// does not match.
|
|
That("put [&{a b}={foo bar lorem}]").Throws(ErrorWithMessage("2 keys but 3 values")),
|
|
)
|
|
}
|
|
|
|
func TestStringLiteral(t *testing.T) {
|
|
Test(t,
|
|
That(`put 'such \"''literal'`).Puts(`such \"'literal`),
|
|
That(`put "much \n\033[31;1m$cool\033[m"`).
|
|
Puts("much \n\033[31;1m$cool\033[m"),
|
|
)
|
|
}
|
|
|
|
func TestTilde(t *testing.T) {
|
|
home := testutil.InTempHome(t)
|
|
testutil.ApplyDir(testutil.Dir{"file1": "", "file2": ""})
|
|
|
|
Test(t,
|
|
// Tilde
|
|
// -----
|
|
That("put ~").Puts(home),
|
|
That("put ~/src").Puts(home+"/src"),
|
|
// Make sure that tilde processing retains trailing slashes.
|
|
That("put ~/src/").Puts(home+"/src/"),
|
|
// Tilde and wildcard.
|
|
That("put ~/*").Puts(home+"/file1", home+"/file2"),
|
|
// TODO: Add regression test for #793.
|
|
// TODO: Add regression test for #1246.
|
|
)
|
|
}
|
|
|
|
func TestWildcard(t *testing.T) {
|
|
Test(t,
|
|
That("put ***").DoesNotCompile(),
|
|
)
|
|
// More tests in glob_test.go
|
|
}
|
|
|
|
func TestOutputCapture(t *testing.T) {
|
|
Test(t,
|
|
// Output capture
|
|
That("put (put lorem ipsum)").Puts("lorem", "ipsum"),
|
|
That("put (print \"lorem\nipsum\")").Puts("lorem", "ipsum"),
|
|
// \r\n is also supported as a line separator
|
|
That(`print "lorem\r\nipsum\r\n" | all`).Puts("lorem", "ipsum"),
|
|
)
|
|
}
|
|
|
|
func TestExceptionCapture(t *testing.T) {
|
|
Test(t,
|
|
// Exception capture
|
|
That("bool ?(nop); bool ?(e:false)").Puts(true, false),
|
|
)
|
|
}
|
|
|
|
func TestVariableUse(t *testing.T) {
|
|
Test(t,
|
|
That("var x = foo", "put $x").Puts("foo"),
|
|
// Must exist before use
|
|
That("put $x").DoesNotCompile(),
|
|
That("put $x[0]").DoesNotCompile(),
|
|
// Compounding
|
|
That("var x = SHELL", "put 'WOW, SUCH '$x', MUCH COOL'\n").
|
|
Puts("WOW, SUCH SHELL, MUCH COOL"),
|
|
// Splicing
|
|
That("var x = [elvish rules]", "put $@x").Puts("elvish", "rules"),
|
|
|
|
// Variable namespace
|
|
// ------------------
|
|
|
|
// Unqualified name resolves to local name before upvalue.
|
|
That("var x = outer; { var x = inner; put $x }").Puts("inner"),
|
|
// Unqualified name resolves to upvalue if no local name exists.
|
|
That("var x = outer; { put $x }").Puts("outer"),
|
|
// Unqualified name resolves to builtin if no local name or upvalue
|
|
// exists.
|
|
That("put $true").Puts(true),
|
|
// Names like $:foo are reserved for now.
|
|
That("var x = val; put $:x").DoesNotCompile(),
|
|
|
|
// Pseudo-namespace E: provides read-write access to environment
|
|
// variables. Colons inside the name are supported.
|
|
That("set-env a:b VAL; put $E:a:b").Puts("VAL"),
|
|
That("set E:a:b = VAL2; get-env a:b").Puts("VAL2"),
|
|
|
|
// Pseudo-namespace e: provides readonly access to external commands.
|
|
// Only names ending in ~ are resolved, and resolution always succeeds
|
|
// regardless of whether the command actually exists. Colons inside the
|
|
// name are supported.
|
|
That("put $e:a:b~").Puts(NewExternalCmd("a:b")),
|
|
|
|
// A "normal" namespace access indexes the namespace as a variable.
|
|
That("var ns: = (ns [&a= val]); put $ns:a").Puts("val"),
|
|
// Multi-level namespace access is supported.
|
|
That("var ns: = (ns [&a:= (ns [&b= val])]); put $ns:a:b").Puts("val"),
|
|
)
|
|
}
|
|
|
|
func TestClosure(t *testing.T) {
|
|
Test(t,
|
|
That("{|| }").DoesNothing(),
|
|
That("{|x| put $x} foo").Puts("foo"),
|
|
|
|
// Assigning to captured variable
|
|
That("var x = lorem; {|| set x = ipsum}; put $x").Puts("ipsum"),
|
|
That("var x = lorem; {|| put $x; set x = ipsum }; put $x").
|
|
Puts("lorem", "ipsum"),
|
|
|
|
// Assigning to element of captured variable
|
|
That("var x = a; { set x = b }; put $x").Puts("b"),
|
|
That("var x = [a]; { set x[0] = b }; put $x[0]").Puts("b"),
|
|
|
|
// Shadowing
|
|
That("var x = ipsum; { var x = lorem; put $x }; put $x").
|
|
Puts("lorem", "ipsum"),
|
|
|
|
// Shadowing by argument
|
|
That("var x = ipsum; {|x| put $x; set x = BAD } lorem; put $x").
|
|
Puts("lorem", "ipsum"),
|
|
|
|
// Closure captures new local variables every time
|
|
That("fn f { var x = (num 0); put { set x = (+ $x 1) } { put $x } }",
|
|
"var inc1 put1 = (f); $put1; $inc1; $put1",
|
|
"var inc2 put2 = (f); $put2; $inc2; $put2").Puts(0, 1, 0, 1),
|
|
|
|
// Rest argument.
|
|
That("{|x @xs| put $x $xs } a b c").Puts("a", vals.MakeList("b", "c")),
|
|
That("{|a @b c| put $a $b $c } a b c d").
|
|
Puts("a", vals.MakeList("b", "c"), "d"),
|
|
// Options.
|
|
That("{|a &k=v| put $a $k } foo &k=bar").Puts("foo", "bar"),
|
|
// Option default value.
|
|
That("{|a &k=v| put $a $k } foo").Puts("foo", "v"),
|
|
// Option must have default value
|
|
That("{|&k| }").DoesNotCompile(),
|
|
// Exception when evaluating option default value.
|
|
That("{|&a=[][0]| }").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"),
|
|
// Option default value must be one value.
|
|
That("{|&a=(put foo bar)| }").Throws(
|
|
errs.ArityMismatch{What: "option default value", ValidLow: 1, ValidHigh: 1, Actual: 2},
|
|
"(put foo bar)"),
|
|
|
|
// Argument name must be unqualified.
|
|
That("{|a:b| }").DoesNotCompile(),
|
|
// Argument name must not be empty.
|
|
That("{|''| }").DoesNotCompile(),
|
|
That("{|@| }").DoesNotCompile(),
|
|
// Option name must be unqualified.
|
|
That("{|&a:b=1| }").DoesNotCompile(),
|
|
// Option name must not be empty.
|
|
That("{|&''=b| }").DoesNotCompile(),
|
|
// Should not have multiple rest arguments.
|
|
That("{|@a @b| }").DoesNotCompile(),
|
|
)
|
|
}
|