2019-12-18 18:38:21 +08:00
|
|
|
package edit
|
2019-11-03 02:32:11 +08:00
|
|
|
|
|
|
|
import (
|
2019-11-10 07:08:13 +08:00
|
|
|
"fmt"
|
|
|
|
"testing"
|
|
|
|
|
2019-12-24 04:00:59 +08:00
|
|
|
"github.com/elves/elvish/pkg/cli/apptest"
|
|
|
|
"github.com/elves/elvish/pkg/cli/term"
|
|
|
|
"github.com/elves/elvish/pkg/eval"
|
|
|
|
"github.com/elves/elvish/pkg/eval/vals"
|
|
|
|
"github.com/elves/elvish/pkg/eval/vars"
|
2020-04-26 01:26:17 +08:00
|
|
|
"github.com/elves/elvish/pkg/parse"
|
2019-12-24 04:00:59 +08:00
|
|
|
"github.com/elves/elvish/pkg/store"
|
2019-11-03 02:32:11 +08:00
|
|
|
)
|
|
|
|
|
2019-12-08 05:24:00 +08:00
|
|
|
var Styles = apptest.Styles
|
2019-11-03 03:08:40 +08:00
|
|
|
|
2019-11-08 23:49:43 +08:00
|
|
|
type fixture struct {
|
|
|
|
Editor *Editor
|
2019-12-18 18:26:49 +08:00
|
|
|
TTYCtrl apptest.TTYCtrl
|
2019-11-08 23:49:43 +08:00
|
|
|
Evaler *eval.Evaler
|
2019-12-27 02:09:26 +08:00
|
|
|
Store store.Store
|
2019-11-17 04:40:19 +08:00
|
|
|
Home string
|
2019-11-08 23:49:43 +08:00
|
|
|
|
2019-12-08 05:24:00 +08:00
|
|
|
width int
|
2019-12-07 05:50:39 +08:00
|
|
|
codeCh <-chan string
|
|
|
|
errCh <-chan error
|
|
|
|
cleanup func()
|
2019-11-03 02:32:11 +08:00
|
|
|
}
|
2019-11-03 02:41:09 +08:00
|
|
|
|
2019-12-02 09:19:33 +08:00
|
|
|
func rc(codes ...string) func(*fixture) {
|
|
|
|
return func(f *fixture) { evals(f.Evaler, codes...) }
|
2019-11-08 23:49:43 +08:00
|
|
|
}
|
|
|
|
|
2019-12-02 09:19:33 +08:00
|
|
|
func assign(name string, val interface{}) func(*fixture) {
|
|
|
|
return func(f *fixture) {
|
|
|
|
f.Evaler.Global["temp"] = vars.NewReadOnly(val)
|
|
|
|
evals(f.Evaler, name+` = $temp`)
|
|
|
|
}
|
2019-11-03 08:44:16 +08:00
|
|
|
}
|
|
|
|
|
2019-12-27 02:09:26 +08:00
|
|
|
func storeOp(storeFn func(store.Store)) func(*fixture) {
|
2019-12-02 09:19:33 +08:00
|
|
|
return func(f *fixture) {
|
|
|
|
storeFn(f.Store)
|
|
|
|
// TODO(xiaq): Don't depend on this Elvish API.
|
|
|
|
evals(f.Evaler, "edit:history:fast-forward")
|
2019-11-08 23:49:43 +08:00
|
|
|
}
|
2019-12-02 09:19:33 +08:00
|
|
|
}
|
|
|
|
|
2019-12-08 05:24:00 +08:00
|
|
|
func setup(fns ...func(*fixture)) *fixture {
|
2019-12-02 09:19:33 +08:00
|
|
|
st, cleanupStore := store.MustGetTempStore()
|
2019-11-17 04:40:19 +08:00
|
|
|
home, cleanupFs := eval.InTempHome()
|
2019-12-18 18:26:49 +08:00
|
|
|
tty, ttyCtrl := apptest.NewFakeTTY()
|
2019-11-03 08:01:02 +08:00
|
|
|
ev := eval.NewEvaler()
|
|
|
|
ed := NewEditor(tty, ev, st)
|
|
|
|
ev.InstallModule("edit", ed.Ns())
|
2019-11-08 23:49:43 +08:00
|
|
|
evals(ev,
|
|
|
|
`use edit`,
|
2020-04-02 23:59:39 +08:00
|
|
|
// This is the same as the default prompt for non-root users. This makes
|
|
|
|
// sure that the tests will work when run as root.
|
|
|
|
"edit:prompt = { tilde-abbr $pwd; put '> ' }",
|
2019-11-08 23:49:43 +08:00
|
|
|
// This will simplify most tests against the terminal.
|
|
|
|
"edit:rprompt = { }")
|
2019-12-07 05:50:39 +08:00
|
|
|
f := &fixture{Editor: ed, TTYCtrl: ttyCtrl, Evaler: ev, Store: st, Home: home}
|
2019-12-08 05:24:00 +08:00
|
|
|
for _, fn := range fns {
|
2019-12-02 09:19:33 +08:00
|
|
|
fn(f)
|
2019-11-03 08:01:02 +08:00
|
|
|
}
|
2019-12-08 05:24:00 +08:00
|
|
|
_, f.width = tty.Size()
|
2019-12-07 05:50:39 +08:00
|
|
|
f.codeCh, f.errCh = apptest.StartReadCode(f.Editor.ReadCode)
|
|
|
|
f.cleanup = func() {
|
|
|
|
f.Editor.app.CommitEOF()
|
|
|
|
f.Wait()
|
|
|
|
cleanupFs()
|
|
|
|
cleanupStore()
|
|
|
|
}
|
2019-11-08 23:49:43 +08:00
|
|
|
return f
|
2019-11-03 02:41:09 +08:00
|
|
|
}
|
|
|
|
|
2019-11-08 23:49:43 +08:00
|
|
|
func (f *fixture) Wait() (string, error) {
|
|
|
|
return <-f.codeCh, <-f.errCh
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *fixture) Cleanup() {
|
2019-12-07 05:50:39 +08:00
|
|
|
f.cleanup()
|
2019-11-08 23:49:43 +08:00
|
|
|
}
|
|
|
|
|
2019-12-08 05:24:00 +08:00
|
|
|
func (f *fixture) MakeBuffer(args ...interface{}) *term.Buffer {
|
|
|
|
return term.NewBufferBuilder(f.width).MarkLines(args...).Buffer()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *fixture) TestTTY(t *testing.T, args ...interface{}) {
|
|
|
|
t.Helper()
|
|
|
|
f.TTYCtrl.TestBuffer(t, f.MakeBuffer(args...))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *fixture) TestTTYNotes(t *testing.T, args ...interface{}) {
|
|
|
|
t.Helper()
|
|
|
|
f.TTYCtrl.TestNotesBuffer(t, f.MakeBuffer(args...))
|
|
|
|
}
|
|
|
|
|
2019-12-18 18:26:49 +08:00
|
|
|
func feedInput(ttyCtrl apptest.TTYCtrl, s string) {
|
2019-11-08 23:49:43 +08:00
|
|
|
for _, r := range s {
|
|
|
|
ttyCtrl.Inject(term.K(r))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func evals(ev *eval.Evaler, codes ...string) {
|
|
|
|
for _, code := range codes {
|
2020-04-26 02:22:38 +08:00
|
|
|
src := parse.Source{Name: "[test]", Code: code}
|
2020-04-26 20:14:51 +08:00
|
|
|
op, err := ev.ParseAndCompile(src, nil)
|
2020-04-15 07:04:23 +08:00
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Errorf("parse and compile %q: %s", code, err))
|
|
|
|
}
|
2020-04-16 07:11:23 +08:00
|
|
|
err = ev.Eval(op, eval.EvalCfg{})
|
2019-11-08 23:49:43 +08:00
|
|
|
if err != nil {
|
2019-11-10 07:08:13 +08:00
|
|
|
panic(fmt.Errorf("eval %q: %s", code, err))
|
2019-11-08 23:49:43 +08:00
|
|
|
}
|
2019-11-03 08:01:02 +08:00
|
|
|
}
|
2019-11-03 02:41:09 +08:00
|
|
|
}
|
2019-11-10 07:08:13 +08:00
|
|
|
|
|
|
|
func getGlobal(ev *eval.Evaler, name string) interface{} {
|
|
|
|
return ev.Global[name].Get()
|
|
|
|
}
|
|
|
|
|
|
|
|
func testGlobals(t *testing.T, ev *eval.Evaler, wantVals map[string]interface{}) {
|
|
|
|
t.Helper()
|
|
|
|
for name, wantVal := range wantVals {
|
|
|
|
testGlobal(t, ev, name, wantVal)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func testGlobal(t *testing.T, ev *eval.Evaler, name string, wantVal interface{}) {
|
|
|
|
t.Helper()
|
|
|
|
if val := getGlobal(ev, name); !vals.Equal(val, wantVal) {
|
|
|
|
t.Errorf("$%s = %s, want %s",
|
|
|
|
name, vals.Repr(val, vals.NoPretty), vals.Repr(wantVal, vals.NoPretty))
|
|
|
|
}
|
|
|
|
}
|