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  }