github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/eval/compile_value_test.go (about)

     1  package eval_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	. "src.elv.sh/pkg/eval"
     7  	"src.elv.sh/pkg/eval/errs"
     8  	"src.elv.sh/pkg/testutil"
     9  
    10  	. "src.elv.sh/pkg/eval/evaltest"
    11  	"src.elv.sh/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  }
    44  
    45  func TestListLiteral(t *testing.T) {
    46  	Test(t,
    47  		That("put [a b c]").Puts(vals.MakeList("a", "b", "c")),
    48  		That("put []").Puts(vals.EmptyList),
    49  		// List expression errors if an element expression errors.
    50  		That("put [ [][0] ]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"),
    51  	)
    52  }
    53  
    54  func TestMapLiteral(t *testing.T) {
    55  	Test(t,
    56  		That("put [&key=value]").Puts(vals.MakeMap("key", "value")),
    57  		That("put [&]").Puts(vals.EmptyMap),
    58  		// Map keys and values may evaluate to multiple values as long as their
    59  		// numbers match.
    60  		That("put [&{a b}={foo bar}]").Puts(vals.MakeMap("a", "foo", "b", "bar")),
    61  		// Map expression errors if a key or value expression errors.
    62  		That("put [ &[][0]=a ]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"),
    63  		That("put [ &a=[][0] ]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"),
    64  		// Map expression errors if number of keys and values in a single pair
    65  		// does not match.
    66  		That("put [&{a b}={foo bar lorem}]").Throws(ErrorWithMessage("2 keys but 3 values")),
    67  	)
    68  }
    69  
    70  func TestStringLiteral(t *testing.T) {
    71  	Test(t,
    72  		That(`put 'such \"''literal'`).Puts(`such \"'literal`),
    73  		That(`put "much \n\033[31;1m$cool\033[m"`).
    74  			Puts("much \n\033[31;1m$cool\033[m"),
    75  	)
    76  }
    77  
    78  func TestTilde(t *testing.T) {
    79  	home, cleanup := testutil.InTempHome()
    80  	testutil.MustCreateEmpty("file1")
    81  	testutil.MustCreateEmpty("file2")
    82  	defer cleanup()
    83  
    84  	Test(t,
    85  		// Tilde
    86  		// -----
    87  		That("put ~").Puts(home),
    88  		That("put ~/src").Puts(home+"/src"),
    89  		// Make sure that tilde processing retains trailing slashes.
    90  		That("put ~/src/").Puts(home+"/src/"),
    91  		// Tilde and wildcard.
    92  		That("put ~/*").Puts(home+"/file1", home+"/file2"),
    93  		// TODO: Add regression test for #793.
    94  		// TODO: Add regression test for #1246.
    95  	)
    96  }
    97  
    98  func TestOutputCapture(t *testing.T) {
    99  	Test(t,
   100  		// Output capture
   101  		That("put (put lorem ipsum)").Puts("lorem", "ipsum"),
   102  		That("put (print \"lorem\nipsum\")").Puts("lorem", "ipsum"),
   103  		// \r\n is also supported as a line separator
   104  		That(`print "lorem\r\nipsum\r\n" | all`).Puts("lorem", "ipsum"),
   105  	)
   106  }
   107  
   108  func TestExceptionCapture(t *testing.T) {
   109  	Test(t,
   110  		// Exception capture
   111  		That("bool ?(nop); bool ?(e:false)").Puts(true, false),
   112  	)
   113  }
   114  
   115  func TestVariableUse(t *testing.T) {
   116  	Test(t,
   117  		That("x = foo", "put $x").Puts("foo"),
   118  		// Must exist before use
   119  		That("put $x").DoesNotCompile(),
   120  		That("put $x[0]").DoesNotCompile(),
   121  		// Compounding
   122  		That("x = SHELL", "put 'WOW, SUCH '$x', MUCH COOL'\n").
   123  			Puts("WOW, SUCH SHELL, MUCH COOL"),
   124  		// Splicing
   125  		That("x = [elvish rules]", "put $@x").Puts("elvish", "rules"),
   126  
   127  		// Variable namespace
   128  		// ------------------
   129  
   130  		// Pseudo-namespace local: accesses the local scope.
   131  		That("x = outer; { local:x = inner; put $local:x }").Puts("inner"),
   132  		// Pseudo-namespace up: accesses upvalues.
   133  		That("x = outer; { local:x = inner; put $up:x }").Puts("outer"),
   134  		// Unqualified name prefers local: to up:.
   135  		That("x = outer; { local:x = inner; put $x }").Puts("inner"),
   136  		// Unqualified name resolves to upvalue if no local name exists.
   137  		That("x = outer; { put $x }").Puts("outer"),
   138  		// Unqualified name resolves to builtin if no local name or upvalue
   139  		// exists.
   140  		That("put $true").Puts(true),
   141  		// A name can be explicitly unqualified by having a leading colon.
   142  		That("x = val; put $:x").Puts("val"),
   143  		That("put $:true").Puts(true),
   144  
   145  		// Pseudo-namespace E: provides read-write access to environment
   146  		// variables. Colons inside the name are supported.
   147  		That("set-env a:b VAL; put $E:a:b").Puts("VAL"),
   148  		That("E:a:b = VAL2; get-env a:b").Puts("VAL2"),
   149  
   150  		// Pseudo-namespace e: provides readonly access to external commands.
   151  		// Only names ending in ~ are resolved, and resolution always succeeds
   152  		// regardless of whether the command actually exists. Colons inside the
   153  		// name are supported.
   154  		That("put $e:a:b~").Puts(NewExternalCmd("a:b")),
   155  
   156  		// A "normal" namespace access indexes the namespace as a variable.
   157  		That("ns: = (ns [&a= val]); put $ns:a").Puts("val"),
   158  		// Multi-level namespace access is supported.
   159  		That("ns: = (ns [&a:= (ns [&b= val])]); put $ns:a:b").Puts("val"),
   160  		// Multi-level namespace access can have a leading colon to signal that
   161  		// the first component is unqualified.
   162  		That("ns: = (ns [&a:= (ns [&b= val])]); put $:ns:a:b").Puts("val"),
   163  		// Multi-level namespace access can be combined with the local:
   164  		// pseudo-namespaces.
   165  		That("ns: = (ns [&a:= (ns [&b= val])]); put $local:ns:a:b").Puts("val"),
   166  		// Multi-level namespace access can be combined with the up:
   167  		// pseudo-namespaces.
   168  		That("ns: = (ns [&a:= (ns [&b= val])]); { put $up:ns:a:b }").Puts("val"),
   169  	)
   170  }
   171  
   172  func TestClosure(t *testing.T) {
   173  	Test(t,
   174  		That("[]{ }").DoesNothing(),
   175  		That("[x]{put $x} foo").Puts("foo"),
   176  
   177  		// Assigning to captured variable
   178  		That("var x = lorem; []{set x = ipsum}; put $x").Puts("ipsum"),
   179  		That("var x = lorem; []{ put $x; set x = ipsum }; put $x").
   180  			Puts("lorem", "ipsum"),
   181  
   182  		// Assigning to element of captured variable
   183  		That("x = a; { x = b }; put $x").Puts("b"),
   184  		That("x = [a]; { x[0] = b }; put $x[0]").Puts("b"),
   185  
   186  		// Shadowing
   187  		That("var x = ipsum; []{ var x = lorem; put $x }; put $x").
   188  			Puts("lorem", "ipsum"),
   189  
   190  		// Shadowing by argument
   191  		That("var x = ipsum; [x]{ put $x; set x = BAD } lorem; put $x").
   192  			Puts("lorem", "ipsum"),
   193  
   194  		// Closure captures new local variables every time
   195  		That("fn f []{ var x = (num 0); put { set x = (+ $x 1) } { put $x } }",
   196  			"var inc1 put1 = (f); $put1; $inc1; $put1",
   197  			"var inc2 put2 = (f); $put2; $inc2; $put2").Puts(0, 1, 0, 1),
   198  
   199  		// Rest argument.
   200  		That("[x @xs]{ put $x $xs } a b c").Puts("a", vals.MakeList("b", "c")),
   201  		That("[a @b c]{ put $a $b $c } a b c d").
   202  			Puts("a", vals.MakeList("b", "c"), "d"),
   203  		// Options.
   204  		That("[a &k=v]{ put $a $k } foo &k=bar").Puts("foo", "bar"),
   205  		// Option default value.
   206  		That("[a &k=v]{ put $a $k } foo").Puts("foo", "v"),
   207  
   208  		// Argument name must be unqualified.
   209  		That("[a:b]{ }").DoesNotCompile(),
   210  		// Argument name must not be empty.
   211  		That("['']{ }").DoesNotCompile(),
   212  		That("[@]{ }").DoesNotCompile(),
   213  		// Option name must be unqualified.
   214  		That("[&a:b=1]{ }").DoesNotCompile(),
   215  		// Option name must not be empty.
   216  		That("[&''=b]{ }").DoesNotCompile(),
   217  
   218  		// Exception when evaluating option default value.
   219  		That("[&a=[][0]]{ }").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"),
   220  		// Option default value must be one value.
   221  		That("[&a=(put foo bar)]{ }").Throws(
   222  			errs.ArityMismatch{
   223  				What: "option default value", ValidLow: 1, ValidHigh: 1, Actual: 2},
   224  			"(put foo bar)"),
   225  	)
   226  }