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

     1  package edit
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/markusbkk/elvish/pkg/cli"
     8  	"github.com/markusbkk/elvish/pkg/cli/clitest"
     9  	"github.com/markusbkk/elvish/pkg/cli/term"
    10  	"github.com/markusbkk/elvish/pkg/cli/tk"
    11  	"github.com/markusbkk/elvish/pkg/eval"
    12  	"github.com/markusbkk/elvish/pkg/eval/vals"
    13  	"github.com/markusbkk/elvish/pkg/eval/vars"
    14  	"github.com/markusbkk/elvish/pkg/mods/file"
    15  	"github.com/markusbkk/elvish/pkg/parse"
    16  	"github.com/markusbkk/elvish/pkg/store"
    17  	"github.com/markusbkk/elvish/pkg/store/storedefs"
    18  	"github.com/markusbkk/elvish/pkg/testutil"
    19  )
    20  
    21  var Styles = clitest.Styles
    22  
    23  type fixture struct {
    24  	Editor  *Editor
    25  	TTYCtrl clitest.TTYCtrl
    26  	Evaler  *eval.Evaler
    27  	Store   storedefs.Store
    28  	Home    string
    29  
    30  	width  int
    31  	codeCh <-chan string
    32  	errCh  <-chan error
    33  }
    34  
    35  func rc(codes ...string) func(*fixture) {
    36  	return func(f *fixture) { evals(f.Evaler, codes...) }
    37  }
    38  
    39  func assign(name string, val interface{}) func(*fixture) {
    40  	return func(f *fixture) {
    41  		f.Evaler.ExtendGlobal(eval.BuildNs().AddVar("temp", vars.NewReadOnly(val)))
    42  		evals(f.Evaler, "set "+name+" = $temp")
    43  	}
    44  }
    45  
    46  func storeOp(storeFn func(storedefs.Store)) func(*fixture) {
    47  	return func(f *fixture) {
    48  		storeFn(f.Store)
    49  		// TODO(xiaq): Don't depend on this Elvish API.
    50  		evals(f.Evaler, "edit:history:fast-forward")
    51  	}
    52  }
    53  
    54  func setup(c testutil.Cleanuper, fns ...func(*fixture)) *fixture {
    55  	st := store.MustTempStore(c)
    56  	home := testutil.InTempHome(c)
    57  	testutil.Setenv(c, "PATH", "")
    58  
    59  	tty, ttyCtrl := clitest.NewFakeTTY()
    60  	ev := eval.NewEvaler()
    61  	ev.ExtendGlobal(eval.BuildNs().AddNs("file", file.Ns))
    62  	ed := NewEditor(tty, ev, st)
    63  	ev.ExtendBuiltin(eval.BuildNs().AddNs("edit", ed))
    64  	evals(ev,
    65  		// This is the same as the default prompt for non-root users. This makes
    66  		// sure that the tests will work when run as root.
    67  		"set edit:prompt = { tilde-abbr $pwd; put '> ' }",
    68  		// This will simplify most tests against the terminal.
    69  		"set edit:rprompt = { }")
    70  	f := &fixture{Editor: ed, TTYCtrl: ttyCtrl, Evaler: ev, Store: st, Home: home}
    71  	for _, fn := range fns {
    72  		fn(f)
    73  	}
    74  	_, f.width = tty.Size()
    75  	f.codeCh, f.errCh = clitest.StartReadCode(f.Editor.ReadCode)
    76  	c.Cleanup(func() {
    77  		f.Editor.app.CommitEOF()
    78  		f.Wait()
    79  	})
    80  	return f
    81  }
    82  
    83  func (f *fixture) Wait() (string, error) {
    84  	return <-f.codeCh, <-f.errCh
    85  }
    86  
    87  func (f *fixture) MakeBuffer(args ...interface{}) *term.Buffer {
    88  	return term.NewBufferBuilder(f.width).MarkLines(args...).Buffer()
    89  }
    90  
    91  func (f *fixture) TestTTY(t *testing.T, args ...interface{}) {
    92  	t.Helper()
    93  	f.TTYCtrl.TestBuffer(t, f.MakeBuffer(args...))
    94  }
    95  
    96  func (f *fixture) TestTTYNotes(t *testing.T, args ...interface{}) {
    97  	t.Helper()
    98  	f.TTYCtrl.TestNotesBuffer(t, f.MakeBuffer(args...))
    99  }
   100  
   101  func (f *fixture) SetCodeBuffer(b tk.CodeBuffer) {
   102  	codeArea(f.Editor.app).MutateState(func(s *tk.CodeAreaState) {
   103  		s.Buffer = b
   104  	})
   105  }
   106  
   107  func feedInput(ttyCtrl clitest.TTYCtrl, s string) {
   108  	for _, r := range s {
   109  		ttyCtrl.Inject(term.K(r))
   110  	}
   111  }
   112  
   113  func evals(ev *eval.Evaler, codes ...string) {
   114  	for _, code := range codes {
   115  		err := ev.Eval(parse.Source{Name: "[test]", Code: code}, eval.EvalCfg{})
   116  		if err != nil {
   117  			panic(fmt.Errorf("eval %q: %s", code, err))
   118  		}
   119  	}
   120  }
   121  
   122  func getGlobal(ev *eval.Evaler, name string) interface{} {
   123  	v, _ := ev.Global().Index(name)
   124  	return v
   125  }
   126  
   127  func testGlobals(t *testing.T, ev *eval.Evaler, wantVals map[string]interface{}) {
   128  	t.Helper()
   129  	for name, wantVal := range wantVals {
   130  		testGlobal(t, ev, name, wantVal)
   131  	}
   132  }
   133  
   134  func testGlobal(t *testing.T, ev *eval.Evaler, name string, wantVal interface{}) {
   135  	t.Helper()
   136  	if val := getGlobal(ev, name); !vals.Equal(val, wantVal) {
   137  		t.Errorf("$%s = %s, want %s",
   138  			name, vals.ReprPlain(val), vals.ReprPlain(wantVal))
   139  	}
   140  }
   141  
   142  func testThatOutputErrorIsBubbled(t *testing.T, f *fixture, code string) {
   143  	t.Helper()
   144  	evals(f.Evaler, "var ret = (bool ?("+code+" >&-))")
   145  	// Exceptions are booleanly false
   146  	testGlobal(t, f.Evaler, "ret", false)
   147  }
   148  
   149  func codeArea(app cli.App) tk.CodeArea { return app.ActiveWidget().(tk.CodeArea) }