diff --git a/ascii_test.go b/ascii_test.go index 071fe72..592808d 100644 --- a/ascii_test.go +++ b/ascii_test.go @@ -1,12 +1,14 @@ -package term +package term_test import ( "bytes" "testing" + + "github.com/moby/term" ) func TestToBytes(t *testing.T) { - codes, err := ToBytes("ctrl-a,a") + codes, err := term.ToBytes("ctrl-a,a") if err != nil { t.Error(err) } @@ -15,12 +17,12 @@ func TestToBytes(t *testing.T) { t.Errorf("expected: %+v, got: %+v", expected, codes) } - _, err = ToBytes("shift-z") + _, err = term.ToBytes("shift-z") if err == nil { t.Error("expected and error") } - codes, err = ToBytes("ctrl-@,ctrl-[,~,ctrl-o") + codes, err = term.ToBytes("ctrl-@,ctrl-[,~,ctrl-o") if err != nil { t.Error(err) } @@ -29,7 +31,7 @@ func TestToBytes(t *testing.T) { t.Errorf("expected: %+v, got: %+v", expected, codes) } - codes, err = ToBytes("DEL,+") + codes, err = term.ToBytes("DEL,+") if err != nil { t.Error(err) } diff --git a/proxy_test.go b/proxy_test.go index 9adb91a..8793855 100644 --- a/proxy_test.go +++ b/proxy_test.go @@ -1,325 +1,169 @@ -package term +package term_test import ( "bytes" + "errors" + "io" "testing" + + "github.com/moby/term" ) func TestEscapeProxyRead(t *testing.T) { - t.Run("no escape keys, keys [a]", func(t *testing.T) { - escapeKeys, _ := ToBytes("") - keys, _ := ToBytes("a") - reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - - buf := make([]byte, len(keys)) - nr, err := reader.Read(buf) - if err != nil { - t.Error(err) - } - if expected := len(keys); nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - }) - - t.Run("no escape keys, keys [a,b,c]", func(t *testing.T) { - escapeKeys, _ := ToBytes("") - keys, _ := ToBytes("a,b,c") - reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - - buf := make([]byte, len(keys)) - nr, err := reader.Read(buf) - if err != nil { - t.Error(err) - } - if expected := len(keys); nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - }) - - t.Run("no escape keys, no keys", func(t *testing.T) { - escapeKeys, _ := ToBytes("") - keys, _ := ToBytes("") - reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - - buf := make([]byte, len(keys)) - nr, err := reader.Read(buf) - if err == nil { - t.Error("expected an error when there are no keys are to read") - } - if expected := 0; len(keys) != expected { - t.Errorf("expected: %d, got: %d", expected, len(keys)) - } - if expected := len(keys); nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if expected := len(keys); len(buf) != expected { - t.Errorf("expected: %d, got: %d", expected, len(buf)) - } - }) - - t.Run("DEL escape key, keys [a,b,c,+]", func(t *testing.T) { - escapeKeys, _ := ToBytes("DEL") - keys, _ := ToBytes("a,b,c,+") - reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - - buf := make([]byte, len(keys)) - nr, err := reader.Read(buf) - if err != nil { - t.Error(err) - } - if expected := len(keys); nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - }) - - t.Run("DEL escape key, no keys", func(t *testing.T) { - escapeKeys, _ := ToBytes("DEL") - keys, _ := ToBytes("") - reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - - buf := make([]byte, len(keys)) - nr, err := reader.Read(buf) - if err == nil { - t.Error("expected an error when there are no keys are to read") - } - if expected := 0; len(keys) != expected { - t.Errorf("expected: %d, got: %d", expected, len(keys)) - } - if expected := len(keys); nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if expected := len(keys); len(buf) != expected { - t.Errorf("expected: %d, got: %d", expected, len(buf)) - } - }) - - t.Run("ctrl-x,ctrl-@ escape key, keys [DEL]", func(t *testing.T) { - escapeKeys, _ := ToBytes("ctrl-x,ctrl-@") - keys, _ := ToBytes("DEL") - reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - - buf := make([]byte, len(keys)) - nr, err := reader.Read(buf) - if err != nil { - t.Error(err) - } - if expected := 1; nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - }) - - t.Run("ctrl-c escape key, keys [ctrl-c]", func(t *testing.T) { - escapeKeys, _ := ToBytes("ctrl-c") - keys, _ := ToBytes("ctrl-c") - reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - - buf := make([]byte, len(keys)) - nr, err := reader.Read(buf) - if expected := "read escape sequence"; err == nil || err.Error() != expected { - t.Errorf("expected: %v, got: %v", expected, err) - } - if expected := 0; nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - }) - - t.Run("ctrl-c,ctrl-z escape key, keys [ctrl-c],[ctrl-z]", func(t *testing.T) { - escapeKeys, _ := ToBytes("ctrl-c,ctrl-z") - keys, _ := ToBytes("ctrl-c,ctrl-z") - reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - - buf := make([]byte, 1) - nr, err := reader.Read(buf) - if err != nil { - t.Error(err) - } - if expected := 0; nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys[0:1]) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - - nr, err = reader.Read(buf) - if expected := "read escape sequence"; err == nil || err.Error() != expected { - t.Errorf("expected: %v, got: %v", expected, err) - } - if expected := 0; nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys[1:]) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - }) - - t.Run("ctrl-c,ctrl-z escape key, keys [ctrl-c,ctrl-z]", func(t *testing.T) { - escapeKeys, _ := ToBytes("ctrl-c,ctrl-z") - keys, _ := ToBytes("ctrl-c,ctrl-z") - reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - - buf := make([]byte, 2) - nr, err := reader.Read(buf) - if expected := "read escape sequence"; err == nil || err.Error() != expected { - t.Errorf("expected: %v, got: %v", expected, err) - } - if expected := 0; nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - }) - - t.Run("ctrl-c,ctrl-z escape key, keys [ctrl-c],[DEL,+]", func(t *testing.T) { - escapeKeys, _ := ToBytes("ctrl-c,ctrl-z") - keys, _ := ToBytes("ctrl-c,DEL,+") - reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - - buf := make([]byte, 1) - nr, err := reader.Read(buf) - if err != nil { - t.Error(err) - } - if expected := 0; nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys[0:1]) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - - buf = make([]byte, len(keys)) - nr, err = reader.Read(buf) - if err != nil { - t.Error(err) - } - if expected := len(keys); nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - }) - - t.Run("ctrl-c,ctrl-z escape key, keys [ctrl-c],[DEL]", func(t *testing.T) { - escapeKeys, _ := ToBytes("ctrl-c,ctrl-z") - keys, _ := ToBytes("ctrl-c,DEL") - reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - - buf := make([]byte, 1) - nr, err := reader.Read(buf) - if err != nil { - t.Error(err) - } - if expected := 0; nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys[0:1]) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - - buf = make([]byte, len(keys)) - nr, err = reader.Read(buf) - if err != nil { - t.Error(err) - } - if expected := len(keys); nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - }) - - t.Run("a,b,c,d escape key, keys [a,b],[c,d]", func(t *testing.T) { - escapeKeys, _ := ToBytes("a,b,c,d") - keys, _ := ToBytes("a,b,c,d") - reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - - buf := make([]byte, 2) - nr, err := reader.Read(buf) - if err != nil { - t.Error(err) - } - if expected := 0; nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys[0:2]) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - - buf = make([]byte, 2) - nr, err = reader.Read(buf) - if expected := "read escape sequence"; err == nil || err.Error() != expected { - t.Errorf("expected: %v, got: %v", expected, err) - } - if expected := 0; nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys[2:4]) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - }) - - t.Run("ctrl-p,ctrl-q escape key, keys [ctrl-p],[a],[ctrl-p,ctrl-q]", func(t *testing.T) { - escapeKeys, _ := ToBytes("ctrl-p,ctrl-q") - keys, _ := ToBytes("ctrl-p,a,ctrl-p,ctrl-q") - reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - - buf := make([]byte, 1) - nr, err := reader.Read(buf) - if err != nil { - t.Error(err) - } - if expected := 0; nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - - buf = make([]byte, 1) - nr, err = reader.Read(buf) - if err != nil { - t.Error(err) - } - if expected := 1; nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys[:1]) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - - buf = make([]byte, 2) - nr, err = reader.Read(buf) - if err != nil { - t.Error(err) - } - if expected := 1; nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - if !bytes.Equal(buf, keys[1:3]) { - t.Errorf("expected: %+v, got: %+v", keys, buf) - } - - buf = make([]byte, 2) - nr, err = reader.Read(buf) - if expected := "read escape sequence"; err == nil || err.Error() != expected { - t.Errorf("expected: %v, got: %v", expected, err) - } - if expected := 0; nr != expected { - t.Errorf("expected: %d, got: %d", expected, nr) - } - }) + type readStep struct { + size int + n int + expErr error + buf []byte + } + + tests := []struct { + name string + escapeKeys string + keys string + reads []readStep + }{ + { + name: "no escape keys, single key", + keys: "a", + reads: []readStep{ + {size: 1, n: 1, buf: []byte("a")}, + }, + }, + { + name: "no escape keys, multiple keys", + keys: "a,b,c", + reads: []readStep{ + {size: 3, n: 3, buf: []byte("abc")}, + }, + }, + { + name: "no escape keys, no input", + reads: []readStep{ + {size: 0, n: 0, expErr: io.EOF}, + }, + }, + { + name: "DEL escape key, unmatched input", + escapeKeys: "DEL", + keys: "a,b,c,+", + reads: []readStep{ + {size: 4, n: 4, buf: []byte("abc+")}, + }, + }, + { + name: "DEL escape key, no input", + escapeKeys: "DEL", + reads: []readStep{ + {size: 0, n: 0, expErr: io.EOF}, + }, + }, + { + name: "ctrl-x,ctrl-@ escape sequence, unmatched DEL", + escapeKeys: "ctrl-x,ctrl-@", + keys: "DEL", + reads: []readStep{ + {size: 1, n: 1, buf: []byte{'\x7f'}}, + }, + }, + { + name: "ctrl-c escape key, complete match", + escapeKeys: "ctrl-c", + keys: "ctrl-c", + reads: []readStep{ + {size: 1, n: 0, expErr: term.EscapeError{}, buf: []byte{'\x03'}}, + }, + }, + { + name: "ctrl-c,ctrl-z escape sequence, match across reads", + escapeKeys: "ctrl-c,ctrl-z", + keys: "ctrl-c,ctrl-z", + reads: []readStep{ + {size: 1, n: 0, buf: []byte{'\x03'}}, + {size: 1, n: 0, expErr: term.EscapeError{}, buf: []byte{'\x1a'}}, + }, + }, + { + name: "ctrl-c,ctrl-z escape sequence, match in one read", + escapeKeys: "ctrl-c,ctrl-z", + keys: "ctrl-c,ctrl-z", + reads: []readStep{ + {size: 2, n: 0, expErr: term.EscapeError{}, buf: []byte{'\x03', '\x1a'}}, + }, + }, + { + name: "ctrl-c,ctrl-z escape sequence, partial match followed by DEL and plus", + escapeKeys: "ctrl-c,ctrl-z", + keys: "ctrl-c,DEL,+", + reads: []readStep{ + {size: 1, n: 0, buf: []byte{'\x03'}}, + {size: 3, n: 3, buf: []byte{'\x03', '\x7f', '+'}}, + }, + }, + { + name: "ctrl-c,ctrl-z escape sequence, partial match followed by DEL", + escapeKeys: "ctrl-c,ctrl-z", + keys: "ctrl-c,DEL", + reads: []readStep{ + {size: 1, n: 0, buf: []byte{'\x03'}}, + {size: 2, n: 2, buf: []byte{'\x03', '\x7f'}}, + }, + }, + { + name: "four-key escape sequence, match across reads", + escapeKeys: "a,b,c,d", + keys: "a,b,c,d", + reads: []readStep{ + {size: 2, n: 0, buf: []byte("ab")}, + {size: 2, n: 0, expErr: term.EscapeError{}, buf: []byte("cd")}, + }, + }, + { + name: "ctrl-p,ctrl-q escape sequence, match after mismatch", + escapeKeys: "ctrl-p,ctrl-q", + keys: "ctrl-p,a,ctrl-p,ctrl-q", + reads: []readStep{ + {size: 1, n: 0}, + {size: 1, n: 1, buf: []byte{'\x10'}}, + {size: 2, n: 1, buf: []byte{'a', '\x10'}}, + {size: 2, n: 0, expErr: term.EscapeError{}}, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var escapeKeys []byte + var err error + if tc.escapeKeys != "" { + escapeKeys, err = term.ToBytes(tc.escapeKeys) + if err != nil { + t.Fatalf("parse escape keys: %v", err) + } + } + var keys []byte + if tc.keys != "" { + keys, err = term.ToBytes(tc.keys) + if err != nil { + t.Fatalf("parse input keys: %v", err) + } + } + + reader := term.NewEscapeProxy(bytes.NewReader(keys), escapeKeys) + for i, step := range tc.reads { + buf := make([]byte, step.size) + n, err := reader.Read(buf) + if !errors.Is(err, step.expErr) { + t.Errorf("read %d: expected error %v, got %v", i+1, step.expErr, err) + } + if n != step.n { + t.Errorf("read %d: expected %d bytes, got %d", i+1, step.n, n) + } + // Compare the entire buffer, including when Read returns n == 0. + if step.buf != nil && !bytes.Equal(buf, step.buf) { + t.Errorf("read %d: expected buffer %v, got %v", i+1, step.buf, buf) + } + } + }) + } } diff --git a/term_unix_test.go b/term_unix_test.go index 01f30e0..3b17812 100644 --- a/term_unix_test.go +++ b/term_unix_test.go @@ -15,12 +15,11 @@ func newTTYForTest(t *testing.T) *os.File { pty, tty, err := cpty.Open() if err != nil { t.Fatalf("error creating pty: %v", err) - } else { - t.Cleanup(func() { - _ = pty.Close() - _ = tty.Close() - }) } + t.Cleanup(func() { + _ = pty.Close() + _ = tty.Close() + }) return pty } @@ -30,9 +29,8 @@ func newTempFile(t *testing.T) *os.File { tmpFile, err := os.CreateTemp(t.TempDir(), "temp") if err != nil { t.Fatalf("error creating tempfile: %v", err) - } else { - t.Cleanup(func() { _ = tmpFile.Close() }) } + t.Cleanup(func() { _ = tmpFile.Close() }) return tmpFile } @@ -46,7 +44,7 @@ func TestGetWinsize(t *testing.T) { t.Fatal("winSize is nil") } - newSize := Winsize{Width: 200, Height: 200, x: winSize.x, y: winSize.y} + newSize := Winsize{Width: 200, Height: 200} err = SetWinsize(tty.Fd(), &newSize) if err != nil { t.Fatal(err) @@ -69,7 +67,7 @@ func TestSetWinsize(t *testing.T) { if winSize == nil { t.Fatal("winSize is nil") } - newSize := Winsize{Width: 200, Height: 200, x: winSize.x, y: winSize.y} + newSize := Winsize{Width: 200, Height: 200} err = SetWinsize(tty.Fd(), &newSize) if err != nil { t.Fatal(err)