star/bed/editor/editor_test.go
2025-04-26 19:33:14 +08:00

884 lines
29 KiB
Go

package editor
import (
"fmt"
"os"
"reflect"
"runtime"
"strings"
"testing"
"b612.me/apps/b612/bed/cmdline"
"b612.me/apps/b612/bed/event"
"b612.me/apps/b612/bed/key"
"b612.me/apps/b612/bed/mode"
"b612.me/apps/b612/bed/state"
"b612.me/apps/b612/bed/window"
)
type testUI struct {
eventCh chan<- event.Event
initCh chan struct{}
redrawCh chan struct{}
}
func newTestUI() *testUI {
return &testUI{
initCh: make(chan struct{}),
redrawCh: make(chan struct{}),
}
}
func (ui *testUI) Init(eventCh chan<- event.Event) error {
ui.eventCh = eventCh
go func() { defer close(ui.initCh); <-ui.redrawCh }()
return nil
}
func (*testUI) Run(map[mode.Mode]*key.Manager) {}
func (*testUI) Size() (int, int) { return 90, 20 }
func (ui *testUI) Redraw(state.State) error {
ui.redrawCh <- struct{}{}
return nil
}
func (*testUI) Close() error { return nil }
func (ui *testUI) Emit(e event.Event) {
<-ui.initCh
ui.eventCh <- e
switch e.Type {
case event.ExecuteCmdline, event.NextSearch, event.PreviousSearch:
<-ui.redrawCh
}
<-ui.redrawCh
}
func createTemp(dir, str string) (*os.File, error) {
f, err := os.CreateTemp(dir, "")
if err != nil {
return nil, err
}
if str != "" {
if _, err = f.WriteString(str); err != nil {
return nil, err
}
}
if err = f.Close(); err != nil {
return nil, err
}
if str == "" {
if err = os.Remove(f.Name()); err != nil {
return nil, err
}
}
return f, nil
}
func TestEditorOpenEmptyWriteQuit(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.OpenEmpty(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
f, err := createTemp(t.TempDir(), "")
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.Increment, Count: 13})
ui.Emit(event.Event{Type: event.Decrement, Count: 6})
ui.Emit(event.Event{Type: event.Write, Arg: f.Name()})
ui.Emit(event.Event{Type: event.Quit, Bang: true})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "1 (0x1) bytes written"; editor.err == nil ||
!strings.HasSuffix(editor.err.Error(), expected) {
t.Errorf("err should end with %q but got: %v", expected, editor.err)
}
if editor.errtyp != state.MessageInfo {
t.Errorf("errtyp should be MessageInfo but got: %v", editor.errtyp)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
bs, err := os.ReadFile(f.Name())
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "\x07"; string(bs) != expected {
t.Errorf("file contents should be %q but got %q", expected, string(bs))
}
}
func TestEditorOpenWriteQuit(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skip on Windows")
}
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
f, err := createTemp(t.TempDir(), "")
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.Open(f.Name()); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.StartInsert})
ui.Emit(event.Event{Type: event.Rune, Rune: '4'})
ui.Emit(event.Event{Type: event.Rune, Rune: '8'})
ui.Emit(event.Event{Type: event.Rune, Rune: '0'})
ui.Emit(event.Event{Type: event.Rune, Rune: '0'})
ui.Emit(event.Event{Type: event.Rune, Rune: 'f'})
ui.Emit(event.Event{Type: event.Rune, Rune: 'a'})
ui.Emit(event.Event{Type: event.ExitInsert})
ui.Emit(event.Event{Type: event.CursorLeft})
ui.Emit(event.Event{Type: event.Decrement})
ui.Emit(event.Event{Type: event.StartInsertHead})
ui.Emit(event.Event{Type: event.Rune, Rune: '1'})
ui.Emit(event.Event{Type: event.Rune, Rune: '2'})
ui.Emit(event.Event{Type: event.ExitInsert})
ui.Emit(event.Event{Type: event.CursorEnd})
ui.Emit(event.Event{Type: event.Delete})
ui.Emit(event.Event{Type: event.WriteQuit})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if err := editor.err; err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
bs, err := os.ReadFile(f.Name())
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "\x12\x48\xff"; string(bs) != expected {
t.Errorf("file contents should be %q but got %q", expected, string(bs))
}
}
func TestEditorOpenQuitBang(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.OpenEmpty(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.StartInsert})
ui.Emit(event.Event{Type: event.Rune, Rune: '4'})
ui.Emit(event.Event{Type: event.Rune, Rune: '8'})
ui.Emit(event.Event{Type: event.ExitInsert})
ui.Emit(event.Event{Type: event.Quit})
ui.Emit(event.Event{Type: event.Quit, Bang: true})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if err, expected := editor.err, "you have unsaved changes in [No Name], "+
"add ! to force :quit"; err == nil || !strings.HasSuffix(err.Error(), expected) {
t.Errorf("err should end with %q but got: %v", expected, err)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
}
func TestEditorOpenWriteQuitBang(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
f, err := createTemp(t.TempDir(), "ab")
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.Open(f.Name()); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.SwitchFocus})
ui.Emit(event.Event{Type: event.StartAppendEnd})
ui.Emit(event.Event{Type: event.Rune, Rune: 'c'})
ui.Emit(event.Event{Type: event.ExitInsert})
ui.Emit(event.Event{Type: event.WriteQuit, Arg: f.Name() + ".out", Bang: true})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
bs, err := os.ReadFile(f.Name())
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "ab"; string(bs) != expected {
t.Errorf("file contents should be %q but got %q", expected, string(bs))
}
bs, err = os.ReadFile(f.Name() + ".out")
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "abc"; string(bs) != expected {
t.Errorf("file contents should be %q but got %q", expected, string(bs))
}
}
func TestEditorReadWriteQuit(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
r := strings.NewReader("Hello, world!")
if err := editor.Read(r); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
f, err := createTemp(t.TempDir(), "")
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.WriteQuit, Arg: f.Name()})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
bs, err := os.ReadFile(f.Name())
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "Hello, world!"; string(bs) != expected {
t.Errorf("file contents should be %q but got %q", expected, string(bs))
}
}
func TestEditorWritePartial(t *testing.T) {
str := "Hello, world! こんにちは、世界!"
f, err := createTemp(t.TempDir(), str)
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
for _, testCase := range []struct {
cmdRange string
count int
expected string
}{
{"", 41, str},
{"-10,$+10", 41, str},
{"10,25", 16, str[10:26]},
{".+3+3+3+5+5 , .+0xa-0x6", 16, str[4:20]},
{"$-20,.+28", 9, str[20:29]},
} {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.Open(f.Name()); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
fout, err := createTemp(t.TempDir(), "")
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func(name string) {
ui.Emit(event.Event{Type: event.StartCmdlineCommand})
for _, c := range testCase.cmdRange + "w " + name {
ui.Emit(event.Event{Type: event.Rune, Rune: c})
}
ui.Emit(event.Event{Type: event.ExecuteCmdline})
ui.Emit(event.Event{Type: event.Quit, Bang: true})
}(fout.Name())
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := fmt.Sprintf("%[1]d (0x%[1]x) bytes written", testCase.count); editor.err == nil ||
!strings.Contains(editor.err.Error(), expected) {
t.Errorf("err should be contain %q but got: %v", expected, editor.err)
}
if editor.errtyp != state.MessageInfo {
t.Errorf("errtyp should be MessageInfo but got: %v", editor.errtyp)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
bs, err := os.ReadFile(fout.Name())
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if string(bs) != testCase.expected {
t.Errorf("file contents should be %q but got %q", testCase.expected, string(bs))
}
}
}
func TestEditorWriteVisualSelection(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
f, err := createTemp(t.TempDir(), "Hello, world!")
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.Open(f.Name()); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.CursorNext, Count: 4})
ui.Emit(event.Event{Type: event.StartVisual})
ui.Emit(event.Event{Type: event.CursorNext, Count: 5})
ui.Emit(event.Event{Type: event.StartCmdlineCommand})
ui.Emit(event.Event{Type: event.Rune, Rune: 'w'})
ui.Emit(event.Event{Type: event.Rune, Rune: ' '})
for _, ch := range f.Name() + ".out" {
ui.Emit(event.Event{Type: event.Rune, Rune: ch})
}
ui.Emit(event.Event{Type: event.ExecuteCmdline})
ui.Emit(event.Event{Type: event.Quit})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "6 (0x6) bytes written"; editor.err == nil ||
!strings.HasSuffix(editor.err.Error(), expected) {
t.Errorf("err should end with %q but got: %v", expected, editor.err)
}
if editor.errtyp != state.MessageInfo {
t.Errorf("errtyp should be MessageInfo but got: %v", editor.errtyp)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
bs, err := os.ReadFile(f.Name() + ".out")
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "o, wor"; string(bs) != expected {
t.Errorf("file contents should be %q but got %q", expected, string(bs))
}
}
func TestEditorWriteUndo(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
f, err := createTemp(t.TempDir(), "abc")
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.Open(f.Name()); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.DeleteByte})
ui.Emit(event.Event{Type: event.Write, Arg: f.Name() + ".out"})
ui.Emit(event.Event{Type: event.Undo})
ui.Emit(event.Event{Type: event.Quit})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "2 (0x2) bytes written"; editor.err == nil ||
!strings.HasSuffix(editor.err.Error(), expected) {
t.Errorf("err should end with %q but got: %v", expected, editor.err)
}
if editor.errtyp != state.MessageInfo {
t.Errorf("errtyp should be MessageInfo but got: %v", editor.errtyp)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
bs, err := os.ReadFile(f.Name())
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "abc"; string(bs) != expected {
t.Errorf("file contents should be %q but got %q", expected, string(bs))
}
bs, err = os.ReadFile(f.Name() + ".out")
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "bc"; string(bs) != expected {
t.Errorf("file contents should be %q but got %q", expected, string(bs))
}
}
func TestEditorSearch(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
f, err := createTemp(t.TempDir(), "abcdefabcdefabcdef")
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.Open(f.Name()); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.StartCmdlineSearchForward})
ui.Emit(event.Event{Type: event.Rune, Rune: 'e'})
ui.Emit(event.Event{Type: event.Rune, Rune: 'f'})
ui.Emit(event.Event{Type: event.ExecuteCmdline})
ui.Emit(event.Event{Type: event.Nop}) // wait for redraw
ui.Emit(event.Event{Type: event.DeleteByte})
ui.Emit(event.Event{Type: event.PreviousSearch})
ui.Emit(event.Event{Type: event.NextSearch})
ui.Emit(event.Event{Type: event.DeleteByte})
ui.Emit(event.Event{Type: event.StartCmdlineSearchBackward})
ui.Emit(event.Event{Type: event.Rune, Rune: 'b'})
ui.Emit(event.Event{Type: event.Rune, Rune: 'c'})
ui.Emit(event.Event{Type: event.ExecuteCmdline})
ui.Emit(event.Event{Type: event.Nop}) // wait for redraw
ui.Emit(event.Event{Type: event.DeleteByte})
ui.Emit(event.Event{Type: event.PreviousSearch})
ui.Emit(event.Event{Type: event.DeleteByte})
ui.Emit(event.Event{Type: event.Write, Arg: f.Name() + ".out"})
ui.Emit(event.Event{Type: event.Quit, Bang: true})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "14 (0xe) bytes written"; editor.err == nil ||
!strings.HasSuffix(editor.err.Error(), expected) {
t.Errorf("err should end with %q but got: %v", expected, editor.err)
}
if editor.errtyp != state.MessageInfo {
t.Errorf("errtyp should be MessageInfo but got: %v", editor.errtyp)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
bs, err := os.ReadFile(f.Name() + ".out")
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "abcdfacdfacdef"; string(bs) != expected {
t.Errorf("file contents should be %q but got %q", expected, string(bs))
}
}
func TestEditorCmdlineCursorGoto(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
f, err := createTemp(t.TempDir(), "Hello, world!")
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.Open(f.Name()); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.StartCmdlineCommand})
ui.Emit(event.Event{Type: event.Rune, Rune: '6'})
ui.Emit(event.Event{Type: event.ExecuteCmdline})
ui.Emit(event.Event{Type: event.DeleteByte})
ui.Emit(event.Event{Type: event.Write, Arg: f.Name() + ".out1"})
ui.Emit(event.Event{Type: event.Undo})
ui.Emit(event.Event{Type: event.StartCmdlineCommand})
ui.Emit(event.Event{Type: event.Rune, Rune: '7'})
ui.Emit(event.Event{Type: event.Rune, Rune: '0'})
ui.Emit(event.Event{Type: event.Rune, Rune: '%'})
ui.Emit(event.Event{Type: event.ExecuteCmdline})
ui.Emit(event.Event{Type: event.DeletePrevByte})
ui.Emit(event.Event{Type: event.Write, Arg: f.Name() + ".out2"})
ui.Emit(event.Event{Type: event.Quit, Bang: true})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
bs, err := os.ReadFile(f.Name() + ".out1")
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "Hello,world!"; string(bs) != expected {
t.Errorf("file contents should be %q but got %q", expected, string(bs))
}
bs, err = os.ReadFile(f.Name() + ".out2")
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "Hello, wrld!"; string(bs) != expected {
t.Errorf("file contents should be %q but got %q", expected, string(bs))
}
}
func TestEditorCmdlineQuit(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.OpenEmpty(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.StartCmdlineCommand})
ui.Emit(event.Event{Type: event.Rune, Rune: 'q'})
ui.Emit(event.Event{Type: event.Rune, Rune: 'u'})
ui.Emit(event.Event{Type: event.Rune, Rune: 'i'})
ui.Emit(event.Event{Type: event.Rune, Rune: 't'})
ui.Emit(event.Event{Type: event.ExecuteCmdline})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if err := editor.err; err != nil {
t.Errorf("err should be nil but got: %v", err)
}
}
func TestEditorCmdlineQuitAll(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.OpenEmpty(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.StartCmdlineCommand})
ui.Emit(event.Event{Type: event.Rune, Rune: 'q'})
ui.Emit(event.Event{Type: event.Rune, Rune: 'a'})
ui.Emit(event.Event{Type: event.Rune, Rune: 'l'})
ui.Emit(event.Event{Type: event.Rune, Rune: 'l'})
ui.Emit(event.Event{Type: event.ExecuteCmdline})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if err := editor.err; err != nil {
t.Errorf("err should be nil but got: %v", err)
}
}
func TestEditorCmdlineQuitErr(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.OpenEmpty(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.StartCmdlineCommand})
ui.Emit(event.Event{Type: event.Rune, Rune: 'c'})
ui.Emit(event.Event{Type: event.Rune, Rune: 'q'})
ui.Emit(event.Event{Type: event.Rune, Rune: ' '})
ui.Emit(event.Event{Type: event.Rune, Rune: '4'})
ui.Emit(event.Event{Type: event.Rune, Rune: '2'})
ui.Emit(event.Event{Type: event.ExecuteCmdline})
}()
if err, expected := editor.Run(), (&quitErr{42}); !reflect.DeepEqual(expected, err) {
t.Errorf("err should be %v but got: %v", expected, err)
}
if err := editor.err; err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
}
func TestEditorReplace(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
f, err := createTemp(t.TempDir(), "Hello, world!")
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.Open(f.Name()); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.CursorNext, Count: 2})
ui.Emit(event.Event{Type: event.StartReplace})
ui.Emit(event.Event{Type: event.SwitchFocus})
ui.Emit(event.Event{Type: event.Rune, Rune: 'a'})
ui.Emit(event.Event{Type: event.Rune, Rune: 'b'})
ui.Emit(event.Event{Type: event.Rune, Rune: 'c'})
ui.Emit(event.Event{Type: event.CursorNext, Count: 2})
ui.Emit(event.Event{Type: event.Rune, Rune: 'd'})
ui.Emit(event.Event{Type: event.Rune, Rune: 'e'})
ui.Emit(event.Event{Type: event.ExitInsert})
ui.Emit(event.Event{Type: event.CursorLeft, Count: 5})
ui.Emit(event.Event{Type: event.StartReplaceByte})
ui.Emit(event.Event{Type: event.SwitchFocus})
ui.Emit(event.Event{Type: event.Rune, Rune: '7'})
ui.Emit(event.Event{Type: event.Rune, Rune: '2'})
ui.Emit(event.Event{Type: event.CursorNext, Count: 2})
ui.Emit(event.Event{Type: event.StartReplace})
ui.Emit(event.Event{Type: event.Rune, Rune: '7'})
ui.Emit(event.Event{Type: event.Rune, Rune: '2'})
ui.Emit(event.Event{Type: event.Rune, Rune: '7'})
ui.Emit(event.Event{Type: event.Rune, Rune: '3'})
ui.Emit(event.Event{Type: event.Rune, Rune: '7'})
ui.Emit(event.Event{Type: event.Rune, Rune: '4'})
ui.Emit(event.Event{Type: event.Rune, Rune: '7'})
ui.Emit(event.Event{Type: event.Rune, Rune: '5'})
ui.Emit(event.Event{Type: event.Backspace})
ui.Emit(event.Event{Type: event.ExitInsert})
ui.Emit(event.Event{Type: event.CursorEnd})
ui.Emit(event.Event{Type: event.StartReplace})
ui.Emit(event.Event{Type: event.Rune, Rune: '7'})
ui.Emit(event.Event{Type: event.Rune, Rune: '6'})
ui.Emit(event.Event{Type: event.Rune, Rune: '7'})
ui.Emit(event.Event{Type: event.Rune, Rune: '7'})
ui.Emit(event.Event{Type: event.Rune, Rune: '7'})
ui.Emit(event.Event{Type: event.Rune, Rune: '8'})
ui.Emit(event.Event{Type: event.Backspace})
ui.Emit(event.Event{Type: event.ExitInsert})
ui.Emit(event.Event{Type: event.CursorHead})
ui.Emit(event.Event{Type: event.DeleteByte})
ui.Emit(event.Event{Type: event.Write, Arg: f.Name() + ".out"})
ui.Emit(event.Event{Type: event.Quit, Bang: true})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "13 (0xd) bytes written"; editor.err == nil ||
!strings.HasSuffix(editor.err.Error(), expected) {
t.Errorf("err should end with %q but got: %v", expected, editor.err)
}
if editor.errtyp != state.MessageInfo {
t.Errorf("errtyp should be MessageInfo but got: %v", editor.errtyp)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
bs, err := os.ReadFile(f.Name() + ".out")
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "earcrsterldvw"; string(bs) != expected {
t.Errorf("file contents should be %q but got %q", expected, string(bs))
}
}
func TestEditorCopyCutPaste(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
f, err := createTemp(t.TempDir(), "Hello, world!")
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.Open(f.Name()); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.CursorNext, Count: 2})
ui.Emit(event.Event{Type: event.StartVisual})
ui.Emit(event.Event{Type: event.CursorNext, Count: 5})
ui.Emit(event.Event{Type: event.Copy})
ui.Emit(event.Event{Type: event.CursorNext, Count: 3})
ui.Emit(event.Event{Type: event.Paste})
ui.Emit(event.Event{Type: event.CursorPrev, Count: 2})
ui.Emit(event.Event{Type: event.StartVisual})
ui.Emit(event.Event{Type: event.CursorPrev, Count: 5})
ui.Emit(event.Event{Type: event.Cut})
ui.Emit(event.Event{Type: event.CursorNext, Count: 5})
ui.Emit(event.Event{Type: event.PastePrev})
ui.Emit(event.Event{Type: event.Write, Arg: f.Name() + ".out"})
ui.Emit(event.Event{Type: event.Quit, Bang: true})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "19 (0x13) bytes written"; editor.err == nil ||
!strings.HasSuffix(editor.err.Error(), expected) {
t.Errorf("err should end with %q but got: %v", expected, editor.err)
}
if editor.errtyp != state.MessageInfo {
t.Errorf("errtyp should be MessageInfo but got: %v", editor.errtyp)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
bs, err := os.ReadFile(f.Name() + ".out")
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "Hell w woo,llo,rld!"; string(bs) != expected {
t.Errorf("file contents should be %q but got %q", expected, string(bs))
}
}
func TestEditorShowBinary(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
f, err := createTemp(t.TempDir(), "Hello, world!")
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.Open(f.Name()); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.ShowBinary})
ui.Emit(event.Event{Type: event.Quit})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "01001000"; editor.err == nil || editor.err.Error() != expected {
t.Errorf("err should be %q but got: %v", expected, editor.err)
}
if editor.errtyp != state.MessageInfo {
t.Errorf("errtyp should be MessageInfo but got: %v", editor.errtyp)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
}
func TestEditorShowDecimal(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
f, err := createTemp(t.TempDir(), "Hello, world!")
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.Open(f.Name()); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.ShowDecimal})
ui.Emit(event.Event{Type: event.Quit})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "72"; editor.err == nil || editor.err.Error() != expected {
t.Errorf("err should be %q but got: %v", expected, editor.err)
}
if editor.errtyp != state.MessageInfo {
t.Errorf("errtyp should be MessageInfo but got: %v", editor.errtyp)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
}
func TestEditorShift(t *testing.T) {
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
f, err := createTemp(t.TempDir(), "Hello, world!")
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.Open(f.Name()); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.ShiftLeft, Count: 1})
ui.Emit(event.Event{Type: event.CursorNext, Count: 7})
ui.Emit(event.Event{Type: event.ShiftRight, Count: 3})
ui.Emit(event.Event{Type: event.Write, Arg: f.Name() + ".out"})
ui.Emit(event.Event{Type: event.Quit, Bang: true})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "13 (0xd) bytes written"; editor.err == nil ||
!strings.HasSuffix(editor.err.Error(), expected) {
t.Errorf("err should end with %q but got: %v", expected, editor.err)
}
if editor.errtyp != state.MessageInfo {
t.Errorf("errtyp should be MessageInfo but got: %v", editor.errtyp)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
bs, err := os.ReadFile(f.Name() + ".out")
if err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if expected := "\x90ello, \x0eorld!"; string(bs) != expected {
t.Errorf("file contents should be %q but got %q", expected, string(bs))
}
}
func TestEditorChdir(t *testing.T) {
dir, err := os.Getwd()
if err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
ui := newTestUI()
editor := NewEditor(ui, window.NewManager(), cmdline.NewCmdline())
if err := editor.Init(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
if err := editor.OpenEmpty(); err != nil {
t.Fatalf("err should be nil but got: %v", err)
}
go func() {
ui.Emit(event.Event{Type: event.Pwd})
ui.Emit(event.Event{Type: event.Chdir, Arg: "../"})
ui.Emit(event.Event{Type: event.Chdir, Arg: "-"})
ui.Emit(event.Event{Type: event.Quit})
}()
if err := editor.Run(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
if err := editor.err; err == nil || err.Error() != dir {
t.Errorf("err should be %q but got: %v", dir, err)
}
if editor.errtyp != state.MessageInfo {
t.Errorf("errtyp should be MessageInfo but got: %v", editor.errtyp)
}
if err := editor.Close(); err != nil {
t.Errorf("err should be nil but got: %v", err)
}
}