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) }