mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-13 01:47:51 +08:00
47d9766f5c
Previously, to avoid showing deprecation warnings for the next release when the user is running on HEAD, a boolean CLI flag -show-deprecations is introduced, and is set to false in the master branch. The idea is that release branches will have this default to true, so people running released versions will see deprecations. However, this means that people running on HEAD will never see any deprecations unless they use this CLI flag, which is not ideal. This commit replaces the flag bool -show-deprecations with a numerical -deprecation-level flag, which requests deprecations that are active as of release 0.X to be shown. The default value of this flag will be the minor version number of the *last* release, so that people running HEAD will see as many deprecation warnings as people running the last release would. This number will be bumped just before releases.
287 lines
9.1 KiB
Go
287 lines
9.1 KiB
Go
package eval_test
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
. "github.com/elves/elvish/pkg/eval"
|
|
"github.com/elves/elvish/pkg/eval/errs"
|
|
"github.com/elves/elvish/pkg/eval/vals"
|
|
|
|
. "github.com/elves/elvish/pkg/eval/evaltest"
|
|
"github.com/elves/elvish/pkg/prog"
|
|
"github.com/elves/elvish/pkg/testutil"
|
|
)
|
|
|
|
func TestVar(t *testing.T) {
|
|
Test(t,
|
|
// Declaring one variable
|
|
That("var x", "put $x").Puts(nil),
|
|
// Declaring one variable whose name needs to be quoted
|
|
That("var 'a/b'", "put $'a/b'").Puts(nil),
|
|
// Declaring one variable whose name ends in ":".
|
|
That("var a:").DoesNothing(),
|
|
// Declaring multiple variables
|
|
That("var x y", "put $x $y").Puts(nil, nil),
|
|
// Declaring one variable with initial value
|
|
That("var x = foo", "put $x").Puts("foo"),
|
|
// Declaring multiple variables with initial values
|
|
That("var x y = foo bar", "put $x $y").Puts("foo", "bar"),
|
|
// Declaring multiple variables with initial values, including a rest
|
|
// variable in the assignment LHS
|
|
That("var x @y z = a b c d", "put $x $y $z").
|
|
Puts("a", vals.MakeList("b", "c"), "d"),
|
|
// An empty RHS is technically legal although rarely useful.
|
|
That("var @x =", "put $x").Puts(vals.EmptyList),
|
|
// Shadowing.
|
|
That("var x = old; fn f { put $x }", "var x = new; put $x; f").
|
|
Puts("new", "old"),
|
|
|
|
// Variable name that must be quoted after $ must be quoted
|
|
That("var a/b").DoesNotCompile(),
|
|
// Multiple @ not allowed
|
|
That("var x @y @z = a b c d").DoesNotCompile(),
|
|
// Namespace not allowed
|
|
That("var local:a").DoesNotCompile(),
|
|
// Index not allowed
|
|
That("var a[0]").DoesNotCompile(),
|
|
// Composite expression not allowed
|
|
That("var a'b'").DoesNotCompile(),
|
|
)
|
|
}
|
|
|
|
func TestSet(t *testing.T) {
|
|
Test(t,
|
|
// Setting one variable
|
|
That("var x; set x = foo", "put $x").Puts("foo"),
|
|
// An empty RHS is technically legal although rarely useful.
|
|
That("var x; set @x =", "put $x").Puts(vals.EmptyList),
|
|
// Not duplicating tests with TestCommand_Assignment.
|
|
//
|
|
// TODO: After legacy assignment form is removed, transfer tests here.
|
|
|
|
// = is required.
|
|
That("var x; set x").DoesNotCompile(),
|
|
)
|
|
}
|
|
|
|
func TestDel(t *testing.T) {
|
|
Test(t,
|
|
// Deleting variable
|
|
That("x = 1; del x").DoesNothing(),
|
|
That("x = 1; del x; echo $x").DoesNotCompile(),
|
|
That("x = 1; del :x; echo $x").DoesNotCompile(),
|
|
That("x = 1; del local:x; echo $x").DoesNotCompile(),
|
|
// Deleting variable whose name contains special characters
|
|
That("'a/b' = foo; del 'a/b'").DoesNothing(),
|
|
// Deleting element
|
|
That("x = [&k=v &k2=v2]; del x[k2]; keys $x").Puts("k"),
|
|
That("x = [[&k=v &k2=v2]]; del x[0][k2]; keys $x[0]").Puts("k"),
|
|
|
|
// Error cases
|
|
|
|
// Deleting nonexistent variable
|
|
That("del x").DoesNotCompile(),
|
|
// Deleting element of nonexistent variable
|
|
That("del x[0]").DoesNotCompile(),
|
|
// Deleting variable in non-local namespace
|
|
That("del a:b").DoesNotCompile(),
|
|
// Variable name given with $
|
|
That("x = 1; del $x").DoesNotCompile(),
|
|
// Variable name not given as a single primary expression
|
|
That("ab = 1; del a'b'").DoesNotCompile(),
|
|
// Variable name not a string
|
|
That("del [a]").DoesNotCompile(),
|
|
// Variable name has sigil
|
|
That("x = []; del @x").DoesNotCompile(),
|
|
// Variable name not quoted when it should be
|
|
That("'a/b' = foo; del a/b").DoesNotCompile(),
|
|
)
|
|
}
|
|
|
|
func TestAnd(t *testing.T) {
|
|
Test(t,
|
|
That("and $true $false").Puts(false),
|
|
That("and a b").Puts("b"),
|
|
That("and $false b").Puts(false),
|
|
That("and $true b").Puts("b"),
|
|
// short circuit
|
|
That("x = a; and $false (x = b); put $x").Puts(false, "a"),
|
|
)
|
|
}
|
|
|
|
func TestOr(t *testing.T) {
|
|
Test(t,
|
|
That("or $true $false").Puts(true),
|
|
That("or a b").Puts("a"),
|
|
That("or $false b").Puts("b"),
|
|
That("or $true b").Puts(true),
|
|
// short circuit
|
|
That("x = a; or $true (x = b); put $x").Puts(true, "a"),
|
|
)
|
|
}
|
|
|
|
func TestIf(t *testing.T) {
|
|
Test(t,
|
|
That("if true { put then }").Puts("then"),
|
|
That("if $false { put then } else { put else }").Puts("else"),
|
|
That("if $false { put 1 } elif $false { put 2 } else { put 3 }").
|
|
Puts("3"),
|
|
That("if $false { put 2 } elif true { put 2 } else { put 3 }").Puts("2"),
|
|
)
|
|
}
|
|
|
|
func TestTry(t *testing.T) {
|
|
Test(t,
|
|
That("try { nop } except { put bad } else { put good }").Puts("good"),
|
|
That("try { e:false } except - { put bad } else { put good }").
|
|
Puts("bad"),
|
|
That("try { fail tr }").Throws(ErrorWithMessage("tr")),
|
|
That("try { fail tr } finally { put final }").
|
|
Puts("final").
|
|
Throws(ErrorWithMessage("tr")),
|
|
|
|
That("try { fail tr } except { fail ex } finally { put final }").
|
|
Puts("final").
|
|
Throws(ErrorWithMessage("ex")),
|
|
|
|
That("try { fail tr } except { put ex } finally { fail final }").
|
|
Puts("ex").
|
|
Throws(ErrorWithMessage("final")),
|
|
|
|
That("try { fail tr } except { fail ex } finally { fail final }").
|
|
Throws(ErrorWithMessage("final")),
|
|
|
|
// wrong syntax
|
|
That("try { nop } except @a { }").DoesNotCompile(),
|
|
)
|
|
}
|
|
|
|
func TestWhile(t *testing.T) {
|
|
Test(t,
|
|
// while
|
|
That("x=0; while (< $x 4) { put $x; x=(+ $x 1) }").
|
|
Puts("0", 1.0, 2.0, 3.0),
|
|
That("x = 0; while (< $x 4) { put $x; break }").Puts("0"),
|
|
That("x = 0; while (< $x 4) { fail haha }").Throws(AnyError),
|
|
That("x = 0; while (< $x 4) { put $x; x=(+ $x 1) } else { put bad }").
|
|
Puts("0", 1.0, 2.0, 3.0),
|
|
That("while $false { put bad } else { put good }").Puts("good"),
|
|
)
|
|
}
|
|
|
|
func TestFor(t *testing.T) {
|
|
Test(t,
|
|
// for
|
|
That("for x [tempora mores] { put 'O '$x }").
|
|
Puts("O tempora", "O mores"),
|
|
// break
|
|
That("for x [a] { break } else { put $x }").DoesNothing(),
|
|
// else
|
|
That("for x [a] { put $x } else { put $x }").Puts("a"),
|
|
// continue
|
|
That("for x [a b] { put $x; continue; put $x; }").Puts("a", "b"),
|
|
// More than one iterator.
|
|
That("for {x,y} [] { }").DoesNotCompile(),
|
|
// Invalid for loop lvalue. You can't use a var in a namespace other
|
|
// than the local namespace as the lvalue in a for loop.
|
|
That("for no-such-namespace:x [a b] { }").DoesNotCompile(),
|
|
// Exception when evaluating iterable.
|
|
That("for x [][0] { }").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"),
|
|
// More than one iterable.
|
|
That("for x (put a b) { }").Throws(
|
|
errs.ArityMismatch{
|
|
What: "value being iterated",
|
|
ValidLow: 1, ValidHigh: 1, Actual: 2},
|
|
"(put a b)"),
|
|
)
|
|
}
|
|
|
|
func TestFn(t *testing.T) {
|
|
Test(t,
|
|
That("fn f [x]{ put x=$x'.' }; f lorem; f ipsum").
|
|
Puts("x=lorem.", "x=ipsum."),
|
|
// Recursive functions with fn. Regression test for #1206.
|
|
That("fn f [n]{ if (== $n 0) { put 1 } else { * $n (f (- $n 1)) } }; f 3").
|
|
Puts(6.0),
|
|
// Exception thrown by return is swallowed by a fn-defined function.
|
|
That("fn f []{ put a; return; put b }; f").Puts("a"),
|
|
)
|
|
}
|
|
|
|
func TestUse(t *testing.T) {
|
|
libdir, cleanup := testutil.InTestDir()
|
|
defer cleanup()
|
|
|
|
testutil.MustMkdirAll(filepath.Join("a", "b", "c"))
|
|
|
|
writeMod := func(name, content string) {
|
|
fname := filepath.Join(strings.Split(name, "/")...) + ".elv"
|
|
testutil.MustWriteFile(fname, []byte(content), 0600)
|
|
}
|
|
writeMod("has-init", "put has-init")
|
|
writeMod("put-x", "put $x")
|
|
writeMod("lorem", "name = lorem; fn put-name { put $name }")
|
|
writeMod("d", "name = d")
|
|
writeMod("a/b/c/d", "name = a/b/c/d")
|
|
writeMod("a/b/c/x",
|
|
"use ./d; d = $d:name; use ../../../lorem; lorem = $lorem:name")
|
|
|
|
TestWithSetup(t, func(ev *Evaler) { ev.SetLibDir(libdir) },
|
|
That(`use lorem; put $lorem:name`).Puts("lorem"),
|
|
// imports are lexically scoped
|
|
// TODO: Support testing for compilation error
|
|
// That(`{ use lorem }; put $lorem:name`).ErrorsAny(),
|
|
|
|
// use of imported variable is captured in upvalue
|
|
That(`use lorem; { put $lorem:name }`).Puts("lorem"),
|
|
That(`{ use lorem; { put $lorem:name } }`).Puts("lorem"),
|
|
That(`({ use lorem; put { { put $lorem:name } } })`).Puts("lorem"),
|
|
// use of imported function is also captured in upvalue
|
|
That(`{ use lorem; { lorem:put-name } }`).Puts("lorem"),
|
|
|
|
// use of a nested module
|
|
That(`use a/b/c/d; put $d:name`).Puts("a/b/c/d"),
|
|
// module is cached after first use
|
|
That(`use has-init; use has-init`).Puts("has-init"),
|
|
// repeated uses result in the same namespace being imported
|
|
That("use lorem; use lorem lorem2; put $lorem:name $lorem2:name").
|
|
Puts("lorem", "lorem"),
|
|
// overriding module
|
|
That(`use d; put $d:name; use a/b/c/d; put $d:name`).
|
|
Puts("d", "a/b/c/d"),
|
|
// relative uses
|
|
That(`use a/b/c/x; put $x:d $x:lorem`).Puts("a/b/c/d", "lorem"),
|
|
// relative uses from top-level
|
|
That(`use ./d; put $d:name`).Puts("d"),
|
|
|
|
// Renaming module
|
|
That(`use a/b/c/d mod; put $mod:name`).Puts("a/b/c/d"),
|
|
|
|
// Variables defined in the default global scope is invisible from
|
|
// modules
|
|
That("x = foo; use put-x").Throws(AnyError),
|
|
|
|
// TODO: Test module namespace
|
|
|
|
// Wrong uses of "use".
|
|
That("use").DoesNotCompile(),
|
|
That("use a b c").DoesNotCompile(),
|
|
)
|
|
}
|
|
|
|
// Regression test for #1072
|
|
func TestUse_WarnsAboutDeprecatedFeatures(t *testing.T) {
|
|
restore := prog.SetDeprecationLevel(15)
|
|
defer restore()
|
|
libdir, cleanup := testutil.InTestDir()
|
|
defer cleanup()
|
|
testutil.MustWriteFile("dep.elv", []byte("x = (ord 1)"), 0600)
|
|
|
|
TestWithSetup(t, func(ev *Evaler) { ev.SetLibDir(libdir) },
|
|
// Importing module triggers check for deprecated features
|
|
That("use dep").PrintsStderrWith("is deprecated"),
|
|
)
|
|
}
|