From 50c971e5dd150baabd0397bca196578d88832a9f Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Sun, 10 Jan 2021 14:48:21 +0000 Subject: [PATCH] pkg/eval: Split test functions. --- pkg/eval/builtin_fn_container_test.go | 70 ++++++++++++++++-- pkg/eval/builtin_fn_env_test.go | 59 ++++++++++++--- pkg/eval/builtin_fn_flow_test.go | 22 ++++-- pkg/eval/builtin_fn_io_test.go | 66 +++++++++++++++-- pkg/eval/builtin_fn_misc_test.go | 14 ++-- pkg/eval/builtin_fn_num_test.go | 14 +++- pkg/eval/builtin_fn_pred_test.go | 16 +++++ pkg/eval/builtin_fn_str_test.go | 38 +++++++++- pkg/eval/builtin_special_test.go | 65 ++++++++++++----- pkg/eval/closure_test.go | 2 +- pkg/eval/compile_effect_test.go | 54 +++++++++----- pkg/eval/compile_lvalue_test.go | 21 ------ pkg/eval/compile_value_test.go | 100 ++++++++++++++++---------- 13 files changed, 412 insertions(+), 129 deletions(-) delete mode 100644 pkg/eval/compile_lvalue_test.go diff --git a/pkg/eval/builtin_fn_container_test.go b/pkg/eval/builtin_fn_container_test.go index 5c193660..fe2a43b9 100644 --- a/pkg/eval/builtin_fn_container_test.go +++ b/pkg/eval/builtin_fn_container_test.go @@ -11,14 +11,18 @@ import ( "github.com/elves/elvish/pkg/eval/vals" ) -func TestBuiltinFnContainer(t *testing.T) { +func TestNsCmd(t *testing.T) { Test(t, That("put (ns [&name=value])[name]").Puts("value"), That("n: = (ns [&name=value]); put $n:name").Puts("value"), That("ns [&[]=[]]").Throws(errs.BadValue{ What: `key of argument of "ns"`, Valid: "string", Actual: "list"}), + ) +} +func TestMakeMap(t *testing.T) { + Test(t, That("make-map []").Puts(vals.EmptyMap), That("make-map [[k v]]").Puts(vals.MakeMap("k", "v")), That("make-map [[k v] [k v2]]").Puts(vals.MakeMap("k", "v2")), @@ -36,43 +40,91 @@ func TestBuiltinFnContainer(t *testing.T) { What: "input to make-map", Valid: "iterable with 2 elements", Actual: "list with 1 elements"}, "make-map [[k]]"), + ) +} +func TestRange(t *testing.T) { + Test(t, That(`range 3`).Puts(0.0, 1.0, 2.0), That(`range 1 3`).Puts(1.0, 2.0), That(`range 0 10 &step=3`).Puts(0.0, 3.0, 6.0, 9.0), + ) +} +func TestRepeat(t *testing.T) { + Test(t, That(`repeat 4 foo`).Puts("foo", "foo", "foo", "foo"), + ) +} +func TestAssoc(t *testing.T) { + Test(t, That(`put (assoc [0] 0 zero)[0]`).Puts("zero"), That(`put (assoc [&] k v)[k]`).Puts("v"), That(`put (assoc [&k=v] k v2)[k]`).Puts("v2"), - That(`has-key (dissoc [&k=v] k) k`).Puts(false), + ) +} +func TestDissoc(t *testing.T) { + Test(t, + That(`has-key (dissoc [&k=v] k) k`).Puts(false), + ) +} + +func TestAll(t *testing.T) { + Test(t, That(`put foo bar | all`).Puts("foo", "bar"), That(`echo foobar | all`).Puts("foobar"), That(`all [foo bar]`).Puts("foo", "bar"), + ) +} + +func TestOne(t *testing.T) { + Test(t, That(`put foo | one`).Puts("foo"), That(`put | one`).Throws(AnyError), That(`put foo bar | one`).Throws(AnyError), That(`one [foo]`).Puts("foo"), That(`one []`).Throws(AnyError), That(`one [foo bar]`).Throws(AnyError), + ) +} +func TestTake(t *testing.T) { + Test(t, That(`range 100 | take 2`).Puts(0.0, 1.0), - That(`range 100 | drop 98`).Puts(98.0, 99.0), + ) +} +func TestDrop(t *testing.T) { + Test(t, + That(`range 100 | drop 98`).Puts(98.0, 99.0), + ) +} + +func TestHasKey(t *testing.T) { + Test(t, That(`has-key [foo bar] 0`).Puts(true), That(`has-key [foo bar] 0:1`).Puts(true), That(`has-key [foo bar] 0:20`).Puts(false), That(`has-key [&lorem=ipsum &foo=bar] lorem`).Puts(true), That(`has-key [&lorem=ipsum &foo=bar] loremwsq`).Puts(false), + ) +} + +func TestHasValue(t *testing.T) { + Test(t, That(`has-value [&lorem=ipsum &foo=bar] lorem`).Puts(false), That(`has-value [&lorem=ipsum &foo=bar] bar`).Puts(true), That(`has-value [foo bar] bar`).Puts(true), That(`has-value [foo bar] badehose`).Puts(false), That(`has-value "foo" o`).Puts(true), That(`has-value "foo" d`).Puts(false), + ) +} +func TestCount(t *testing.T) { + Test(t, That(`range 100 | count`).Puts("100"), That(`count [(range 100)]`).Puts("100"), That(`count 123`).Puts("3"), @@ -81,13 +133,21 @@ func TestBuiltinFnContainer(t *testing.T) { What: "arguments here", ValidLow: 0, ValidHigh: 1, Actual: 3}, "count 1 2 3"), That(`count $true`).Throws(ErrorWithMessage("cannot get length of a bool")), + ) +} +func TestKeys(t *testing.T) { + Test(t, That(`keys [&]`).DoesNothing(), That(`keys [&a=foo]`).Puts("a"), // Windows does not have an external sort command. Disabled until we have a // builtin sort command. That(`keys [&a=foo &b=bar] | order`).Puts("a", "b"), + ) +} +func TestOrder(t *testing.T) { + Test(t, // Ordering strings That("put foo bar ipsum | order").Puts("bar", "foo", "ipsum"), That("put foo bar bar | order").Puts("bar", "bar", "foo"), @@ -143,8 +203,8 @@ func TestBuiltinFnContainer(t *testing.T) { // &less-than and &reverse That("put 1 10 2 5 | order &reverse &less-than=[a b]{ < $a $b }"). Puts("10", "5", "2", "1"), - // Sort should be stable - test by pretending that all values but one d - // are equal, an check that the order among them has not change d + // Sort should be stable - test by pretending that all values but one + // are equal, an check that the order among them has not changed. That("put l x o x r x e x m | order &less-than=[a b]{ eq $a x }"). Puts("x", "x", "x", "x", "l", "o", "r", "e", "m"), ) diff --git a/pkg/eval/builtin_fn_env_test.go b/pkg/eval/builtin_fn_env_test.go index 249e55e1..e6ba1865 100644 --- a/pkg/eval/builtin_fn_env_test.go +++ b/pkg/eval/builtin_fn_env_test.go @@ -4,34 +4,71 @@ import ( "os" "testing" - "github.com/elves/elvish/pkg/env" "github.com/elves/elvish/pkg/eval" . "github.com/elves/elvish/pkg/eval/evaltest" "github.com/elves/elvish/pkg/eval/vals" ) -func TestBuiltinFnEnv(t *testing.T) { - oldpath := os.Getenv(env.PATH) - listSep := string(os.PathListSeparator) +func TestGetEnv(t *testing.T) { + restore := saveEnv("var") + defer restore() + + os.Unsetenv("var") + Test(t, That(`get-env var`).Throws(eval.ErrNonExistentEnvVar)) + + os.Setenv("var", "test1") Test(t, - That(`get-env var`).Throws(eval.ErrNonExistentEnvVar), - That(`set-env var test1`), That(`get-env var`).Puts("test1"), That(`put $E:var`).Puts("test1"), + ) - That(`set-env var test2`), + os.Setenv("var", "test2") + Test(t, That(`get-env var`).Puts("test2"), That(`put $E:var`).Puts("test2"), + ) +} - That(`has-env var`).Puts(true), - That(`unset-env var`), - That(`has-env var`).Puts(false), +func TestHasEnv(t *testing.T) { + restore := saveEnv("var") + defer restore() + os.Setenv("var", "test1") + Test(t, That(`has-env var`).Puts(true)) + + os.Unsetenv("var") + Test(t, That(`has-env var`).Puts(false)) +} + +func TestSetEnv(t *testing.T) { + restore := saveEnv("var") + defer restore() + + Test(t, That("set-env var test1").DoesNothing()) + if envVal := os.Getenv("var"); envVal != "test1" { + t.Errorf("got $E:var = %q, want 'test1'", envVal) + } +} + +func TestSetEnv_PATH(t *testing.T) { + restore := saveEnv("PATH") + defer restore() + + listSep := string(os.PathListSeparator) + Test(t, That(`set-env PATH /test-path`), That(`put $paths`).Puts(vals.MakeList("/test-path")), That(`paths = [/test-path2 $@paths]`), That(`get-env PATH`).Puts("/test-path2"+listSep+"/test-path"), ) - os.Setenv(env.PATH, oldpath) +} + +func saveEnv(name string) func() { + oldValue, ok := os.LookupEnv(name) + return func() { + if ok { + os.Setenv(name, oldValue) + } + } } diff --git a/pkg/eval/builtin_fn_flow_test.go b/pkg/eval/builtin_fn_flow_test.go index 88e48409..5251ecb8 100644 --- a/pkg/eval/builtin_fn_flow_test.go +++ b/pkg/eval/builtin_fn_flow_test.go @@ -9,11 +9,15 @@ import ( "github.com/elves/elvish/pkg/eval/vals" ) -func TestBuiltinFnFlow(t *testing.T) { +func TestRunParallel(t *testing.T) { Test(t, That(`run-parallel { put lorem } { echo ipsum }`). Puts("lorem").Prints("ipsum\n"), + ) +} +func TestEach(t *testing.T) { + Test(t, That(`put 1 233 | each $put~`).Puts("1", "233"), That(`echo "1\n233" | each $put~`).Puts("1", "233"), That(`echo "1\r\n233" | each $put~`).Puts("1", "233"), @@ -23,8 +27,13 @@ func TestBuiltinFnFlow(t *testing.T) { That(`range 10 | each [x]{ if (== $x 4) { fail haha }; put $x }`). Puts(0.0, 1.0, 2.0, 3.0).Throws(AnyError), // TODO(xiaq): Test that "each" does not close the stdin. - // TODO: test peach + ) +} +// TODO: test peach + +func TestFail(t *testing.T) { + Test(t, That("fail haha").Throws(FailError{"haha"}, "fail haha"), That("fn f { fail haha }", "fail ?(f)").Throws( FailError{"haha"}, "fail haha ", "f"), @@ -32,7 +41,12 @@ func TestBuiltinFnFlow(t *testing.T) { FailError{vals.EmptyList}, "fail []"), That("put ?(fail 1)[reason][type]").Puts("fail"), That("put ?(fail 1)[reason][content]").Puts("1"), - - That(`return`).Throws(Return), + ) +} + +func TestReturn(t *testing.T) { + Test(t, + That("return").Throws(Return), + // Use of return inside fn is tested in TestFn ) } diff --git a/pkg/eval/builtin_fn_io_test.go b/pkg/eval/builtin_fn_io_test.go index cf2b54dd..92c78489 100644 --- a/pkg/eval/builtin_fn_io_test.go +++ b/pkg/eval/builtin_fn_io_test.go @@ -7,47 +7,105 @@ import ( "github.com/elves/elvish/pkg/eval/vals" ) -func TestBuiltinFnIO(t *testing.T) { +func TestPut(t *testing.T) { Test(t, That(`put foo bar`).Puts("foo", "bar"), That(`put $nil`).Puts(nil), + ) +} +func TestReadUpto(t *testing.T) { + Test(t, That("print abcd | read-upto c").Puts("abc"), // read-upto does not consume more than needed That("print abcd | { read-upto c; slurp }").Puts("abc", "d"), // read-upto reads up to EOF That("print abcd | read-upto z").Puts("abcd"), + ) +} +func TestReadLine(t *testing.T) { + Test(t, That(`print eof-ending | read-line`).Puts("eof-ending"), That(`print "lf-ending\n" | read-line`).Puts("lf-ending"), That(`print "crlf-ending\r\n" | read-line`).Puts("crlf-ending"), That(`print "extra-cr\r\r\n" | read-line`).Puts("extra-cr\r"), + ) +} +func TestPrint(t *testing.T) { + Test(t, That(`print [foo bar]`).Prints("[foo bar]"), That(`print foo bar &sep=,`).Prints("foo,bar"), - That(`echo [foo bar]`).Prints("[foo bar]\n"), - That(`pprint [foo bar]`).Prints("[\n foo\n bar\n]\n"), - That(`repr foo bar ['foo bar']`).Prints("foo bar ['foo bar']\n"), + ) +} +func TestEcho(t *testing.T) { + Test(t, + That(`echo [foo bar]`).Prints("[foo bar]\n"), + ) +} + +func TestPprint(t *testing.T) { + Test(t, + That(`pprint [foo bar]`).Prints("[\n foo\n bar\n]\n"), + ) +} + +func TestReprCmd(t *testing.T) { + Test(t, + That(`repr foo bar ['foo bar']`).Prints("foo bar ['foo bar']\n"), + ) +} + +func TestShow(t *testing.T) { + Test(t, // A sanity test that show writes something. That(`show ?(fail foo) | !=s (slurp) ''`).Puts(true), + ) +} +func TestOnlyBytesAndOnlyValues(t *testing.T) { + Test(t, // Baseline for only-{bytes,values} That(`{ print bytes; put values }`).Prints("bytes").Puts("values"), That(`{ print bytes; put values } | only-bytes`).Prints("bytes").Puts(), That(`{ print bytes; put values } | only-values`).Prints("").Puts("values"), + ) +} +func TestSlurp(t *testing.T) { + Test(t, That(`print "a\nb" | slurp`).Puts("a\nb"), + ) +} + +func TestFromLines(t *testing.T) { + Test(t, That(`print "a\nb" | from-lines`).Puts("a", "b"), That(`print "a\nb\n" | from-lines`).Puts("a", "b"), + ) +} + +func TestFromJson(t *testing.T) { + Test(t, That(`echo '{"k": "v", "a": [1, 2]}' '"foo"' | from-json`). Puts(vals.MakeMap("k", "v", "a", vals.MakeList(1.0, 2.0)), "foo"), That(`echo '[null, "foo"]' | from-json`).Puts( vals.MakeList(nil, "foo")), That(`echo 'invalid' | from-json`).Throws(AnyError), + ) +} +func TestToLines(t *testing.T) { + Test(t, That(`put "l\norem" ipsum | to-lines`).Prints("l\norem\nipsum\n"), + ) +} + +func TestToJson(t *testing.T) { + Test(t, That(`put [&k=v &a=[1 2]] foo | to-json`). Prints(`{"a":["1","2"],"k":"v"} "foo" diff --git a/pkg/eval/builtin_fn_misc_test.go b/pkg/eval/builtin_fn_misc_test.go index f7b3b4af..723f59d5 100644 --- a/pkg/eval/builtin_fn_misc_test.go +++ b/pkg/eval/builtin_fn_misc_test.go @@ -12,12 +12,14 @@ import ( "github.com/elves/elvish/pkg/testutil" ) -func TestBuiltinFnMisc(t *testing.T) { +func TestConstantly(t *testing.T) { Test(t, That(`f = (constantly foo); $f; $f`).Puts("foo", "foo"), + ) +} - // eval - // ==== +func TestEval(t *testing.T) { + Test(t, That("eval 'put x'").Puts("x"), // Using variable from the local scope. That("x = foo; eval 'put $x'").Puts("foo"), @@ -43,9 +45,11 @@ func TestBuiltinFnMisc(t *testing.T) { That("eval 'put $x'").Throws(AnyError), // Exception. That("eval 'fail x'").Throws(FailError{"x"}), + ) +} - // Test the "time" builtin. - // +func TestTime(t *testing.T) { + Test(t, // Since runtime duration is non-deterministic, we only have some sanity // checks here. That("time { echo foo } | a _ = (all)", "put $a").Puts("foo"), diff --git a/pkg/eval/builtin_fn_num_test.go b/pkg/eval/builtin_fn_num_test.go index f34821c6..19f5baf4 100644 --- a/pkg/eval/builtin_fn_num_test.go +++ b/pkg/eval/builtin_fn_num_test.go @@ -10,11 +10,15 @@ import ( . "github.com/elves/elvish/pkg/eval/evaltest" ) -func TestBuiltinFnNum(t *testing.T) { +func TestFloat64(t *testing.T) { Test(t, That("float64 1").Puts(1.0), That("float64 (float64 1)").Puts(1.0), + ) +} +func TestNumberComparisonCommands(t *testing.T) { + Test(t, That("< 1 2 3").Puts(true), That("< 1 3 2").Puts(false), That("<= 1 1 2").Puts(true), @@ -27,7 +31,11 @@ func TestBuiltinFnNum(t *testing.T) { That("> 3 1 2").Puts(false), That(">= 3 3 2").Puts(true), That(">= 3 2 3").Puts(false), + ) +} +func TestArithmeticCommands(t *testing.T) { + Test(t, // TODO test more edge cases That("+ 233100 233").Puts(233333.0), That("- 233333 233100").Puts(233.0), @@ -37,7 +45,11 @@ func TestBuiltinFnNum(t *testing.T) { That("/ 1 0").Puts(math.Inf(1)), That("% 23 7").Puts("2"), That("% 1 0").Throws(AnyError), + ) +} +func TestRandint(t *testing.T) { + Test(t, That("randint 1 2").Puts("1"), That("i = (randint 10 100); >= $i 10; < $i 100").Puts(true, true), That("randint 2 1").Throws(ErrArgs, "randint 2 1"), diff --git a/pkg/eval/builtin_fn_pred_test.go b/pkg/eval/builtin_fn_pred_test.go index ad5c7f04..30c4e7bc 100644 --- a/pkg/eval/builtin_fn_pred_test.go +++ b/pkg/eval/builtin_fn_pred_test.go @@ -19,23 +19,39 @@ func TestBool(t *testing.T) { // Only errors and $false are false That(`bool ?(fail x)`).Puts(false), That(`bool $false`).Puts(false), + ) +} +func TestNot(t *testing.T) { + Test(t, That(`not $false`).Puts(true), That(`not ?(fail x)`).Puts(true), That(`not $true`).Puts(false), That(`not 0`).Puts(false), + ) +} +func TestIs(t *testing.T) { + Test(t, That(`is 1 1`).Puts(true), That(`is a b`).Puts(false), That(`is [] []`).Puts(true), That(`is [1] [1]`).Puts(false), + ) +} +func TestEq(t *testing.T) { + Test(t, That(`eq 1 1`).Puts(true), That(`eq a b`).Puts(false), That(`eq [] []`).Puts(true), That(`eq [1] [1]`).Puts(true), That(`eq 1 1 2`).Puts(false), + ) +} +func TestNotEq(t *testing.T) { + Test(t, That(`not-eq a b`).Puts(true), That(`not-eq a a`).Puts(false), // not-eq is true as long as each adjacent pair is not equal. diff --git a/pkg/eval/builtin_fn_str_test.go b/pkg/eval/builtin_fn_str_test.go index 0eccf44a..45eb7e57 100644 --- a/pkg/eval/builtin_fn_str_test.go +++ b/pkg/eval/builtin_fn_str_test.go @@ -6,7 +6,7 @@ import ( . "github.com/elves/elvish/pkg/eval/evaltest" ) -func TestBuiltinFnStr(t *testing.T) { +func TestStringComparisonCommands(t *testing.T) { Test(t, That(`=s a a`).Puts(true), That(`>=s a b`).Puts(false), That(`>=s b a`).Puts(true), + ) +} +func TestToString(t *testing.T) { + Test(t, That(`to-string str (float64 1) $true`).Puts("str", "1", "$true"), + ) +} +func TestOrd(t *testing.T) { + Test(t, That(`ord a`).Puts("0x61"), That(`ord 你好`).Puts("0x4f60", "0x597d"), + ) +} + +func TestChr(t *testing.T) { + Test(t, That(`chr 0x61`).Puts("a"), That(`chr 0x4f60 0x597d`).Puts("你好"), That(`chr -1`).Throws(AnyError), + ) +} +func TestBase(t *testing.T) { + Test(t, That(`base 2 1 3 4 16 255`).Puts("1", "11", "100", "10000", "11111111"), That(`base 16 42 233`).Puts("2a", "e9"), That(`base 1 1`).Throws(AnyError), // no base-1 That(`base 37 10`).Throws(AnyError), // no letter for base-37 + ) +} +func TestWcswidth(t *testing.T) { + Test(t, That(`wcswidth 你好`).Puts("4"), That(`-override-wcwidth x 10; wcswidth 1x2x; -override-wcwidth x 1`). Puts("22"), + ) +} +func TestHasPrefix(t *testing.T) { + Test(t, That(`has-prefix golang go`).Puts(true), That(`has-prefix golang x`).Puts(false), - That(`has-suffix golang x`).Puts(false), + ) +} +func TestHasSuffix(t *testing.T) { + Test(t, + That(`has-suffix golang x`).Puts(false), + ) +} + +func TestEawk(t *testing.T) { + Test(t, That(`echo " ax by cz \n11\t22 33" | eawk [@a]{ put $a[-1] }`). Puts("cz", "33"), ) diff --git a/pkg/eval/builtin_special_test.go b/pkg/eval/builtin_special_test.go index 317ea882..3b4c2898 100644 --- a/pkg/eval/builtin_special_test.go +++ b/pkg/eval/builtin_special_test.go @@ -13,7 +13,7 @@ import ( "github.com/elves/elvish/pkg/testutil" ) -func TestBuiltinSpecial(t *testing.T) { +func TestDel(t *testing.T) { Test(t, // del - deleting variable That("x = 1; del x").DoesNothing(), @@ -31,48 +31,69 @@ func TestBuiltinSpecial(t *testing.T) { That("x = 1; del $x").DoesNotCompile(), That("del [a]").DoesNotCompile(), That("x = []; del @x").DoesNotCompile(), + ) +} - // and +func TestAnd(t *testing.T) { + Test(t, That("and $true $false").Puts(false), - // and - short circuit + 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"), + ) +} - // or +func TestOr(t *testing.T) { + Test(t, That("or $true $false").Puts(true), That("or a b").Puts("a"), - // or - short circuit + 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"), + ) +} - // if +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"), + ) +} - // try +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")), + Puts("final"). + Throws(ErrorWithMessage("tr")), That("try { fail tr } except { fail ex } finally { put final }"). - Puts("final").Throws(ErrorWithMessage( - "ex")), + Puts("final"). + Throws(ErrorWithMessage("ex")), That("try { fail tr } except { put ex } finally { fail final }"). - Puts("ex").Throws(ErrorWithMessage( - "final")), + Puts("ex"). + Throws(ErrorWithMessage("final")), - That("try { fail tr } except { fail ex } finally { fail final }").Throws(ErrorWithMessage( - "final")), + That("try { fail tr } except { fail ex } finally { fail final }"). + Throws(ErrorWithMessage("final")), - // try - wrong use + // 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), @@ -81,7 +102,11 @@ func TestBuiltinSpecial(t *testing.T) { 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"), @@ -104,15 +129,17 @@ func TestBuiltinSpecial(t *testing.T) { What: "value being iterated", ValidLow: 1, ValidHigh: 1, Actual: 2}, "(put a b)"), + ) +} - // fn. +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), - - // return. + // Exception thrown by return is swallowed by a fn-defined function. That("fn f []{ put a; return; put b }; f").Puts("a"), ) } diff --git a/pkg/eval/closure_test.go b/pkg/eval/closure_test.go index d7d1233f..d937f626 100644 --- a/pkg/eval/closure_test.go +++ b/pkg/eval/closure_test.go @@ -10,7 +10,7 @@ import ( . "github.com/elves/elvish/pkg/eval/evaltest" ) -func TestClosure(t *testing.T) { +func TestClosureAsValue(t *testing.T) { Test(t, // Basic operations as a value. That("kind-of { }").Puts("fn"), diff --git a/pkg/eval/compile_effect_test.go b/pkg/eval/compile_effect_test.go index bc4e7e42..85bcd562 100644 --- a/pkg/eval/compile_effect_test.go +++ b/pkg/eval/compile_effect_test.go @@ -5,34 +5,30 @@ import ( "github.com/elves/elvish/pkg/eval/errs" + . "github.com/elves/elvish/pkg/eval" . "github.com/elves/elvish/pkg/eval/evaltest" "github.com/elves/elvish/pkg/eval/vals" "github.com/elves/elvish/pkg/eval/vars" "github.com/elves/elvish/pkg/testutil" ) -func TestCompileEffect(t *testing.T) { - _, cleanup := testutil.InTestDir() - defer cleanup() - +func TestChunk(t *testing.T) { Test(t, - // Chunks - // ------ - // Empty chunk That("").DoesNothing(), // Outputs of pipelines in a chunk are concatenated That("put x; put y; put z").Puts("x", "y", "z"), // A failed pipeline cause the whole chunk to fail That("put a; e:false; put b").Puts("a").Throws(AnyError), + ) +} - // Pipelines - // --------- - +func TestPipeline(t *testing.T) { + Test(t, // Pure byte pipeline That(`echo "Albert\nAllan\nAlbraham\nBerlin" | sed s/l/1/g | grep e`). Prints("A1bert\nBer1in\n"), - // Pure channel pipeline + // Pure value pipeline That(`put 233 42 19 | each [x]{+ $x 10}`).Puts(243.0, 52.0, 29.0), // Pipeline draining. That(`range 100 | put x`).Puts("x"), @@ -44,10 +40,11 @@ func TestCompileEffect(t *testing.T) { "slurp < $p", "prclose $p").Puts("foo"), // TODO: Add a useful hybrid pipeline sample + ) +} - // Commands - // -------- - +func TestCommand(t *testing.T) { + Test(t, That("put foo").Puts("foo"), // Command errors when the head is not a single value. That("{put put} foo").Throws( @@ -69,14 +66,25 @@ func TestCompileEffect(t *testing.T) { "put &[]=[]"), // Command errors when any optional evaluation errors. That("put &x=[][1]").Throws(ErrorWithType(errs.OutOfRange{}), "[][1]"), + ) +} +func TestCommand_Special(t *testing.T) { + Test(t, // Regression test for #1204; ensures that the arguments of special // forms are not accidentally compiled twice. That("nop (and (use builtin)); nop $builtin:echo~").DoesNothing(), - // Assignments - // ----------- + // Behavior of individual special commands are tested in + // builtin_special_test.go. + ) +} +func TestCommand_Assignment(t *testing.T) { + // NOTE: TestClosure has more tests for the interaction between assignment + // and variable scoping. + + Test(t, // Spacey assignment. That("a = foo; put $a").Puts("foo"), That("a b = foo bar; put $a $b").Puts("foo", "bar"), @@ -141,9 +149,17 @@ func TestCompileEffect(t *testing.T) { ValidLow: 2, ValidHigh: -1, Actual: 1}, "x y @z = 1"), - // Redirections - // ------------ + // Trying to add a new name in a namespace throws an exception. + // Regression test for #1214. + That("ns: = (ns [&]); ns:a = b").Throws(NoSuchVariable("ns:a"), "ns:a = b"), + ) +} +func TestCommand_Redir(t *testing.T) { + _, cleanup := testutil.InTestDir() + defer cleanup() + + Test(t, // Output and input redirection. That("echo 233 > out1", " slurp < out1").Puts("233\n"), // Append. @@ -198,7 +214,7 @@ func TestCompileEffect(t *testing.T) { ) } -func TestStacktrace(t *testing.T) { +func TestCommand_Stacktrace(t *testing.T) { oops := ErrorWithMessage("oops") Test(t, // Stack traces. diff --git a/pkg/eval/compile_lvalue_test.go b/pkg/eval/compile_lvalue_test.go deleted file mode 100644 index 1e437bbb..00000000 --- a/pkg/eval/compile_lvalue_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package eval_test - -import ( - "testing" - - . "github.com/elves/elvish/pkg/eval" - . "github.com/elves/elvish/pkg/eval/evaltest" -) - -func TestAssignment(t *testing.T) { - Test(t, - That("x = a; put $x").Puts("a"), - That("x = [a]; x[0] = b; put $x[0]").Puts("b"), - That("x = a; { x = b }; put $x").Puts("b"), - That("x = [a]; { x[0] = b }; put $x[0]").Puts("b"), - - // Trying to add a new name in a namespace throws an exception. - // Regression test for #1214. - That("ns: = (ns [&]); ns:a = b").Throws(NoSuchVariable("ns:a"), "ns:a = b"), - ) -} diff --git a/pkg/eval/compile_value_test.go b/pkg/eval/compile_value_test.go index feb2342d..b5365ffe 100644 --- a/pkg/eval/compile_value_test.go +++ b/pkg/eval/compile_value_test.go @@ -11,15 +11,8 @@ import ( "github.com/elves/elvish/pkg/eval/vals" ) -func TestCompileValue(t *testing.T) { - home, cleanup := testutil.InTempHome() - testutil.MustCreateEmpty("file1") - testutil.MustCreateEmpty("file2") - defer cleanup() - +func TestCompound(t *testing.T) { Test(t, - // Compounding - // ----------- That("put {fi,elvi}sh{1.0,1.1}").Puts( "fish1.0", "fish1.1", "elvish1.0", "elvish1.1"), @@ -36,52 +29,90 @@ func TestCompileValue(t *testing.T) { 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")), + ) +} - // List, Map and Indexing - // ---------------------- - - That("echo [a b c] [&key=value] | each $put~"). - Puts("[a b c] [&key=value]"), +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]"), + ) +} +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")), - - // List expression errors if an element expression errors. - That("put [ [][0] ]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"), // 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")), + ) +} - // String Literals - // --------------- +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"), + ) +} - // Captures - // --------- +func TestTilde(t *testing.T) { + home, cleanup := testutil.InTempHome() + testutil.MustCreateEmpty("file1") + testutil.MustCreateEmpty("file2") + defer cleanup() + 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(xiaq): Add regression test for #793. + ) +} + +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), + ) +} - // Variable Use - // ------------ - +func TestVariableUse(t *testing.T) { + Test(t, That("x = foo", "put $x").Puts("foo"), // Must exist before use That("put $x").DoesNotCompile(), @@ -136,27 +167,22 @@ func TestCompileValue(t *testing.T) { // Multi-level namespace access can be combined with the up: // pseudo-namespaces. That("ns: = (ns [&a:= (ns [&b= val])]); { put $up:ns:a:b }").Puts("val"), + ) +} - // 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(xiaq): Add regression test for #793. - - // Closure - // ------- - +func TestClosure(t *testing.T) { + Test(t, That("[]{ }").DoesNothing(), That("[x]{put $x} foo").Puts("foo"), - // Variable capture + // Assigning to captured variable That("x=lorem; []{x=ipsum}; put $x").Puts("ipsum"), That("x=lorem; []{ put $x; x=ipsum }; put $x").Puts("lorem", "ipsum"), + // Assigning to element of captured variable + That("x = a; { x = b }; put $x").Puts("b"), + That("x = [a]; { x[0] = b }; put $x[0]").Puts("b"), + // Shadowing That("x=ipsum; []{ local:x=lorem; put $x }; put $x").Puts("lorem", "ipsum"),