github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/eval/compile_value_test.go (about) 1 package eval_test 2 3 import ( 4 "testing" 5 6 . "github.com/markusbkk/elvish/pkg/eval" 7 "github.com/markusbkk/elvish/pkg/eval/errs" 8 . "github.com/markusbkk/elvish/pkg/testutil" 9 10 . "github.com/markusbkk/elvish/pkg/eval/evaltest" 11 "github.com/markusbkk/elvish/pkg/eval/vals" 12 ) 13 14 func TestCompound(t *testing.T) { 15 Test(t, 16 That("put {fi,elvi}sh{1.0,1.1}").Puts( 17 "fish1.0", "fish1.1", "elvish1.0", "elvish1.1"), 18 19 // As a special case, an empty compound expression evaluates to an empty 20 // string. 21 That("put {}").Puts(""), 22 That("put [&k=][k]").Puts(""), 23 24 // TODO: Test the case where fsutil.GetHome returns an error. 25 26 // Error in any of the components throws an exception. 27 That("put a{[][1]}").Throws(ErrorWithType(errs.OutOfRange{}), "[][1]"), 28 // Error in concatenating the values throws an exception. 29 That("put []a").Throws(ErrorWithMessage("cannot concatenate list and string")), 30 // Error when applying tilde throws an exception. 31 That("put ~[]").Throws(ErrorWithMessage("tilde doesn't work on value of type list")), 32 ) 33 } 34 35 func TestIndexing(t *testing.T) { 36 Test(t, 37 That("put [a b c][2]").Puts("c"), 38 That("put [][0]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"), 39 That("put [&key=value][key]").Puts("value"), 40 That("put [&key=value][bad]").Throws( 41 vals.NoSuchKey("bad"), "[&key=value][bad]"), 42 43 That("put (fail x)[a]").Throws(FailError{"x"}, "fail x"), 44 That("put [foo][(fail x)]").Throws(FailError{"x"}, "fail x"), 45 ) 46 } 47 48 func TestListLiteral(t *testing.T) { 49 Test(t, 50 That("put [a b c]").Puts(vals.MakeList("a", "b", "c")), 51 That("put []").Puts(vals.EmptyList), 52 // List expression errors if an element expression errors. 53 That("put [ [][0] ]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"), 54 ) 55 } 56 57 func TestMapLiteral(t *testing.T) { 58 Test(t, 59 That("put [&key=value]").Puts(vals.MakeMap("key", "value")), 60 That("put [&]").Puts(vals.EmptyMap), 61 // Map keys and values may evaluate to multiple values as long as their 62 // numbers match. 63 That("put [&{a b}={foo bar}]").Puts(vals.MakeMap("a", "foo", "b", "bar")), 64 // Map expression errors if a key or value expression errors. 65 That("put [ &[][0]=a ]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"), 66 That("put [ &a=[][0] ]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"), 67 // Map expression errors if number of keys and values in a single pair 68 // does not match. 69 That("put [&{a b}={foo bar lorem}]").Throws(ErrorWithMessage("2 keys but 3 values")), 70 ) 71 } 72 73 func TestStringLiteral(t *testing.T) { 74 Test(t, 75 That(`put 'such \"''literal'`).Puts(`such \"'literal`), 76 That(`put "much \n\033[31;1m$cool\033[m"`). 77 Puts("much \n\033[31;1m$cool\033[m"), 78 ) 79 } 80 81 func TestTilde(t *testing.T) { 82 home := InTempHome(t) 83 ApplyDir(Dir{"file1": "", "file2": ""}) 84 85 Test(t, 86 // Tilde 87 // ----- 88 That("put ~").Puts(home), 89 That("put ~/src").Puts(home+"/src"), 90 // Make sure that tilde processing retains trailing slashes. 91 That("put ~/src/").Puts(home+"/src/"), 92 // Tilde and wildcard. 93 That("put ~/*").Puts(home+"/file1", home+"/file2"), 94 // TODO: Add regression test for #793. 95 // TODO: Add regression test for #1246. 96 ) 97 } 98 99 func TestWildcard(t *testing.T) { 100 Test(t, 101 That("put ***").DoesNotCompile(), 102 ) 103 // More tests in glob_test.go 104 } 105 106 func TestOutputCapture(t *testing.T) { 107 Test(t, 108 // Output capture 109 That("put (put lorem ipsum)").Puts("lorem", "ipsum"), 110 That("put (print \"lorem\nipsum\")").Puts("lorem", "ipsum"), 111 // \r\n is also supported as a line separator 112 That(`print "lorem\r\nipsum\r\n" | all`).Puts("lorem", "ipsum"), 113 ) 114 } 115 116 func TestExceptionCapture(t *testing.T) { 117 Test(t, 118 // Exception capture 119 That("bool ?(nop); bool ?(e:false)").Puts(true, false), 120 ) 121 } 122 123 func TestVariableUse(t *testing.T) { 124 Test(t, 125 That("var x = foo", "put $x").Puts("foo"), 126 // Must exist before use 127 That("put $x").DoesNotCompile(), 128 That("put $x[0]").DoesNotCompile(), 129 // Compounding 130 That("var x = SHELL", "put 'WOW, SUCH '$x', MUCH COOL'\n"). 131 Puts("WOW, SUCH SHELL, MUCH COOL"), 132 // Splicing 133 That("var x = [elvish rules]", "put $@x").Puts("elvish", "rules"), 134 135 // Variable namespace 136 // ------------------ 137 138 // Unqualified name resolves to local name before upvalue. 139 That("var x = outer; { var x = inner; put $x }").Puts("inner"), 140 // Unqualified name resolves to upvalue if no local name exists. 141 That("var x = outer; { put $x }").Puts("outer"), 142 // Unqualified name resolves to builtin if no local name or upvalue 143 // exists. 144 That("put $true").Puts(true), 145 // Names like $:foo are reserved for now. 146 That("var x = val; put $:x").DoesNotCompile(), 147 148 // Pseudo-namespace E: provides read-write access to environment 149 // variables. Colons inside the name are supported. 150 That("set-env a:b VAL; put $E:a:b").Puts("VAL"), 151 That("set E:a:b = VAL2; get-env a:b").Puts("VAL2"), 152 153 // Pseudo-namespace e: provides readonly access to external commands. 154 // Only names ending in ~ are resolved, and resolution always succeeds 155 // regardless of whether the command actually exists. Colons inside the 156 // name are supported. 157 That("put $e:a:b~").Puts(NewExternalCmd("a:b")), 158 159 // A "normal" namespace access indexes the namespace as a variable. 160 That("var ns: = (ns [&a= val]); put $ns:a").Puts("val"), 161 // Multi-level namespace access is supported. 162 That("var ns: = (ns [&a:= (ns [&b= val])]); put $ns:a:b").Puts("val"), 163 ) 164 } 165 166 func TestClosure(t *testing.T) { 167 Test(t, 168 That("{|| }").DoesNothing(), 169 That("{|x| put $x} foo").Puts("foo"), 170 171 // Assigning to captured variable 172 That("var x = lorem; {|| set x = ipsum}; put $x").Puts("ipsum"), 173 That("var x = lorem; {|| put $x; set x = ipsum }; put $x"). 174 Puts("lorem", "ipsum"), 175 176 // Assigning to element of captured variable 177 That("var x = a; { set x = b }; put $x").Puts("b"), 178 That("var x = [a]; { set x[0] = b }; put $x[0]").Puts("b"), 179 180 // Shadowing 181 That("var x = ipsum; { var x = lorem; put $x }; put $x"). 182 Puts("lorem", "ipsum"), 183 184 // Shadowing by argument 185 That("var x = ipsum; {|x| put $x; set x = BAD } lorem; put $x"). 186 Puts("lorem", "ipsum"), 187 188 // Closure captures new local variables every time 189 That("fn f { var x = (num 0); put { set x = (+ $x 1) } { put $x } }", 190 "var inc1 put1 = (f); $put1; $inc1; $put1", 191 "var inc2 put2 = (f); $put2; $inc2; $put2").Puts(0, 1, 0, 1), 192 193 // Rest argument. 194 That("{|x @xs| put $x $xs } a b c").Puts("a", vals.MakeList("b", "c")), 195 That("{|a @b c| put $a $b $c } a b c d"). 196 Puts("a", vals.MakeList("b", "c"), "d"), 197 // Options. 198 That("{|a &k=v| put $a $k } foo &k=bar").Puts("foo", "bar"), 199 // Option default value. 200 That("{|a &k=v| put $a $k } foo").Puts("foo", "v"), 201 // Option must have default value 202 That("{|&k| }").DoesNotCompile(), 203 // Exception when evaluating option default value. 204 That("{|&a=[][0]| }").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"), 205 // Option default value must be one value. 206 That("{|&a=(put foo bar)| }").Throws( 207 errs.ArityMismatch{What: "option default value", ValidLow: 1, ValidHigh: 1, Actual: 2}, 208 "(put foo bar)"), 209 210 // Argument name must be unqualified. 211 That("{|a:b| }").DoesNotCompile(), 212 // Argument name must not be empty. 213 That("{|''| }").DoesNotCompile(), 214 That("{|@| }").DoesNotCompile(), 215 // Option name must be unqualified. 216 That("{|&a:b=1| }").DoesNotCompile(), 217 // Option name must not be empty. 218 That("{|&''=b| }").DoesNotCompile(), 219 // Should not have multiple rest arguments. 220 That("{|@a @b| }").DoesNotCompile(), 221 ) 222 }