github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/eval/eval_test.go (about)

     1  package eval_test
     2  
     3  import (
     4  	"strconv"
     5  	"sync"
     6  	"syscall"
     7  	"testing"
     8  
     9  	. "github.com/markusbkk/elvish/pkg/eval"
    10  	"github.com/markusbkk/elvish/pkg/prog/progtest"
    11  
    12  	. "github.com/markusbkk/elvish/pkg/eval/evaltest"
    13  	"github.com/markusbkk/elvish/pkg/eval/vals"
    14  	"github.com/markusbkk/elvish/pkg/eval/vars"
    15  	"github.com/markusbkk/elvish/pkg/parse"
    16  	"github.com/markusbkk/elvish/pkg/testutil"
    17  )
    18  
    19  func TestPid(t *testing.T) {
    20  	pid := strconv.Itoa(syscall.Getpid())
    21  	Test(t, That("put $pid").Puts(pid))
    22  }
    23  
    24  func TestNumBgJobs(t *testing.T) {
    25  	Test(t,
    26  		That("put $num-bg-jobs").Puts("0"),
    27  		// TODO(xiaq): Test cases where $num-bg-jobs > 0. This cannot be done
    28  		// with { put $num-bg-jobs }& because the output channel may have
    29  		// already been closed when the closure is run.
    30  	)
    31  }
    32  
    33  func TestArgs(t *testing.T) {
    34  	Test(t,
    35  		That("put $args").Puts(vals.EmptyList))
    36  	TestWithSetup(t,
    37  		func(ev *Evaler) { ev.SetArgs([]string{"foo", "bar"}) },
    38  		That("put $args").Puts(vals.MakeList("foo", "bar")))
    39  }
    40  
    41  func TestEvalTimeDeprecate(t *testing.T) {
    42  	progtest.SetDeprecationLevel(t, 42)
    43  	testutil.InTempDir(t)
    44  
    45  	TestWithSetup(t, func(ev *Evaler) {
    46  		ev.ExtendGlobal(BuildNs().AddGoFn("dep", func(fm *Frame) {
    47  			fm.Deprecate("deprecated", nil, 42)
    48  		}))
    49  	},
    50  		That("dep").PrintsStderrWith("deprecated"),
    51  		// Deprecation message is only shown once.
    52  		That("dep 2> tmp.txt; dep").DoesNothing(),
    53  	)
    54  }
    55  
    56  func TestMultipleEval(t *testing.T) {
    57  	Test(t,
    58  		That("var x = hello").Then("put $x").Puts("hello"),
    59  
    60  		// Shadowing with fn. Regression test for #1213.
    61  		That("fn f { put old }").Then("fn f { put new }").Then("f").
    62  			Puts("new"),
    63  		// Variable deletion. Regression test for #1213.
    64  		That("var x = foo").Then("del x").Then("put $x").DoesNotCompile(),
    65  	)
    66  }
    67  
    68  func TestEval_AlternativeGlobal(t *testing.T) {
    69  	ev := NewEvaler()
    70  	g := BuildNs().AddVar("a", vars.NewReadOnly("")).Ns()
    71  	err := ev.Eval(parse.Source{Code: "nop $a"}, EvalCfg{Global: g})
    72  	if err != nil {
    73  		t.Errorf("got error %v, want nil", err)
    74  	}
    75  	// Regression test for #1223
    76  	if ev.Global().HasKeyString("a") {
    77  		t.Errorf("$a from alternative global leaked into Evaler global")
    78  	}
    79  }
    80  
    81  func TestEval_Concurrent(t *testing.T) {
    82  	ev := NewEvaler()
    83  
    84  	var wg sync.WaitGroup
    85  	wg.Add(2)
    86  	go func() {
    87  		ev.Eval(parse.Source{Code: "var a"}, EvalCfg{})
    88  		wg.Done()
    89  	}()
    90  	go func() {
    91  		ev.Eval(parse.Source{Code: "var b"}, EvalCfg{})
    92  		wg.Done()
    93  	}()
    94  	wg.Wait()
    95  	g := ev.Global()
    96  	if !g.HasKeyString("a") {
    97  		t.Errorf("variable $a not created")
    98  	}
    99  	if !g.HasKeyString("b") {
   100  		t.Errorf("variable $b not created")
   101  	}
   102  }
   103  
   104  type fooOpts struct{ Opt string }
   105  
   106  func (*fooOpts) SetDefaultOptions() {}
   107  
   108  func TestCall(t *testing.T) {
   109  	ev := NewEvaler()
   110  	var gotOpt, gotArg string
   111  	fn := NewGoFn("foo", func(fm *Frame, opts fooOpts, arg string) {
   112  		gotOpt = opts.Opt
   113  		gotArg = arg
   114  	})
   115  
   116  	passedArg := "arg value"
   117  	passedOpt := "opt value"
   118  	ev.Call(fn,
   119  		CallCfg{
   120  			Args: []interface{}{passedArg},
   121  			Opts: map[string]interface{}{"opt": passedOpt},
   122  			From: "[TestCall]"},
   123  		EvalCfg{})
   124  
   125  	if gotArg != passedArg {
   126  		t.Errorf("got arg %q, want %q", gotArg, passedArg)
   127  	}
   128  	if gotOpt != passedOpt {
   129  		t.Errorf("got opt %q, want %q", gotOpt, passedOpt)
   130  	}
   131  }
   132  
   133  var checkTests = []struct {
   134  	name           string
   135  	code           string
   136  	wantParseErr   bool
   137  	wantCompileErr bool
   138  }{
   139  	{name: "no error", code: "put $nil"},
   140  	{name: "parse error only", code: "put [",
   141  		wantParseErr: true},
   142  	{name: "compile error only", code: "put $x",
   143  		wantCompileErr: true},
   144  	{name: "both parse and compile error", code: "put [$x",
   145  		wantParseErr: true, wantCompileErr: true},
   146  }
   147  
   148  func TestCheck(t *testing.T) {
   149  	ev := NewEvaler()
   150  	for _, test := range checkTests {
   151  		t.Run(test.name, func(t *testing.T) {
   152  			parseErr, compileErr := ev.Check(parse.Source{Code: test.code}, nil)
   153  			if (parseErr != nil) != test.wantParseErr {
   154  				t.Errorf("got parse error %v, when wantParseErr = %v",
   155  					parseErr, test.wantParseErr)
   156  			}
   157  			if (compileErr != nil) != test.wantCompileErr {
   158  				t.Errorf("got compile error %v, when wantCompileErr = %v",
   159  					compileErr, test.wantCompileErr)
   160  			}
   161  		})
   162  	}
   163  }