github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/eval/builtin_special_test.go (about) 1 package eval_test 2 3 import ( 4 "errors" 5 "testing" 6 7 "github.com/markusbkk/elvish/pkg/diag" 8 . "github.com/markusbkk/elvish/pkg/eval" 9 "github.com/markusbkk/elvish/pkg/eval/errs" 10 "github.com/markusbkk/elvish/pkg/eval/vals" 11 "github.com/markusbkk/elvish/pkg/eval/vars" 12 "github.com/markusbkk/elvish/pkg/parse" 13 "github.com/markusbkk/elvish/pkg/prog/progtest" 14 15 . "github.com/markusbkk/elvish/pkg/eval/evaltest" 16 . "github.com/markusbkk/elvish/pkg/testutil" 17 ) 18 19 func TestPragma(t *testing.T) { 20 Test(t, 21 That("pragma unknown-command").DoesNotCompile(), 22 That("pragma unknown-command =").DoesNotCompile(), 23 That("pragma unknown-command x").DoesNotCompile(), 24 That("pragma bad-name = some-value").DoesNotCompile(), 25 That("pragma unknown-command = bad").DoesNotCompile(), 26 ) 27 // Actual effect of the unknown-command pragma is tested in TestCommand_External 28 } 29 30 func TestVar(t *testing.T) { 31 // NOTE: TestClosure has more tests for the interaction between assignment 32 // and variable scoping. 33 34 Test(t, 35 // Declaring one variable 36 That("var x", "put $x").Puts(nil), 37 // Declaring one variable whose name needs to be quoted 38 That("var 'a/b'", "put $'a/b'").Puts(nil), 39 // Declaring one variable whose name ends in ":". 40 That("var a:").DoesNothing(), 41 // Declaring a variable whose name ends in "~" initializes it to the 42 // builtin nop function. 43 That("var cmd~; cmd &ignored-opt ignored-arg").DoesNothing(), 44 // Declaring multiple variables 45 That("var x y", "put $x $y").Puts(nil, nil), 46 // Declaring one variable with initial value 47 That("var x = foo", "put $x").Puts("foo"), 48 // Declaring multiple variables with initial values 49 That("var x y = foo bar", "put $x $y").Puts("foo", "bar"), 50 // Declaring multiple variables with initial values, including a rest 51 // variable in the assignment LHS 52 That("var x @y z = a b c d", "put $x $y $z"). 53 Puts("a", vals.MakeList("b", "c"), "d"), 54 // An empty RHS is technically legal although rarely useful. 55 That("var @x =", "put $x").Puts(vals.EmptyList), 56 // Shadowing. 57 That("var x = old; fn f { put $x }", "var x = new; put $x; f"). 58 Puts("new", "old"), 59 60 // Concurrently creating a new variable and accessing existing variable. 61 // Run with "go test -race". 62 That("var x = 1", "put $x | var y = (all)").DoesNothing(), 63 That("nop (var x = 1) | nop").DoesNothing(), 64 65 // Assignment errors when the RHS errors. 66 That("var x = [][1]").Throws(ErrorWithType(errs.OutOfRange{}), "[][1]"), 67 // Arity mismatch. 68 That("var x = 1 2").Throws( 69 errs.ArityMismatch{What: "assignment right-hand-side", 70 ValidLow: 1, ValidHigh: 1, Actual: 2}, 71 "var x = 1 2"), 72 That("var x y = 1").Throws( 73 errs.ArityMismatch{What: "assignment right-hand-side", 74 ValidLow: 2, ValidHigh: 2, Actual: 1}, 75 "var x y = 1"), 76 That("var x y @z = 1").Throws( 77 errs.ArityMismatch{What: "assignment right-hand-side", 78 ValidLow: 2, ValidHigh: -1, Actual: 1}, 79 "var x y @z = 1"), 80 81 // Variable name that must be quoted after $ must be quoted 82 That("var a/b").DoesNotCompile(), 83 // Multiple @ not allowed 84 That("var x @y @z = a b c d").DoesNotCompile(), 85 // Non-local not allowed 86 That("var ns:a").DoesNotCompile(), 87 // Index not allowed 88 That("var a[0]").DoesNotCompile(), 89 // Composite expression not allowed 90 That("var a'b'").DoesNotCompile(), 91 ) 92 } 93 94 func TestSet(t *testing.T) { 95 Test(t, 96 // Setting one variable 97 That("var x; set x = foo", "put $x").Puts("foo"), 98 // An empty RHS is technically legal although rarely useful. 99 That("var x; set @x =", "put $x").Puts(vals.EmptyList), 100 // Variable must already exist 101 That("set x = foo").DoesNotCompile(), 102 // List element assignment 103 That("var li = [foo bar]; set li[0] = 233; put $@li").Puts("233", "bar"), 104 // Variable in list assignment must already be defined. Regression test 105 // for b.elv.sh/889. 106 That("set foobarlorem[0] = a").DoesNotCompile(), 107 // Map element assignment 108 That("var di = [&k=v]; set di[k] = lorem; set di[k2] = ipsum", 109 "put $di[k] $di[k2]").Puts("lorem", "ipsum"), 110 That("var d = [&a=[&b=v]]; put $d[a][b]; set d[a][b] = u; put $d[a][b]"). 111 Puts("v", "u"), 112 That("var li = [foo]; set li[(fail foo)] = bar").Throws(FailError{"foo"}), 113 That("var li = [foo]; set li[0 1] = foo bar"). 114 Throws(ErrorWithMessage("multi indexing not implemented")), 115 That("var li = [[]]; set li[1][2] = bar"). 116 Throws(errs.OutOfRange{What: "index", 117 ValidLow: "0", ValidHigh: "0", Actual: "1"}, "li[1][2]"), 118 119 // Assignment to read-only var is a compile-time error. 120 That("set nil = 1").DoesNotCompile(), 121 That("var a b; set a true b = 1 2 3").DoesNotCompile(), 122 That("set @true = 1").DoesNotCompile(), 123 That("var r; set true @r = 1").DoesNotCompile(), 124 That("var r; set @r true = 1").DoesNotCompile(), 125 126 // Error conditions already covered by TestVar are not repeated. 127 128 // = is required. 129 That("var x; set x").DoesNotCompile(), 130 ) 131 } 132 133 func TestSet_ErrorInSetMethod(t *testing.T) { 134 TestWithSetup(t, func(ev *Evaler) { addBadVar(ev, 0) }, 135 That("set bad = foo").Throws(errBadVar, "bad"), 136 That("var a; set bad @a = foo").Throws(errBadVar, "bad"), 137 That("var a; set a @bad = foo").Throws(errBadVar, "@bad"), 138 That("var a; set @a bad = foo").Throws(errBadVar, "bad"), 139 ) 140 } 141 142 func TestTmp(t *testing.T) { 143 Test(t, 144 That("var x = foo; put $x; { tmp x = bar; put $x }; put $x"). 145 Puts("foo", "bar", "foo"), 146 147 That("var x; tmp x = y").DoesNotCompile(), 148 That("{ tmp x = y }").DoesNotCompile(), 149 ) 150 } 151 152 func TestTmp_ErrorSetting(t *testing.T) { 153 TestWithSetup(t, func(ev *Evaler) { addBadVar(ev, 0) }, 154 That("{ tmp bad = foo }").Throws(errBadVar, "bad", "{ tmp bad = foo }"), 155 ) 156 } 157 158 func TestTmp_ErrorRestoring(t *testing.T) { 159 TestWithSetup(t, func(ev *Evaler) { addBadVar(ev, 1) }, 160 That("{ tmp bad = foo; put after }"). 161 Puts("after"). 162 Throws(ErrorWithMessage("restore variable: bad var"), 163 "bad", "{ tmp bad = foo; put after }"), 164 ) 165 } 166 167 func addBadVar(ev *Evaler, allowedSets int) { 168 ev.ExtendGlobal(BuildNs().AddVar("bad", &badVar{allowedSets})) 169 } 170 171 var errBadVar = errors.New("bad var") 172 173 type badVar struct{ allowedSets int } 174 175 func (v *badVar) Get() interface{} { return nil } 176 177 func (v *badVar) Set(interface{}) error { 178 if v.allowedSets == 0 { 179 return errBadVar 180 } 181 v.allowedSets-- 182 return nil 183 } 184 185 func TestDel(t *testing.T) { 186 Setenv(t, "TEST_ENV", "test value") 187 188 Test(t, 189 // Deleting variable 190 That("var x = 1; del x").DoesNothing(), 191 That("var x = 1; del x; echo $x").DoesNotCompile(), 192 // Deleting environment variable 193 That("has-env TEST_ENV", "del E:TEST_ENV", "has-env TEST_ENV").Puts(true, false), 194 // Deleting variable whose name contains special characters 195 That("var 'a/b' = foo; del 'a/b'").DoesNothing(), 196 // Deleting element 197 That("var x = [&k=v &k2=v2]; del x[k2]; keys $x").Puts("k"), 198 That("var x = [[&k=v &k2=v2]]; del x[0][k2]; keys $x[0]").Puts("k"), 199 200 // Error cases 201 202 // Deleting nonexistent variable 203 That("del x").DoesNotCompile(), 204 // Deleting element of nonexistent variable 205 That("del x[0]").DoesNotCompile(), 206 // Deleting variable in non-local namespace 207 That("var a: = (ns [&b=$nil])", "del a:b").DoesNotCompile(), 208 // Variable name given with $ 209 That("var x = 1; del $x").DoesNotCompile(), 210 // Variable name not given as a single primary expression 211 That("var ab = 1; del a'b'").DoesNotCompile(), 212 // Variable name not a string 213 That("del [a]").DoesNotCompile(), 214 // Variable name has sigil 215 That("var x = []; del @x").DoesNotCompile(), 216 // Variable name not quoted when it should be 217 That("var 'a/b' = foo; del a/b").DoesNotCompile(), 218 219 // Index is multiple values 220 That("var x = [&k1=v1 &k2=v2]", "del x[k1 k2]").Throws( 221 ErrorWithMessage("index must evaluate to a single value in argument to del"), 222 "k1 k2"), 223 // Index expression throws exception 224 That("var x = [&k]", "del x[(fail x)]").Throws(FailError{"x"}, "fail x"), 225 // Value does not support element removal 226 That("var x = (num 1)", "del x[k]").Throws( 227 ErrorWithMessage("value does not support element removal"), 228 // TODO: Fix the stack trace so that it is "x[k]" 229 "x[k"), 230 // Intermediate element does not exist 231 That("var x = [&]", "del x[k][0]").Throws( 232 ErrorWithMessage("no such key: k"), 233 // TODO: Fix the stack trace so that it is "x[k]" 234 "x"), 235 ) 236 } 237 238 func TestAnd(t *testing.T) { 239 Test(t, 240 That("and $true $false").Puts(false), 241 That("and a b").Puts("b"), 242 That("and $false b").Puts(false), 243 That("and $true b").Puts("b"), 244 // short circuit 245 That("var x = a; and $false (x = b); put $x").Puts(false, "a"), 246 247 // Exception 248 That("and a (fail x)").Throws(FailError{"x"}, "fail x"), 249 thatOutputErrorIsBubbled("and a"), 250 ) 251 } 252 253 func TestOr(t *testing.T) { 254 Test(t, 255 That("or $true $false").Puts(true), 256 That("or a b").Puts("a"), 257 That("or $false b").Puts("b"), 258 That("or $true b").Puts(true), 259 // short circuit 260 That("var x = a; or $true (x = b); put $x").Puts(true, "a"), 261 262 // Exception 263 That("or $false (fail x)").Throws(FailError{"x"}, "fail x"), 264 thatOutputErrorIsBubbled("or a"), 265 ) 266 } 267 268 func TestCoalesce(t *testing.T) { 269 Test(t, 270 That("coalesce a b").Puts("a"), 271 That("coalesce $nil b").Puts("b"), 272 That("coalesce $nil $nil").Puts(nil), 273 That("coalesce").Puts(nil), 274 // exception propagation 275 That("coalesce $nil (fail foo)").Throws(FailError{"foo"}), 276 // short circuit 277 That("coalesce a (fail foo)").Puts("a"), 278 279 thatOutputErrorIsBubbled("coalesce a"), 280 ) 281 } 282 283 func TestSpecialFormThunks(t *testing.T) { 284 // Regression test for b.elv.sh/1456 285 Test(t, 286 That("for x [] {|arg| }").DoesNotCompile(), 287 That("for x [] {|&opt=val| }").DoesNotCompile(), 288 // The other special forms use the same utility under the hood and are 289 // not repeated 290 ) 291 } 292 293 func TestIf(t *testing.T) { 294 Test(t, 295 That("if true { put then }").Puts("then"), 296 That("if $false { put then } else { put else }").Puts("else"), 297 That("if $false { put 1 } elif $false { put 2 } else { put 3 }"). 298 Puts("3"), 299 That("if $false { put 2 } elif true { put 2 } else { put 3 }").Puts("2"), 300 301 // Exception in condition expression 302 That("if (fail x) { }").Throws(FailError{"x"}, "fail x"), 303 ) 304 } 305 306 func TestTry(t *testing.T) { 307 Test(t, 308 That("try { nop } except { put bad } else { put good }").Puts("good"), 309 That("try { e:false } except - { put bad } else { put good }"). 310 Puts("bad"), 311 That("try { fail tr }").Throws(ErrorWithMessage("tr")), 312 That("try { fail tr } finally { put final }"). 313 Puts("final"). 314 Throws(ErrorWithMessage("tr")), 315 316 That("try { fail tr } except { fail ex } finally { put final }"). 317 Puts("final"). 318 Throws(ErrorWithMessage("ex")), 319 320 That("try { fail tr } except { put ex } finally { fail final }"). 321 Puts("ex"). 322 Throws(ErrorWithMessage("final")), 323 324 That("try { fail tr } except { fail ex } finally { fail final }"). 325 Throws(ErrorWithMessage("final")), 326 327 // wrong syntax 328 That("try { nop } except @a { }").DoesNotCompile(), 329 330 // A readonly var as a target for the "except" clause is a compile-time 331 // error. 332 That("try { fail reason } except nil { }").DoesNotCompile(), 333 That("try { fail reason } except x { }").DoesNothing(), 334 335 // A quoted var name, that would be invalid as a bareword, should be allowed as the referent 336 // in a `try...except...` block. 337 That("try { fail hard } except 'x=' { put 'x= ='(to-string $'x=') }"). 338 Puts("x= =[&reason=[&content=hard &type=fail]]"), 339 ) 340 } 341 342 func TestWhile(t *testing.T) { 343 Test(t, 344 That("var x = (num 0)", "while (< $x 4) { put $x; set x = (+ $x 1) }"). 345 Puts(0, 1, 2, 3), 346 // break 347 That("var x = (num 0)", "while (< $x 4) { put $x; break }").Puts(0), 348 // continue 349 That("var x = (num 0)", 350 "while (< $x 4) { put $x; set x = (+ $x 1); continue; put bad }"). 351 Puts(0, 1, 2, 3), 352 // Exception in body 353 That("var x = 0; while (< $x 4) { fail haha }").Throws(FailError{"haha"}), 354 // Exception in condition 355 That("while (fail x) { }").Throws(FailError{"x"}, "fail x"), 356 357 // else branch - not taken 358 That("var x = 0; while (< $x 4) { put $x; set x = (+ $x 1) } else { put bad }"). 359 Puts("0", 1, 2, 3), 360 // else branch - taken 361 That("while $false { put bad } else { put good }").Puts("good"), 362 ) 363 } 364 365 func TestFor(t *testing.T) { 366 Test(t, 367 // for 368 That("for x [tempora mores] { put 'O '$x }"). 369 Puts("O tempora", "O mores"), 370 // break 371 That("for x [a] { break } else { put $x }").DoesNothing(), 372 // else 373 That("for x [a] { put $x } else { put $x }").Puts("a"), 374 // continue 375 That("for x [a b] { put $x; continue; put $x; }").Puts("a", "b"), 376 // else 377 That("for x [] { } else { put else }").Puts("else"), 378 That("for x [a] { } else { put else }").DoesNothing(), 379 // Propagating exception. 380 That("for x [a] { fail foo }").Throws(FailError{"foo"}), 381 382 // More than one iterator. 383 That("for {x,y} [] { }").DoesNotCompile(), 384 // Invalid for loop lvalue. You can't use a var in a namespace other 385 // than the local namespace as the lvalue in a for loop. 386 That("for no-such-namespace:x [a b] { }").DoesNotCompile(), 387 // Exception with the variable 388 That("var a: = (ns [&])", "for a:b [] { }").Throws( 389 ErrorWithMessage("no variable $a:b"), 390 "a:b"), 391 // Exception when evaluating iterable. 392 That("for x [][0] { }").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"), 393 // More than one iterable. 394 That("for x (put a b) { }").Throws( 395 errs.ArityMismatch{What: "value being iterated", 396 ValidLow: 1, ValidHigh: 1, Actual: 2}, 397 "(put a b)"), 398 // Non-iterable value 399 That("for x (num 0) { }").Throws(ErrorWithMessage("cannot iterate number")), 400 ) 401 } 402 403 func TestFn(t *testing.T) { 404 Test(t, 405 That("fn f {|x| put x=$x'.' }; f lorem; f ipsum"). 406 Puts("x=lorem.", "x=ipsum."), 407 // Recursive functions with fn. Regression test for #1206. 408 That("fn f {|n| if (== $n 0) { num 1 } else { * $n (f (- $n 1)) } }; f 3"). 409 Puts(6), 410 // Exception thrown by return is swallowed by a fn-defined function. 411 That("fn f { put a; return; put b }; f").Puts("a"), 412 413 // Error when evaluating the lambda 414 That("fn f {|&opt=(fail x)| }").Throws(FailError{"x"}, "fail x"), 415 ) 416 } 417 418 // Regression test for #1225 419 func TestUse_SetsVariableCorrectlyIfModuleCallsExtendGlobal(t *testing.T) { 420 libdir := InTempDir(t) 421 422 ApplyDir(Dir{"a.elv": "add-var"}) 423 ev := NewEvaler() 424 ev.LibDirs = []string{libdir} 425 addVar := func() { 426 ev.ExtendGlobal(BuildNs().AddVar("b", vars.NewReadOnly("foo"))) 427 } 428 ev.ExtendBuiltin(BuildNs().AddGoFn("add-var", addVar)) 429 430 err := ev.Eval(parse.Source{Code: "use a"}, EvalCfg{}) 431 if err != nil { 432 t.Fatal(err) 433 } 434 435 g := ev.Global() 436 if g.IndexString("a:").Get().(*Ns) == nil { 437 t.Errorf("$a: is nil") 438 } 439 if g.IndexString("b").Get().(string) != "foo" { 440 t.Errorf(`$b is not "foo"`) 441 } 442 } 443 444 func TestUse_SupportsCircularDependency(t *testing.T) { 445 libdir := InTempDir(t) 446 ApplyDir(Dir{ 447 "a.elv": "var pre = apre; use b; put $b:pre $b:post; var post = apost", 448 "b.elv": "var pre = bpre; use a; put $a:pre $a:post; var post = bpost", 449 }) 450 451 TestWithSetup(t, func(ev *Evaler) { ev.LibDirs = []string{libdir} }, 452 That(`use a`).Puts( 453 // When b.elv is imported from a.elv, $a:pre is set but $a:post is 454 // not 455 "apre", nil, 456 // After a.elv imports b.elv, both $b:pre and $b:post are set 457 "bpre", "bpost"), 458 ) 459 } 460 461 func TestUse(t *testing.T) { 462 libdir1 := InTempDir(t) 463 ApplyDir(Dir{ 464 "shadow.elv": "put lib1", 465 }) 466 467 libdir2 := InTempDir(t) 468 ApplyDir(Dir{ 469 "has-init.elv": "put has-init", 470 "put-x.elv": "put $x", 471 "lorem.elv": "var name = lorem; fn put-name { put $name }", 472 "d.elv": "var name = d", 473 "shadow.elv": "put lib2", 474 "a": Dir{ 475 "b": Dir{ 476 "c": Dir{ 477 "d.elv": "var name = a/b/c/d", 478 "x.elv": "use ./d; var d = $d:name; use ../../../lorem; var lorem = $lorem:name", 479 }, 480 }, 481 }, 482 }) 483 484 TestWithSetup(t, func(ev *Evaler) { ev.LibDirs = []string{libdir1, libdir2} }, 485 That(`use lorem; put $lorem:name`).Puts("lorem"), 486 // imports are lexically scoped 487 // TODO: Support testing for compilation error 488 That(`{ use lorem }; put $lorem:name`).DoesNotCompile(), 489 490 // prefers lib dir that appear earlier 491 That("use shadow").Puts("lib1"), 492 493 // use of imported variable is captured in upvalue 494 That(`use lorem; { put $lorem:name }`).Puts("lorem"), 495 That(`{ use lorem; { put $lorem:name } }`).Puts("lorem"), 496 That(`({ use lorem; put { { put $lorem:name } } })`).Puts("lorem"), 497 // use of imported function is also captured in upvalue 498 That(`{ use lorem; { lorem:put-name } }`).Puts("lorem"), 499 500 // use of a nested module 501 That(`use a/b/c/d; put $d:name`).Puts("a/b/c/d"), 502 // module is cached after first use 503 That(`use has-init; use has-init`).Puts("has-init"), 504 // repeated uses result in the same namespace being imported 505 That("use lorem; use lorem lorem2; put $lorem:name $lorem2:name"). 506 Puts("lorem", "lorem"), 507 // overriding module 508 That(`use d; put $d:name; use a/b/c/d; put $d:name`). 509 Puts("d", "a/b/c/d"), 510 // relative uses 511 That(`use a/b/c/x; put $x:d $x:lorem`).Puts("a/b/c/d", "lorem"), 512 // relative uses from top-level 513 That(`use ./d; put $d:name`).Puts("d"), 514 515 // Renaming module 516 That(`use a/b/c/d mod; put $mod:name`).Puts("a/b/c/d"), 517 518 // Variables defined in the default global scope is invisible from 519 // modules 520 That("var x = foo; use put-x").Throws(ErrorWithType(&diag.Error{})), 521 522 // Using an unknown module spec fails. 523 That("use unknown").Throws(ErrorWithType(NoSuchModule{})), 524 That("use ./unknown").Throws(ErrorWithType(NoSuchModule{})), 525 That("use ../unknown").Throws(ErrorWithType(NoSuchModule{})), 526 527 // Nonexistent module 528 That("use non-existent").Throws(ErrorWithMessage("no such module: non-existent")), 529 530 // Wrong uses of "use". 531 That("use").DoesNotCompile(), 532 That("use a b c").DoesNotCompile(), 533 ) 534 } 535 536 // Regression test for #1072 537 func TestUse_WarnsAboutDeprecatedFeatures(t *testing.T) { 538 progtest.SetDeprecationLevel(t, 18) 539 libdir := InTempDir(t) 540 MustWriteFile("dep.elv", "a=b nop $a") 541 542 TestWithSetup(t, func(ev *Evaler) { ev.LibDirs = []string{libdir} }, 543 // Importing module triggers check for deprecated features 544 That("use dep").PrintsStderrWith("is deprecated"), 545 ) 546 }