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