github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/eval/compile_effect_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/eval/evaltest"
     9  	"src.elv.sh/pkg/eval/vals"
    10  	"src.elv.sh/pkg/testutil"
    11  )
    12  
    13  func TestChunk(t *testing.T) {
    14  	Test(t,
    15  		// Empty chunk
    16  		That("").DoesNothing(),
    17  		// Outputs of pipelines in a chunk are concatenated
    18  		That("put x; put y; put z").Puts("x", "y", "z"),
    19  		// A failed pipeline cause the whole chunk to fail
    20  		That("put a; e:false; put b").Puts("a").Throws(AnyError),
    21  	)
    22  }
    23  
    24  func TestPipeline(t *testing.T) {
    25  	Test(t,
    26  		// Pure byte pipeline
    27  		That(`echo "Albert\nAllan\nAlbraham\nBerlin" | sed s/l/1/g | grep e`).
    28  			Prints("A1bert\nBer1in\n"),
    29  		// Pure value pipeline
    30  		That(`put 233 42 19 | each [x]{+ $x 10}`).Puts(243, 52, 29),
    31  		// Pipeline draining.
    32  		That(`range 100 | put x`).Puts("x"),
    33  		// Background pipeline.
    34  		That(
    35  			"notify-bg-job-success = $false",
    36  			"p = (pipe)",
    37  			"{ print foo > $p; pwclose $p }&",
    38  			"slurp < $p",
    39  			"prclose $p").Puts("foo"),
    40  		// TODO: Add a useful hybrid pipeline sample
    41  	)
    42  }
    43  
    44  func TestCommand(t *testing.T) {
    45  	Test(t,
    46  		That("put foo").Puts("foo"),
    47  		// Command errors when the head is not a single value.
    48  		That("{put put} foo").Throws(
    49  			errs.ArityMismatch{
    50  				What: "command", ValidLow: 1, ValidHigh: 1, Actual: 2},
    51  			"{put put}"),
    52  		// Command errors when the head is not callable or string containing slash.
    53  		That("[] foo").Throws(
    54  			errs.BadValue{
    55  				What:   "command",
    56  				Valid:  "callable or string containing slash",
    57  				Actual: "list"},
    58  			"[]"),
    59  		// Command errors when when argument errors.
    60  		That("put [][1]").Throws(ErrorWithType(errs.OutOfRange{}), "[][1]"),
    61  		// Command errors when an option key is not string.
    62  		That("put &[]=[]").Throws(
    63  			errs.BadValue{What: "option key", Valid: "string", Actual: "list"},
    64  			"put &[]=[]"),
    65  		// Command errors when any optional evaluation errors.
    66  		That("put &x=[][1]").Throws(ErrorWithType(errs.OutOfRange{}), "[][1]"),
    67  	)
    68  }
    69  
    70  func TestCommand_Special(t *testing.T) {
    71  	Test(t,
    72  		// Regression test for #1204; ensures that the arguments of special
    73  		// forms are not accidentally compiled twice.
    74  		That("nop (and (use builtin)); nop $builtin:echo~").DoesNothing(),
    75  
    76  		// Behavior of individual special commands are tested in
    77  		// builtin_special_test.go.
    78  	)
    79  }
    80  
    81  func TestCommand_Assignment(t *testing.T) {
    82  	// NOTE: TestClosure has more tests for the interaction between assignment
    83  	// and variable scoping.
    84  
    85  	Test(t,
    86  		// Spacey assignment.
    87  		That("a = foo; put $a").Puts("foo"),
    88  		That("a b = foo bar; put $a $b").Puts("foo", "bar"),
    89  		That("a @b = 2 3 foo; put $a $b").Puts("2", vals.MakeList("3", "foo")),
    90  		That("a @b c = 1 2 3 4; put $a $b $c").
    91  			Puts("1", vals.MakeList("2", "3"), "4"),
    92  		That("a @b c = 1 2; put $a $b $c").Puts("1", vals.EmptyList, "2"),
    93  		That("@a = ; put $a").Puts(vals.EmptyList),
    94  
    95  		// List element assignment
    96  		That("var li = [foo bar]; set li[0] = 233; put $@li").Puts("233", "bar"),
    97  		// Variable in list assignment must already be defined. Regression test
    98  		// for b.elv.sh/889.
    99  		That("set foobarlorem[0] = a").DoesNotCompile(),
   100  		// Map element assignment
   101  		That("var di = [&k=v]; set di[k] = lorem; set di[k2] = ipsum",
   102  			"put $di[k] $di[k2]").Puts("lorem", "ipsum"),
   103  		That("var d = [&a=[&b=v]]; put $d[a][b]; set d[a][b] = u; put $d[a][b]").
   104  			Puts("v", "u"),
   105  
   106  		// Temporary assignment.
   107  		That("var a b = alice bob; {a,@b}=(put amy ben) put $a $@b; put $a $b").
   108  			Puts("amy", "ben", "alice", "bob"),
   109  		// Temporary assignment of list element.
   110  		That("l = [a]; l[0]=x put $l[0]; put $l[0]").Puts("x", "a"),
   111  		// Temporary assignment of map element.
   112  		That("m = [&k=v]; m[k]=v2 put $m[k]; put $m[k]").Puts("v2", "v"),
   113  		// Temporary assignment before special form.
   114  		That("li=[foo bar] for x $li { put $x }").Puts("foo", "bar"),
   115  		// Multiple LHSs in temporary assignments.
   116  		That("{a b}={foo bar} put $a $b").Puts("foo", "bar"),
   117  		That("@a=(put a b) put $@a").Puts("a", "b"),
   118  		That("{a,@b}=(put a b c) put $@b").Puts("b", "c"),
   119  		// Spacey assignment with temporary assignment
   120  		That("x = 1; x=2 y = (+ 1 $x); put $x $y").Puts("1", 3),
   121  		// Using syntax of temporary assignment for non-temporary assignment no
   122  		// longer compiles
   123  		That("x=y").DoesNotCompile(),
   124  
   125  		// Concurrently creating a new variable and accessing existing variable.
   126  		// Run with "go test -race".
   127  		That("x = 1", "put $x | y = (all)").DoesNothing(),
   128  		That("nop (x = 1) | nop").DoesNothing(),
   129  
   130  		// Assignment errors when the RHS errors.
   131  		That("x = [][1]").Throws(ErrorWithType(errs.OutOfRange{}), "[][1]"),
   132  		// Assignment to read-only var is an error.
   133  		That("nil = 1").Throws(errs.SetReadOnlyVar{VarName: "nil"}, "nil"),
   134  		That("a true b = 1 2 3").Throws(errs.SetReadOnlyVar{VarName: "true"}, "true"),
   135  		That("@true = 1").Throws(errs.SetReadOnlyVar{VarName: "@true"}, "@true"),
   136  		// A readonly var as a target for the `except` clause should error.
   137  		That("try { fail reason } except nil { }").Throws(errs.SetReadOnlyVar{VarName: "nil"}, "nil"),
   138  		That("try { fail reason } except x { }").DoesNothing(),
   139  		// Evaluation of the assignability occurs at run-time so, if no exception is raised, this
   140  		// otherwise invalid use of `nil` is okay.
   141  		That("try { } except nil { }").DoesNothing(),
   142  		// Arity mismatch.
   143  		That("x = 1 2").Throws(
   144  			errs.ArityMismatch{
   145  				What:     "assignment right-hand-side",
   146  				ValidLow: 1, ValidHigh: 1, Actual: 2},
   147  			"x = 1 2"),
   148  		That("x y = 1").Throws(
   149  			errs.ArityMismatch{
   150  				What:     "assignment right-hand-side",
   151  				ValidLow: 2, ValidHigh: 2, Actual: 1},
   152  			"x y = 1"),
   153  		That("x y @z = 1").Throws(
   154  			errs.ArityMismatch{
   155  				What:     "assignment right-hand-side",
   156  				ValidLow: 2, ValidHigh: -1, Actual: 1},
   157  			"x y @z = 1"),
   158  
   159  		// Trying to add a new name in a namespace throws an exception.
   160  		// Regression test for #1214.
   161  		That("ns: = (ns [&]); ns:a = b").Throws(NoSuchVariable("ns:a"), "ns:a = b"),
   162  	)
   163  }
   164  
   165  func TestCommand_Redir(t *testing.T) {
   166  	_, cleanup := testutil.InTestDir()
   167  	defer cleanup()
   168  
   169  	Test(t,
   170  		// Output and input redirection.
   171  		That("echo 233 > out1", " slurp < out1").Puts("233\n"),
   172  		// Append.
   173  		That("echo 1 > out; echo 2 >> out; slurp < out").Puts("1\n2\n"),
   174  
   175  		// Redirections from special form.
   176  		That(`for x [lorem ipsum] { echo $x } > out2`, `slurp < out2`).
   177  			Puts("lorem\nipsum\n"),
   178  
   179  		// Using numeric FDs as source and destination.
   180  		That(`{ echo foobar >&2 } 2> out3`, `slurp < out3`).
   181  			Puts("foobar\n"),
   182  		// Using named FDs as source and destination.
   183  		That(`{ echo foobar >&stderr } stderr> out4`, `slurp < out4`).
   184  			Puts("foobar\n"),
   185  		// Using a new FD as source throws an exception.
   186  		That(`echo foo >&4`).Throws(AnyError),
   187  		// Using a new FD as destination is OK, and makes it available.
   188  		That(`{ echo foo >&4 } 4>out5`, `slurp < out5`).Puts("foo\n"),
   189  
   190  		// Redirections from File object.
   191  		That(`echo haha > out3`, `f = (fopen out3)`, `slurp <$f`, ` fclose $f`).
   192  			Puts("haha\n"),
   193  		// Redirections from Pipe object.
   194  		That(`p = (pipe); echo haha > $p; pwclose $p; slurp < $p; prclose $p`).
   195  			Puts("haha\n"),
   196  
   197  		// We can't read values from a file and shouldn't hang when iterating
   198  		// over input from a file.
   199  		// Regression test for https://src.elv.sh/issues/1010
   200  		That("echo abc > bytes", "each $echo~ < bytes").Prints("abc\n"),
   201  		That("echo def > bytes", "only-values < bytes | count").Puts(0),
   202  
   203  		// Invalid redirection destination.
   204  		That("echo []> test").Throws(
   205  			errs.BadValue{
   206  				What:  "redirection destination",
   207  				Valid: "fd name or number", Actual: "[]"},
   208  			"[]"),
   209  		// Invalid fd redirection source.
   210  		That("echo >&test").Throws(
   211  			errs.BadValue{
   212  				What:  "redirection source",
   213  				Valid: "fd name or number or '-'", Actual: "test"},
   214  			"test"),
   215  		// Invalid redirection source.
   216  		That("echo > []").Throws(
   217  			errs.BadValue{
   218  				What:  "redirection source",
   219  				Valid: "string, file or pipe", Actual: "list"},
   220  			"[]"),
   221  	)
   222  }
   223  
   224  func TestCommand_Stacktrace(t *testing.T) {
   225  	oops := ErrorWithMessage("oops")
   226  	Test(t,
   227  		// Stack traces.
   228  		That("fail oops").Throws(oops, "fail oops"),
   229  		That("fn f { fail oops }", "f").Throws(oops, "fail oops ", "f"),
   230  		That("fn f { fail oops }", "fn g { f }", "g").Throws(
   231  			oops, "fail oops ", "f ", "g"),
   232  		// Error thrown before execution.
   233  		That("fn f { }", "f a").Throws(ErrorWithType(errs.ArityMismatch{}), "f a"),
   234  		// Error from builtin.
   235  		That("count 1 2 3").Throws(
   236  			ErrorWithType(errs.ArityMismatch{}), "count 1 2 3"),
   237  	)
   238  }